The owner's frame, 2026-08-18: with agents writing most of the code, the estate can stop stacking onto one codebase and run "a factory approach": parts that each provide a specific value, standard plumbing every part needs, and customer-facing apps that are assembled from a mix of both, each with some one-off, imperfect, customer-specific additions, which is fine and is how every sibling app was built. This doc gives that frame words: eight of them, each with what we mean by it today, the estate read through them, questions that usually help decide which word fits, and the weaknesses we already know about. It is a vocabulary for naming, filing, and planning. It is not a taxonomy: the words overlap, a thing may take two of them, and at least one kind of thing has no word yet. Third pass, 2026-08-18; earlier passes and a skeptical review are logged at the end so the reasoning is auditable. Tagged as working names by the owner on 2026-08-18 after the third naming test; expected to change if a fourth naming strains it.
Audience: anyone naming a thing, filing a doc, planning a build, or deciding where a piece of work belongs.
Why words, and two commitments#
The factory works only if its parts are standard: an agent building the ninth module must not rediscover what a module is, and an agent assembling an app must not read a module's source. Agreeing on a word for a thing is the cheapest way to agree on its contract, owner, version cadence, docs, and callers, which is all this vocabulary is for. Two commitments sit under the frame:
- The seams are the product. Modules and apps are replaceable; the contracts between them and the runner they share are what compound.
- Bespoke work is allowed and labelled. Every sibling shipped customer-specific glue. It is a label on code under any word, with rules that keep it bounded (below), not a word in the table.
The words#
One row per word. What we mean is how the word is used in this tree today, written tightly so it can be argued with; it is not a rule of membership. Maturity says how much weight the word can bear: settled (used the same way everywhere, no known strain), working (used consistently here, known to overlap with a neighbour), provisional (kept for now, might fold into another word). Examples are what exists in the estate. Also called are the alternatives considered.
| Word | What we mean, today | Maturity | Examples in the estate | Also called |
|---|---|---|---|---|
| Payload | The authored implementation of one capability: code that takes one item and a context and returns an outcome, using only the runner's verbs, holding no queue, store, credential, or per-tenant state of its own. A capability may have several variants (canon/14); each variant is a payload and the manifest names the default. A judge is a payload when it runs inside the module. | working; overlaps with runner at the seam, and a connector's payload is not item-shaped | the enhancement agents in lib/pipeline/methods; speedway's partType, scrape, normalize, content processors; walmart's taxonomy/, scraping/, content/, walmart/ packages; the extractor's scrape ladder | capability code, method, step, agent |
| Runner | The standard engine that turns payloads into a callable service: it accepts jobs from identified callers for a tenant, dispatches and executes payloads with retries, heartbeat, cancel, and checkpoint, records outcomes and usage and logs, and serves the module surface on the module's behalf. It calls payloads through contracts/runner-verbs.md. Prescriptively one implementation per language, shared by every module. | working; by the fitting test it is the largest fitting, promoted for emphasis because two contracts bind it | today five, none shared, which is the thing the factory changes: versable-runner's app/; speedway's run.server.ts + runs.server.ts + queue.server.ts; walmart's orchestrator.py + jobs.py + worker.py; App V5's worker/ + lib/tasks/; the runner template to be built | engine, harness, worker framework, jig |
| Module | A deployed unit that is one capability (its variants and its judges included) mounted on the runner, with a manifest, its own version, identity audience, queue, and storage, exposing contracts/module-surface.md, callable standalone by any app and composable through requires/provides. Co-hosting a second capability in one deployment is the stated exception in canon/14, not the default. A connector is a module whose payload's truth lives in an external system it mirrors and never owns. Prescriptively a module has a manifest; no deployment has one yet, so today's are modules-in-waiting. A judge deployed standalone is a module. | working; a deployment, not a kind of code, so it sits at a different level from payload and fitting; connectors strain the job shape | modules-in-waiting: content generation (services-api), extraction (extraction-api, provisional until its source is read); to be built: image generation, part-type matching, attribute normalization; connectors to be built: Walmart Marketplace publish and reconcile (today a payload inside walmart-mvp), a future PIM sync | capability service, service, connector (for the external-system kind) |
| Fitting | A cross-cutting, standard system every runner and app needs, delivered as a recipe plus library code or one shared service, and authored here rather than bought: replacing it with an equivalent changes nothing about what a customer can do (their screens, their credits, their alerts keep working the same way). Identity verification and tenancy propagation, queue adapters, storage adapters, caching, the credits and usage ledger, observability, configuration and secrets, deployment and environments, notifications, and the UI kit on the app side. | working; the broadest word here, and the runner passes its test | the verifier port and the issuer; the queue port with Cloud Tasks, arq, and in-process adapters; the storage port; the metering ledger; the logger and tracer; the config object; deploy.sh; @versable-git/ui | plumbing, rigging, substrate, chassis, infrastructure, kit (UI) |
| App | A customer-facing application that owns users, tenants, tenant configuration, the catalog or other system of record, orchestration, review, and its own outputs, and composes modules and fittings around them. An app is nobody's dependency: no module or other app submits work to it; modules call back into an app that asked them to, and an app may expose its own data API to a customer system. | working; app-side engines (orchestrator, review queue, catalog) have no word yet and are filed here for now | the Workflow Console, walmart-mvp, speedway, App V5, Winhere if revived | shell, product, console, surface |
| Contract | A normative shape that lets things built by different people or agents agree without reading each other's source: the module surface, the manifest and field registry, the runner verbs, the usage event, callbacks, conformance. Versioned rarely; a breaking route change bumps the path prefix, and contract_version in the manifest says which contract a module speaks (canon/11). | settled | contracts/* in this tree | seam, interface, spec, protocol |
| Reference data | A published, versioned data artifact that payloads load and never query live: taxonomies, PCdb, AutoCare, a marketplace's spec sheet, accepted-value lists. Has a publisher; the version used is recorded on every outcome. | provisional; could fold into a fitting concern plus an artifact type | done as the word asks: services-api/assets/, speedway data/, walmart's spec snapshot; done as the word forbids and listed as the lapse: App V5's second read-only Mongo and walmart's TaxonomyKnowledge in Postgres, both queried live (canon/05) | stock, snapshots, feedstock, datasets |
| Canon | The written knowledge that tells an agent or a person how to build and judge the other kinds: rules with reasoning, guides, instance breakdowns, ADRs, evidence. | settled | this tree; versable-builder's design language, guidebook, patterns, breakdowns | docs, doctrine, design language |
Eight words. Payload, runner, module, fitting, and app are said of things that run; contract, reference data, and canon of things they are built with. The count is not a claim that the space is covered.
Three notes the words need beside them:
- Reference data is the only kind of data with a word of its own. The
other content buckets in
guides/02(identity data, system config, tenant config, the job snapshot, the record) are owned by an app, a runner, or a module and have no word of their own. - A declared output transform (
canon/15) has no word either; it is an artifact of the manifest, owned by the module for module-native outputs and by the app for exports. - A pure data transform with no vendor, no model, and no state is a function
inside whatever calls it, not a payload and not a module (
guides/00).
A label that crosses all of them: bespoke#
Bespoke (working name; also trim, custom, one-off) is a label, not a word in the table above. The table answers "what kind of part is this"; bespoke answers "how far may it travel". Bespoke code sits under a word (an app, a payload variant) and carries the label because it exists to meet one customer's exact need, names that customer, and would not belong in a second app. It is allowed, because that is how every sibling shipped, and the label comes with governance because otherwise it spreads:
- it lives in a named place (
bespoke/<customer>/in an app; abespoke:tag on a payload variant), never scattered across FE and BE - it appears in the owning instance doc's Lapses or a Bespoke section
- it carries an expiry or a review date; at review it is promoted (to a payload variant, a module, a fitting, or tenant configuration), declared permanent with a reason, or deleted
The estate's examples: JEGS and Zeder upload and enhancement templates
hardcoded across App V5's frontend and backend (owner,
evidence/20260817-owner-answers.md §12); walmart's global rate floor
standing in for per-seller tiers (walmart_items_rpm, guides/02,
evidence/20260818-data-model-split/walmart-mvp.md §7); customer xlsx
skeletons committed to App V5's backend repo
(evidence/20260818-data-model-split/enhancement-product.md §4).
Three delivery forms, not words in the table#
- Scaffolds and templates are how a kind of thing is delivered as code to start from: the runner template, a module scaffold, an app starter. A scaffold is a runner or a fitting or an app in template form.
- Shared services are fittings deployed once and called (the issuer, a shared vendor cache); most fittings are libraries. Same word, two forms.
- Tools are code that produces or checks an artifact of another kind and
runs nowhere at request time: the snapshot tool that publishes reference
data,
scripts/build-do-nots.pythat generates canon, a conformance runner, and a proof surface such as versable-builder's playground (canon tooling that also hosts primitives; vb-fable's warning is that a proof surface out-costs the product if it becomes one). A tool is filed under the word whose artifact it produces.
Questions that usually help decide#
Ask roughly in this order. They are not a test with a single answer; when two questions both say yes, the thing is both, and the doc filing goes with whichever the reader would look under first.
- Does it have users and tenants, and does a customer open it? App.
- Does it take an item and return an outcome? Payload. (If it would
break when the queue or store changed, it is still a payload; it is a
leaking one, fix it per
canon/01.) - Does it accept jobs, run payloads, and serve
/jobs? Runner if it is the shared engine; Module if it is a deployment of it with its own manifest, version, and identity audience. - Is it authored here, would every runner and app need it, and does swapping it for an equivalent leave what the customer can do unchanged? Fitting. Bought infrastructure (Redis, Cloud Tasks, GCS, Langfuse) is not classified; the adapter over it is the fitting.
- Is it data with a publisher and a version, loaded rather than queried? Reference data.
- Does it exist so two things built by different people agree? Contract.
- Does it tell someone how to build one of the above? Canon.
And the label: does it name a customer? Bespoke, on whichever word it sits under.
Confusions the tests catch:
- A "module" that reads the customer's org table is an app fragment.
- A "fitting" that changes what a customer sees (a review queue, an export shape) is app.
- A feature two apps need is a module or a fitting waiting to be extracted; the tell is the second copy.
- Reference data queried live from another system's database is reference
data placed wrongly (
canon/05). - A payload that imports a queue client, a store client, or a tracing
client is a runner leak (
canon/01). - Bespoke that survives two reviews is promoted or declared permanent.
The estate read through these words#
One line per real thing, to see whether the words are enough to name it. A cell may carry two words.
| Thing | Word(s) |
|---|---|
services-api deployment | module-in-waiting (content), whose runner is app/ and whose payloads are lib/pipeline/methods; a module once it serves a manifest |
extraction-api + extraction-worker | provisionally a module-in-waiting (extraction); no source read yet (instances/extraction.md), so which parts are runner, payload, and topology is unconfirmed |
| walmart-mvp | app; inside it a runner (orchestrator.py, jobs.py, worker.py), payloads (taxonomy/, scraping/, content/), and in walmart/ the payload of what would be a connector module |
| speedway | app; inside it a runner and four payloads; its extractor client is how an app calls a module |
| App V5 | app; inside it a runner (worker/, lib/tasks/), many payloads, and fittings (breakers, retry budget, heartbeat, credits) that the contract lifts out |
| the issuer | fitting, shared-service form |
| Redis on Render | bought infrastructure, unclassified; the cache and queue adapters over it are fittings |
| PCdb, AutoCare, the Walmart spec snapshot | reference data |
deploy.sh, build-do-nots.py, the snapshot tool | tools: of the deployment fitting, of canon, of reference data |
@versable-git/ui | fitting, UI side |
| the Workflow Console | app |
| JEGS and Zeder templates in App V5 | bespoke, in an app |
| this tree, versable-builder docs | canon |
Two things the reading exposed. The sibling apps each contain a runner and
payloads and fittings inside one repo, which is exactly the App V5
assumption the factory replaces (guides/00), and the words give that
extraction a vocabulary: lift the runner out as the shared one, lift
payloads into modules, lift fittings into recipes, and the app keeps what is
left. And several things needed two words or a note, which is the honest
signal that this is a vocabulary and not a partition.
Known weaknesses#
Written here so the tidiness above is not read as finality. Each is real; none is fixed yet; the trigger for fixing them is at the end.
- Three kinds of distinction in one list. Payload, runner, module are nested (a module is a payload mounted on the runner, deployed); fitting is cross-cutting; app is a deployment; contract, reference data, canon are artifacts. A cleaner shape is two axes, artifact kind (running code, deployment, data, spec, knowledge) by role (produces value, carries value, composes, assembles), with these words as cells. A flat list was kept because folders and plans are named by single words.
- The runner passes the fitting test. Authored here, every module needs it, swapping it for an equivalent changes nothing a customer can do. It has its own word because two contracts bind it and the owner named workers as primary; that is an emphasis, not a difference in kind.
- Module is a level, not a kind. "Deployment of runner plus payload with a manifest" puts module at a different level from payload and fitting, which is why the manifest became the discriminator and why today's deployments had to be called modules-in-waiting.
- Connectors strain the job shape. The contract is N items in, N outcomes out; a marketplace's reconcile and unpublish sweep are long-lived sync loops over state the app does not own. Calling a connector "a kind of module" may bend the job envelope to fit. This is likely the first place the runner needs a second primary shape (a sync loop with a cursor and a reconcile).
- App-side engines have no word. The orchestrator loop, the review queue engine, and the catalog as system of record are filed under app, and the "a feature two apps need is waiting to be extracted" test will fire on all three once Winhere, walmart, and the console share them. They are neither modules (not item to outcome) nor fittings (not every app needs an orchestrator). A word is missing here.
- Bespoke is a wish without a mechanism. A label with a folder, a review date, and a promotion path binds only if something enforces it; nothing does yet, and the label will be skipped exactly when it matters.
- The wire field was renamed to protect a word.
payloadbecameinputon the wire so the word means one thing. Cheap now; still the vocabulary bending the contract rather than the reverse. - Reference data is borderline. It is the only data word beside two document words; it earns its place because every sibling placed it differently, and it folds into a fitting concern plus an artifact type without loss if fewer words are wanted.
When to restructure: use these words to name the next three real things (the runner template, the content module upgrade, the console plan). If the third one does not fit without a note, move to the two-axis shape and add the app-engine word then. Do not polish the list before that.
Names#
The owner asked for a name for the plumbing word and for the choice to be tagged. Working names; expiry is the same trigger as above. Candidates and reasoning:
| Candidate | Why it fits | Why it might not |
|---|---|---|
| Fittings | plain word, extends the plumbing metaphor, pairs with "kit" (@versable-git/ui) as the two things every app is fitted with; standard, replaceable, nobody's product | slightly quaint |
| Rigging | the bolted-and-riveted metaphor; holds the load without being the cargo | reads as ropes |
| Substrate | precise: what everything sits on | abstract, no verb |
| Chassis | vehicle metaphor; the app is the body | close to "shell", which was dropped for implying empty |
| Plumbing | the owner's word, zero explanation | pejorative in a customer deck |
Recommendation: Fittings. Full vocabulary: payloads, runner, modules (and connectors), fittings, apps, contracts, reference data, canon, plus the bespoke label. All plain words. Collisions inside the estate, and which reading wins where:
| Word | Also means | Ruling |
|---|---|---|
| payload | the per-item input on the wire ({item_id, payload} in versable-runner; payload.json in its bucket) | the class wins; the wire field is input in the contract as of 2026-08-18, and versable-runner's payload.json is a lapse to rename on upgrade |
| module | a speedway stage file (app/lib/modules/*.server.ts); App V5 and the kit use "module" for a UI or product module | the class wins in this tree; speedway's files are payloads under it |
| runner | versable-runner, the service name; pipeline_runner.py, a CLI | the class wins; the service is a module-in-waiting whose runner is app/ |
| app | the app/ directory in versable-runner (the runner) and in speedway (the whole app) | folder names are not classes; the class wins in prose |
| kit | @versable-git/ui | kept as the UI fitting's proper name |
Tagged 2026-08-18 by the owner: working names, "just tagging", no
restructure. The eight words above and the bespoke label stand as the
working vocabulary, with the restructure trigger unchanged (naming test
three, the console plan, passed with the one friction noted in
plans/v6-decision-set.md D10). Two more working names joined at the same
ruling, at the estate level rather than the class level:
| Word | Means | Owner's words |
|---|---|---|
| forge | the product app: App V6 (versable-forge-v6) and its successors, the thing customers use | "forge for the final customer main app with big balls" |
| foundry | versable-builder (the kit, the templates, the toolkit) plus this contract tree: what the forge is built from | "foundry for the builder / contract stuff" |
versable-builder currently describes itself as "a monorepo forge" (its
ADR-001 context); under this tag it is the foundry, and that relabel is the
versable-builder lane's to carry. Still to tag in adr/006 and the
versable-builder glossary when those files are next touched.
What the words change in this tree#
canon/01is the payload/runner seam; the other canon docs each bind one or two words (02fittings and apps,03and04runner,05to09fittings,10module and app,11module,12and13fittings,14module,15module and app);contracts/is the seams;patterns/is how to build each kind;guides/are the decisions that cut across kinds;instances/breaks down every shipped app and module;v6/is the canon for the first app.- Repo and folder names carry the word:
modules/content,runner/py,fittings/identity,apps/console; reference data is a published artifact in object storage, so its folder, if any, holds the publisher tool, not the snapshot. - A plan for a new thing starts by saying which word it takes, and says so if it takes two or none; a "none" is a finding for this doc.
- Every app gets a
bespoke/<customer>/folder and a Bespoke section in its instance doc.
Third pass: the skeptical review, 2026-08-18#
A fresh reviewer read the second draft against the whole tree
(evidence/20260818-class-model-review.md, 29 findings). All were folded
above except where noted; the dispositions are in that file. The ones that
changed the model: the fitting clause now discriminates on what a customer
can do, not what they see, and requires "authored here" so bought
infrastructure exits the test; the ordered test's second question now
returns a word; tools became a third delivery form; the manifest is stated
as prescriptive and today's deployments as modules-in-waiting; the module is
one capability by default with co-hosting as the canon/14 exception,
reconciled with the charter; the wire field payload was renamed input so
the word means one thing; the "no collisions" claim was replaced by a
collision table.
Changes from the first draft, and why#
| First draft | Now | Reason |
|---|---|---|
| workers and queues listed as a fitting | Runner is a class; the queue adapter stays a fitting | the runner is the standard machine bound by two contracts, not swappable plumbing; owner: "workers seems way more primary than just fittings" |
| Jigs as a class | scaffolds are a delivery form | a template is a runner or fitting in code form, not a kind of system |
| Shells | Apps | "shell" says empty; the console owns the orchestrator and the system of record |
| Trim as a class | bespoke as a label across classes | it is a property of code that any class can carry; making it a class hid that |
| Connectors as a peer class | a kind of module | same manifest, jobs, runner verbs; what differs is a mirroring pattern, which is a guide |
| Stock | Reference data, kept, foldable | the only non-code runtime class; every sibling placed it differently, which argues for a name; plain word over the cute one |
| no payload class | Payload added | it is what a module builder writes, and the seam between it and the runner is the tree's foundation |
| definitions mixed with examples | separate columns | owner: the "in one line" column "devolved soon into just listing out what all can come under this" |