THE OBJECT MAP — Rocoto's production domain
Status: companion to THE-ARCHITECTURE.md. This is a normative map of objects, containment, identity, and doors. It is a target domain model, not a claim that each record, event stream, or projection is deployed.
OBJ-00 · Laws of the map
- Every durable object has a stable opaque ID, tenant scope where applicable, revision, lifecycle state, actor/time provenance, and an audit trail.
- An object is owned by one aggregate/stream. A projection may join it, but no screen invents a second owner or mutable copy.
- A relation names both endpoints and its own provenance. A relationship does not transfer commercial facts from one endpoint to the other.
- A source is evidence. External providers/users keep incoming originals; Rocoto retains authorized references and minimal derived business facts separately from normalized fields. Raw email/file content is transient only (THE-SOURCE-PRIVACY-SPEC).
- Contextual object doors open the same stable ID if the actor may read it. An order card is one destination and opens only its order; customer/product/truck doors are inside the sheet, not nested in the card. The separate hover Archive action sits beside, not inside, that destination. A missing permission produces a safe unavailable state, never an ID leak.
- Product, Customer, and Albarán are the only primary sheet grammars. Reference concepts, Internal category/groups, Related products links, sources, tariffs, lots, jobs and contacts are related objects or focused editors; they do not pretend to be Product Sheets.
OBJ-00.1 · Capability and interpretation records
ProductCapabilityProfile owns tenant/profile ID, capability key, state (disabled/design-reviewed/kit-interactive/production-authorized), effective revision, authority and release evidence. It does not own the underlying commercial or inventory data. A service may be design-reviewed or browser-interactive while remaining unauthorized; projections and command authorization must both honour the profile. Release-profile truth wins over a detailed future UI contract.
CatalogueIntakeSession is a session-scoped intake envelope for one Drop a file or
Paste catalogue text choice. It owns opaque session/tenant/actor/origin IDs, source kind,
visible queue state, item order, transient raw source material and a minimal source checksum or text
fingerprint. It is not a primary sheet and it does not durably retain raw bytes/text, create an SKU,
write an Internal category/group, create a RelatedItemLink, approve artwork or invoke a model/provider.
CatalogueIntakeReviewQueue belongs to one CatalogueIntakeSession. It owns display order,
per-item state and the current recovery door only. It is session-scoped: it is not a durable worker
queue, a parser/model job, a source archive or a record-admission command.
CatalogueIntakeReviewItem belongs to one CatalogueIntakeSession. It owns a raw row/text
locator, original wording, displayed readback (Product name, SKU, Unit), duplicate/required-field
state and explicit item decision. Internal category, artwork and source history remain later
Product-Sheet facts rather than per-item intake chores. Only an
explicit confirmed decision may create an exact SKU and persist reviewed literal facts/provenance.
Ordinary review never proposes or creates a RelatedItemLink. Its visible states and recovery doors
are defined in Catalogue Contract §2.1; their named seam events are UI/audit boundaries only.
OrderLineInterpretation owns the source-line ID/revision, current disposition (exact-SKU, field-check, off-SKU-provisional, multiple-SKU-choice), candidate SKU IDs, exact field scope, literal wording, operator decision, actor/time and supersession. It never owns an SKU or silently changes the source. Multiple-choice candidates are unordered unless evidence supports an explicit rank; the UI cannot invent a top candidate.
ReplyAttention owns order/thread/source scope, specific reason, lifecycle (open, dismissed, resolved, reopened), dismissal text, actor/time and evidence revision. It is orthogonal to OrderLineInterpretation: a correct SKU can still need a reply, and an ambiguous SKU does not automatically imply a customer message. In the order-review profile its only communication door is the native provider.
ProvisionalOrderLine owns accepted literal product wording, quantity/unit/note and one-off decision evidence. It can appear on an unvalued albarán but has no ProductSKU foreign key. Accepting it does not create catalogue, artwork, price, stock, tariff or ERP master data.
UncodedSaleItem is the optional deliberate catalogue projection of one or more accepted
ProvisionalOrderLine records. It owns tenant ID, stable uncoded-item ID, literal display wording,
literal Product Unit, internal category reference, illustration association/status, direct relation
endpoints, request-history references, notes/change history and actor/time provenance. It is never
created by merely reading an off-SKU phrase; its admission cites an explicit accepted source line.
It is not a SKU, and cannot become a tariff/stock/lot/ERP/customer-alias master merely by being
recurrent. A separately authorized Assign SKU operation may link it to a new or existing coded
SKU only after review; it preserves every historical request and relation as evidence.
RequestHistoryEntry is a read projection from one exact coded SKU or UncodedSaleItem to an
Albarán line/source revision. It owns the return context token (origin Products filters, sheet and
scroll anchor) only for navigation; it never copies or mutates the line. Opening it goes to the
exact Albarán/line; Back restores that token if it remains valid.
ConversationWorkspaceSnapshot owns actor/session, thread, source route, scroll anchor and one of two postures: collapsed or expanded. It owns no customer-send draft in the order-review profile. AssistantConversation is a different internal object and may own a recoverable internal draft; its tool proposals still require their separate domain commands.
OBJ-01 · The containment graph
flowchart TD
P[Platform reference catalogue] --> RC[Reference concept]
T[Tenant] --> TC[Tenant catalogue]
T --> CU[Customer]
T --> AB[Albarán]
T --> ST[Stock movement / lot]
T --> PE[Daily stock estimate]
T --> TA[Tariff]
T --> CO[Connection]
T --> JO[Output / integration job]
TC --> ICG[Internal category/group]
TC --> SKU[SKU]
TC --> U[Accepted uncoded sale item]
RC -. classifies .-> SKU
ICG -. groups .-> SKU
SKU -. direct endpoint .-> REL[Related product link]
U -. direct endpoint .-> REL
REL -. direct endpoint .-> SKU
REL -. direct endpoint .-> U
SRC[External source reference] --> TC
SRC -. evidence for .-> SKU
SRC -. native source for .-> AB
CU --> CT[Contact / address / customer alias]
CU --> CA[Action / observation]
AB --> L[Albarán line]
L --> SKU
L --> LOT[Lot allocation]
SKU --> TP[Tariff price history]
SKU --> ART[Artwork association]
U --> ART
SKU --> RH[Request history]
U --> RH
SKU --> ST
SKU --> SRCL[Related source link]
PE -. planning for .-> SKU
AB --> CON[Concern]
AB --> OUT[Document snapshot / delivery / output job]
The arrows from reference concept and Internal category/group to SKU are classification arrows,
not ownership. A RelatedItemLink has two direct catalogue-record endpoints; each endpoint is a
coded SKU or an accepted UncodedSaleItem. It is reciprocal for navigation only and is neither a
group nor a base record. The arrow from a source artifact to an SKU
is evidence/review linkage. Neither a reference concept, category/group, relation nor source can
make an SKU sellable without the tenant catalogue's reviewed command.
OBJ-02 · Primary objects and their owners
| Object | Scope and identity | Authoritative owner | Can open a primary sheet? | Core lifecycle |
|---|---|---|---|---|
| Reference concept | platform-owned immutable ID/version | reference-catalogue stream | No | active, superseded, withdrawn; historical versions remain addressable |
| Tenant | tenant ID | tenant stream | Administration context, not a generic sheet | provisioning, active, restricted, closing, closed |
| Tenant catalogue | one logical catalogue per tenant/version | catalogue stream | Catalogue navigation, not a Product Sheet | active, importing, repair, archived tenant |
| Internal category/group | tenant classification ID | catalogue stream | No; it is a browse/filter context | suggested, reviewed, not organised, superseded; may be empty |
| Related product link | relation ID with two exact coded-SKU or accepted-uncoded-item endpoints | catalogue stream | No; it opens either exact record | active, removed, historical; reciprocal and non-transitive |
| Uncoded sale item | tenant uncoded-item ID, never recycled | catalogue/request stream | Yes: Product Sheet | accepted from off-SKU line, active, archived, assigned-to-SKU-by-explicit-command; literal request evidence remains |
| SKU | tenant SKU ID, never recycled | SKU/catalogue stream | Yes: Product Sheet | proposed, active, archived, restored; commercial history remains |
| Customer option definition / preparation note | tenant option ID, scoped to a customer, account policy, or brochure | customer/request stream | No; opens contextual customer/request editor | draft, active, paused, retired; recurrence never makes it an SKU |
| Option line snapshot | opaque albarán line-option snapshot ID, tied to one base-SKU line revision | albarán stream | No standalone sheet; opens from its base line | quoted, accepted, superseded/corrected, historical; preserves audience tags and commercial terms |
| Customer | tenant customer ID | customer stream | Yes: Customer Sheet | active, merged-by-explicit-link, archived/anonymized subject to retention |
| Albarán | tenant albarán ID plus document revisions | albarán stream | Yes: Albarán Sheet | draft, ready, allocated/prepared, issued, dispatched, delivered, corrected/voided, archived |
| Source artifact | tenant source ID and provider/user reference | source-ingestion stream | No; opens native-source context | referenced, transiently fetched, parsed, blocked, unavailable/revoked under policy |
| Inventory position | tenant position ID for exact SKU/location/canonical unit | inventory stream | No; opens focused Stock ledger/editor | evidence-backed, revisioned projection |
| Named lot | opaque tenant lot ID, one exact SKU/location | inventory stream | No; opens focused inspector from Stock or Albarán trace | active, depleted, expired, blocked, recalled, historical; never rewritten |
| Lot commitment | opaque tenant commitment ID, one exact line/position target | inventory stream | No; opens line fulfilment/lot inspector | reserved, allocated, released, fulfilled, superseded; never physical balance |
| Daily stock estimate | tenant planning-rule ID/revision | planning stream | No; opens its focused SKU planning editor | active, superseded, paused, retired; never becomes a physical movement |
| Tariff and price revision | tenant tariff ID / price revision ID | commercial stream | No; editors are part of Product/Admin context | active/retired tariff; dated immutable price history |
| Output or integration job | tenant job ID | job/output stream | No; opens job status/detail | queued, running, succeeded, failed, cancelled, expired according to policy |
| Connection | tenant connection ID | integration-settings stream | Administration connection editor | proposed, authorizing, connected, degraded, revoked, removed |
A reference concept does not acquire a tenant ID. A tenant SKU does not become a platform object.
An Internal category/group is classification context and a RelatedItemLink is a direct relation
between two existing catalogue records, each a coded SKU or accepted UncodedSaleItem; neither is
a hidden base SKU. A customer option/preparation note is not a
hidden SKU: it cannot own stock, a tariff, an artwork association, a standalone albarán
product line, or an external master record. It may be snapshotted as an approved overlay on a base
SKU line, with preparation/pack/weight and a surcharge or final unit price that belong to that line
revision only. A source artifact and a job have IDs because they need audit and recovery, but they
do not compete with the three primary sheets.
OBJ-03 · Product ownership: exact sellable-record facts
The Product Sheet names one exact sellable record: a coded SKU or an UncodedSaleItem. The following facts belong to that exact record and cannot
be read as Internal category/group, Related products, or similarly named product facts:
| Related object | Owner and required relation | Invariant |
|---|---|---|
| Original identity | SKU stream → immutable OriginalIdentity record(s) |
imported/source name, code, row/attachment reference, and received time survive every display-name edit |
| Current identity | SKU stream | display name, optional sales code, sole Product Unit, reviewed attributes, and classification are revisioned projections; the SKU ID never changes |
| Alias | SKU stream → Alias with scope |
tenant aliases may support tenant-wide search; a customer alias carries a customer ID and is never used outside that customer resolution context |
| Reference classification | SKU ↔ reference-concept relation | classification is evidence/versioned policy, never a substitute SKU ID or stock membership |
| Internal category/group | SKU ↔ reviewable classification relation | category/group can group items without relating them and cannot change commercial facts |
| Related products | exact record ↔ direct reciprocal RelatedItemLink |
add/remove can change only navigation; it cannot change price, stock, source identity, artwork or order history; each endpoint may be coded or accepted uncoded |
| Requests history | exact record ↔ frozen Albarán line/source revision | opens the exact historical line; route Back restores the Products origin token and never turns a request into a new master fact |
| Tariff price | SKU ↔ tariff → append-only price revision | every tariff has this SKU's own exact money/unit/effective period history; no implicit inherited price |
| Stock and lot | SKU ↔ stock movement/lot | movements, balances, reservations, counts, and allocations are exact-SKU facts; unknown unit equivalence stays separate rather than summed falsely |
| Artwork | SKU ↔ reviewed artwork association | an asset may be byte-deduplicated, but approval is SKU-scoped; a category/group or Related products image is only a candidate |
| Related source/email | SKU ↔ source-link with evidence/review state | link explains why a source is shown. It never turns email into product stock or auto-creates a SKU |
| Albarán line/history | exact line → SKU ID + frozen snapshot | line joins use the recorded SKU ID; later rename/reclassification does not rewrite old documents |
| Insight | derived exact-SKU projection | buyers, revenue, trends and recommendations cite source lines and exclude other SKU data; category/group or Related products context never supplies an exact-SKU metric |
A SKU can be active with sparse information. Still to record, unknown, pending review, and
no price set are valid states. Zero, a generated image, a guessed equivalence, or a category/group
or Related products metric is never used to make a sparse SKU look complete.
OBJ-04 · Source, imports, and aliases
A SourceArtifact holds the unaltered received material and delivery metadata permitted by its
retention policy. It can be an uploaded file, imported row, supplier email and attachment,
customer message, manual note, scanner result, or provider webhook. Parsing creates a
SourceObservation that references the artifact span/row/page. Review can create a proposal;
acceptance writes a domain command. The parsed value never overwrites the artifact. For E186
file/pasted-list intake, the raw file or text belongs only to CatalogueIntakeSession; a durable
source association has only the allowed checksum/reference or text fingerprint, locator and reviewed
result/decision.
| Relation | What it records | What it cannot do alone |
|---|---|---|
| source → tenant catalogue | import batch, parser version, row status, matching evidence | add/merge/archive a SKU without a reviewed command |
| source → SKU | external code/name, evidence span, link state, reviewer/actor | change SKU price, stock, unit, artwork, Internal category/group or Related products silently |
| source → Albarán | native message/thread/attachment, source span, channel capability | claim that a message was sent, read, or replied to without a provider receipt |
| source → supplier | declared sender/connection association and confidence | make supplier identity authoritative where verification failed |
| alias → SKU | phrase, normalized index key, scope, provenance, validity, reviewer | select a more specific SKU than evidence supports or cross a customer boundary |
Supplier email is a source channel. It may suggest catalogue membership, receipt, availability, or
lot facts. It becomes a stock movement only through StockReceived or another inventory command
with exact SKU/unit/location/lot data. A Product Sheet's Source evidence section reads only its exact-SKU source-link projection,
uses the empty state No related source evidence, opens native-source context and returns to the
same product context. It is not a Stock navigation seat; the separately gated Stock update owns a
supplier/source feed.
OBJ-05 · Customer ownership
The Customer Sheet owns the tenant's relationship with a business customer. Contacts, delivery addresses, account facts, customer-scoped aliases, actions, observations, and preferences are children of that relationship. Orders/albaranes are separate objects linked by customer ID.
| Child or relation | Owner | Rule |
|---|---|---|
| Contact | customer stream | name, role, channel, consent/preference, verification status, and revision are customer-scoped; a contact can be archived without deleting prior document attribution |
| Address / delivery preference | customer stream | delivery details are versioned and retain validity/effective period; a route snapshot on an albarán is frozen separately |
| Customer alias / menu entry | customer ↔ SKU relation | preserves the customer's language and evidence; it cannot rename the SKU or make it available to other customers |
| Action | customer stream | an assigned actionable record with due state, completion, actor, and links; it is not an invented insight |
| Frequent product | derived projection | computed from exact historical lines with a stated time window; one result opens its SKU and evidence |
| Upcoming/history | albarán projection keyed by customer | upcoming uses lifecycle state; history uses issued/delivered/corrected records and preserves snapshots |
| Observation/insight | evidence-backed derived record | source IDs, rule/model version, and validity are visible in the related evidence/details view; it never edits customer facts by itself |
A customer merge is an explicit, authorized, audited relationship that keeps both prior IDs and reassigns only permitted future references. The merge screen must disclose historical effect. It never silently combines two contacts because names, phones, or email text look similar.
OBJ-06 · Albarán ownership
An Albarán is the user-facing continuous work record. It starts with a request/source and gains review, fulfilment, document, and delivery facts over time. It is not deleted merely because it has been printed or delivered.
| Child or relation | Owner | Rule |
|---|---|---|
| Native source conversation | source-link projection | original words/media/attachments and channel capability stay visible above or beside the work they caused; forwarding-only is read-only |
| Line | albarán stream | stable line ID, original phrase, exact resolved SKU or unresolved state, quantity, unit, equivalence revision, rate, total, tax treatment, class, packing note, and provenance |
| Lot commitment / allocation | inventory relation from line | exact lot ID or explicit unassigned position plus quantity; it is scoped to exact line and changes no physical balance. A missing lot number is honest unassigned stock, never generated code. |
| Concern | concern stream linked to precise target | target is source block, line, field, or explicit line group; concern state cannot be changed by an unrelated edit or navigation |
| Fulfilment | albarán/inventory stream relation | allocation, preparation, dispatch, delivery, and exception facts are events with actor/time and evidence |
| Document snapshot | document stream | issue creates immutable revision with commercial snapshot; reprint points to that revision |
| Delivery / output job | job stream relation | queueing, submission, receipt, failure, retry, and cancellation are separate facts; delivery result is not inferred from a button click |
A line with unresolved product language remains an honest line/proposal and can be fulfilled only under the tenant's approved exception policy. It does not acquire an Internal category/group or Related products link as a fake SKU. A corrected commercial document creates a linked successor, correction, or void record according to the configured legal workflow; old documents remain auditable.
OBJ-07 · Inventory, money, and output ownership
| Object | Owner | Rule that protects history |
|---|---|---|
| Unit definition/equivalence | commercial/catalogue stream | equivalence has its own ID/revision, source, precision, and effective scope. Unknown equivalence remains unknown. |
| Tariff | tenant commercial stream | tariff ID is stable through retirement; retirement ends future applicability, not historical price lookup. |
| Tariff price revision | exact SKU + tariff | amount is exact decimal/currency/unit/effective interval; correcting a price appends a revision. |
| Stock movement | inventory stream | physical receipt/count/variance/depletion/adjustment/reversal carries exact SKU, location, quantity, unit/equivalence revision and source. Recorded balance is the evidence-only projection; rule/estimate events never feed it. |
| Inventory position | inventory stream | one exact tenant/SKU/location/canonical-unit position. Recorded balance is sum of named lot positions and explicit unassigned position; no forecast/inbound/commitment feeds it. |
| Named lot | inventory stream | opaque immutable lot ID plus human-entered display lot number, exact SKU/location, origin and source links. Same display code never auto-merges; never a generic category/group or Related-products lot. |
| Unassigned stock position | inventory stream | explicit physical quantity with no trustworthy lot number. It is visible/auditable, never a synthetic lot. |
| Physical lot posting | inventory stream | signed receipt/count/depletion/correction/zero-net-attribution posting against one named lot or unassigned position. Historical facts reverse/compensate; they do not rewrite. |
| Reservation / allocation / release | inventory-commercial relation | exact line/position commitment. It may feed Available to promise but never Recorded balance. |
| Lot-position reassignment | inventory stream | revision-checked equal-and-opposite named/unassigned attribution postings plus source/reason. It supports Product Assign stock to lots and must leave Recorded balance unchanged. |
| Daily stock estimate | planning stream | recurring user assumption with owner/time/timezone/revision. It changes no physical balance, lot, availability, receipt or delivery promise. |
| Expected delivery | inbound/source relation | supplier-confirmed quantity/unit/due/source relation. It stays separate until a physical receipt exists. |
| Availability | derived projection | records calculation time, input watermarks, scope, reservations/allocations and unknowns. It must not claim physical availability from missing data or an estimate. |
| Print/export/email/ERP job | job stream | immutable selection/snapshot/profile/version/content hash, idempotency key, attempts and receipts. A job result never revises an albarán silently. |
LOT-01 · Lot position, provenance and commitment law
A named lot is not a code string and an allocation is not a physical movement. The stock model has three independently auditable layers:
Recorded balance
→ named lot physical positions + Unassigned stock position
→ changed only by receipt, count, physical depletion or physical correction
Assign stock to lots
→ revision-checked reattribution among named/unassigned positions
→ equal-and-opposite postings; Recorded balance unchanged
Albarán line allocation
→ commitment against an existing eligible position
→ Available to promise/fulfilment changes; Recorded balance unchanged
A new named lot has opaque ID, typed display lot number, origin, source/evidence, actor/time and exact SKU/location. It can be created by receipt, count or Product Assign stock to lots with different provenance. A new typed name during Product assignment must not be called receipt or supplier confirmation. When a person cannot identify a lot, amount remains in explicit unassigned position. A count’s after quantities across named lots/unassigned add to exact counted total. A Product lot split’s after positions add to its fixed Recorded balance. A commitment cannot make a physical position negative or be silently cancelled by count/reattribution.
THE-STOCK-LOTS-SPEC.md is the detailed authority for LOT-01 fields, controls, API/event names, collision handling, traceability, recall, states and release proof.
OBJ-08 · The three sheet grammars
Every sheet has an identity header, contextual action area, current work, history/evidence, insights where evidence exists, and a stable browser/mobile return path. The section order is fixed enough for habit but each sheet carries different work.
| Sheet | Header and first work | Required related sections | Doors and prohibited substitutions |
|---|---|---|---|
| Product Sheet | exact sellable-record identity, active/archive state, kind-eligible image truth, Internal category, Related products and Unit | exact-record albaranes/history/insights/details always; coded-SKU stock/lots, source evidence and tariff history only under their gates | Related-product rows open either endpoint’s Product Sheet. Internal category/group opens feedback only. Do not show another record’s price/stock/art/history as the current record’s own. |
| Customer Sheet | customer identity, contacts, delivery/account facts, next actions | frequent products, upcoming work, history, observations, insights, customer aliases/menu | product and albarán doors carry their own IDs. Do not make a contact or an inferred buying pattern a second customer. |
| Albarán Sheet | status, customer, delivery context, editable exact lines and current concerns | quantity/unit/rate/total/class/lot/note controls, native source, fulfilment, document snapshots, output/delivery status, audit/history | line product opens exact SKU; customer opens Customer Sheet; a concern opens its exact target. Do not replace native source with paraphrase or represent a queue as delivery. |
State matrices, save/error behaviour, keyboard rules, and responsive requirements are in
THE-SYSTEM-SPEC.md under PRD-01, CUS-01, ALB-01, and OBJ-01.
OBJ-09 · Doors, projections, and visibility
A door is an intent to open an object, not a raw client-side pointer. The query service resolves it against the active tenant membership and object lifecycle, then returns one of: object projection, archived projection, unavailable/permission-safe response, deleted/redacted explanation, or loading/retry state. It never returns cross-tenant search suggestions, identifiers, counts, or source text.
Projection rows contain objectId, objectType, revision, asOf, and relation evidence where
needed. A list may be stale only within its declared watermark; a mutating command acknowledgement
returns the resulting aggregate revision and projection cursor. The client then reconciles its
optimistic state or shows the explicit conflict/failure state. Presence is advisory and names an
actor/action only where tenant policy permits; it is never a lock.
OBJ-10 · Archive, deletion, and redaction
Archive removes an object from default active work without erasing history. An archived SKU remains reachable from old albarán lines, price history, stock movements, source evidence, audit, and explicit archived browsing. It cannot receive new stock, price, artwork, or direct order offerings until restored. An archived customer remains visible on historical documents under retention rules. An issued Albarán is never physically deleted by a regular user.
True deletion is limited to eligible drafts, temporary uploads, unadopted leads, and configured personal data after authorization/retention review. It records a deletion/redaction event, reason, actor, policy basis, and affected projections. Legal/commercial records retain the minimum required audit shell while protected personal content is redacted or cryptographically erased where policy permits. Deletion is not a hidden projection filter and does not allow identifiers to be reused.
OBJ-10 · Unit ownership
TenantUnitDefinition owns literal vocabulary, kind and precision. Product.unit is the sole current Product Unit. A scoped UnitConversion owns an explicit exact multiplier: the reusable global object is the default and an exact-product exception shadows only its matching Unit pair. Nominal/catch-weight text remains non-calculating measurement evidence. UnitUseSnapshot freezes the Unit and any applied conversion revision on each commercial or stock act. Price, lots, stock, order lines and issued documents keep their own snapshot facts rather than becoming parallel Product-unit roles. None belongs to an Internal category/group or Related products link; see THE-UNITS-SPEC.