Skip to content

KN-86 Purchasable-Catalog Taxonomy

catalog_classes knob), ../../../tools/economy_sim.py and ../../../tools/economy-sim.lsp (own every number), ../cartridges/authoring/verb-partition.md (what a Tier-2 verb is), ADR-0042 (the thirteen first-party programs).

This doc defines the taxonomy of the purchasable catalog: the item classes, the stable-ID scheme, the exposure-stage schema, and where catalog data lives. Decisions are recorded at the lowest fidelity that holds; every number is a balancing variable owned by the simulators.

In-flight boundaries. The item census (which items exist, per item) is GWP-713. The shop surface and its owner is GWP-709. The ownership ledger’s storage layout is GWP-716; this doc says “the ledger” and leaves the layout there. The teaching-mission generator is GWP-714; this doc says “the teaching debrief” and leaves the generation logic there.


Six classes. Every purchasable item belongs to exactly one. Acquisition channels are economy-model.md’s three: bought, discovered, granted.

ClassWhat it isChannelsPrice bandExposure band
verbA Tier-2 CORE verb-tool (verb-partition.md). The atom the shop sells. Declared composites are built by CONS over owned cores and are never catalog items; :discoverable composites arrive through discovery only.bought; granted (each cart’s starter set)low, wide (the volume class)full career
equipmentCart-domain gear that is neither a verb nor a program: the fiction’s hardware (jammer coils, salvage rigs, credential blanks). Carries a standard band plus a late exclusive band.bought; granted (starter pieces)low-mid standard band; high exclusive bandfull career; exclusive band late
programA locked first-party program from the ADR-0042 roster of thirteen. A subset is active from boot (granted); the remainder ship locked and are bought.bought; grantedmidearly-mid
program tierThe tier-2 or tier-3 upgrade of a first-party program. Own-before-upgrade: buying a tier requires owning the program (or holding it granted).boughtmid (tier 2), high (tier 3)mid (tier 2), late (tier 3)
rigA permanent deck stat upgrade.boughthighmid
consumableAn expendable bought repeatedly; its catalog entry never exhausts. The recurring drain economy-model.md asks for lives here.boughtlowearly, then always

Two standing rules:

  1. The discovered channel is priceless. A :discoverable composite never gets a catalog price. Selling it kills the discovery joy (economy-model.md). Discovered items therefore sit outside this catalog entirely; they still get stable IDs (§2) because the ledger records them.
  2. Bands are knobs. A class’s price band and exposure band are rows in the simulators’ catalog_classes knob (§5). Items spread linearly across a row’s bands until the census (GWP-713) assigns per-item values.

Every catalog item receives two identifiers when the census enters it into the registry:

  • A symbolic name. A path whose first segment is the class and whose last is the item name, with a provider segment (the cart’s short code) in the middle where a cart contributes the item: verb/iceb/crack, equipment/dc/salvage-winch, program/ripsaw, tier2/ripsaw, rig/mem, consumable/panic-flare. The authoring and display handle.
  • A numeric ID. An unsigned integer, dense from 0, allocated append-only in registry order. The persistence key.

The rules:

  1. Append-only allocation. A new item takes the next unallocated integer, assigned at the moment the item first lands in the registry. The registry file (§4) is the single allocation point; an item’s ID is its position of first appearance.
  2. Never reuse. A retired item keeps its ID forever, flagged retired in the registry. The integer is never reassigned. Ownership bits recorded against a retired ID stay meaningful in old saves.
  3. The numeric ID persists; the symbolic name never does. The ledger (GWP-716) and the teaching generator (GWP-714) key on the numeric ID. Renaming an item touches only the symbolic column; saves are unaffected.
  4. Sizing is the ledger’s problem. This scheme guarantees density and append-only growth so the ledger can store ownership as a bitfield indexed by ID. How many bits it reserves and where they live in the UDS is GWP-716’s decision.

Per-item lifecycle:

hidden -> exposed -> owned (verb, equipment, program, program tier, rig)
hidden -> exposed (consumable; stays exposed, bought repeatedly)
  • hidden. Absent from the shop. No price shown, no mention made.
  • exposed. Listed with a price. An item becomes exposed on the first of:
    1. the teaching debrief: the mission that advertised the gap resolves, and the debrief surfaces the missing item with its price (economy-model.md’s acquisition loop, step 3; generator is GWP-714);
    2. the reputation floor: the operator’s reputation crosses the item’s exposure value. The floor guarantees no item hides forever when the generator never picks it (the pacing analog of the roguelike First Law: the lesson is available even if the teacher never shows up).
  • owned. Entered by purchase (bought channel) or by boot grant (granted channel). Never regresses; there is no resale or refund path in the model.

Discovered composites bypass this schema: they are never hidden-with-a-price, they simply exist once found, and the ledger records the find.

The exposure value is a per-item number on the reputation scale. Until the census assigns per-item values, the per-class exposure band (the stage_lo/stage_hi columns in §5) places items linearly. Both simulators model exposure with the reputation proxy alone; the teaching debrief accelerates individual items in play without changing the aggregate pacing the sims tune.

Three consumers need one source: the runtime (shop, ledger, teaching generator), the Python career simulator, and the KEC Lisp career simulator. Options considered:

  • (a) Compiled C constants. Free for the runtime; both simulators would have to parse C headers or restate the data. Two standing mirrors. Rejected.
  • (b) A runtime baseline .lsp module. Loaded natively by the runtime and by economy-sim.lsp; Python parses s-expressions. Workable, but the module would mix executable code with the data, and the census wants a flat record table.
  • (c) A data-only .lsp registry file. Quoted records, zero code. The runtime tree-walks it natively (the Fe userland already loads .lsp files), economy-sim.lsp reads it natively, and economy_sim.py needs a restricted s-expression reader for a data-only subset (tens of lines, no evaluation). One source, three readers, no build step.

Decision: (c). The registry is runtime/system-image/lib/deck/catalog.lsp, data-only, one record per item:

(id symbolic-name class provider price expose flags)

The census (GWP-713) creates and populates it. Until it lands, the class/count/band rows (catalog_classes, §5) stay hand-mirrored across the two simulators, exactly as every other knob already is. Once the registry lands, both simulators drop their rows and read the registry, and catalog_classes dissolves into the registry’s per-item price and expose columns.

The knob is a list of rows. Row shape:

(class, count, (price_lo, price_hi), stage_lo, stage_hi)

A class may span multiple rows (multiple bands; equipment’s late exclusive band, the two program tiers). Items spread linearly across a row’s price and stage intervals. The current rows live in tools/economy_sim.py (KNOBS["catalog_classes"]) and tools/economy-sim.lsp (the catalog-add calls); the two are kept identical by hand.

The consumable class has no row: the row model is exhaust-on-buy, and consumables repeat. Its knobs are consumable_price and consumable_rate in the same files.

  1. The T2 reading. economy-model.md’s T2 says “first program upgrade at ~4 hours.” In both simulators the first program-class purchase lands near hour 35-40, while upgrading in general (the first verb purchases) starts inside the first session. If T2 means the former, program exposure must move much earlier; if the latter, both sims already hit it. Settle against play.
  2. Provider short codes. iceb, bl, dc and friends appear across the design docs with no canonical registry. The census should fix the code set when it assigns symbolic names.
  3. Tier exposure for granted programs. Whether a granted program’s tier-2 upgrade exposes earlier than the program class band (the operator already holds the base program). Tuning-level.