[data] full codex catalog + data validator

Transcribe the §2-§6 codex into machine-readable canon and add the validator
that keeps five parallel lanes from drowning in dangling ids.

- validator (28 tests): recipes point back at their machine, every item/tech/
  commission reference resolves, ids unique + kebab-case, colors hex, footprints
  >=1x1, plus a reachability pass proving MELT is craftable from raw ore.
- items 9->36, machines 7->21, recipes 4->35, tech 0->17, commissions 1->10.
- mosh rewired to canon (gop-crate -> melt + reclaimed anchor slab); all seed
  recipe ids preserved so nothing dangles mid-round.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
type-two 2026-07-17 16:04:27 +10:00
parent 1a769e6cbd
commit f015a66283
7 changed files with 595 additions and 9 deletions

View File

@ -1,4 +1,41 @@
[
{ "id": "first-taste", "flavor": "One (1) unit of MELT. We hear you make it fresh. No questions will be answered.",
"wants": { "melt": 1 }, "rewardBandwidth": 50 }
{ "id": "first-taste",
"flavor": "One (1) unit of MELT. We hear you make it fresh. No questions will be answered.",
"wants": { "melt": 1 }, "rewardBandwidth": 50 },
{ "id": "dust-allowance",
"flavor": "12 STATIC CANISTERS. Brine grade, not display grade. Our previous supplier shipped display grade and we no longer speak of them.",
"wants": { "static-canister": 12 }, "rewardBandwidth": 120 },
{ "id": "patio-resurfacing",
"flavor": "48 MACROBLOCK BRICKS for a patio. Client has been advised the patio will not be flat. Client insists the patio has never been flat. Proceed.",
"wants": { "macroblock-bricks": 48 }, "rewardBandwidth": 220 },
{ "id": "haunted-doorway",
"flavor": "9 RINGING HALOS, hung not stacked. They are for a doorway. The doorway is for nothing. Do not ask what comes through it; it is already through.",
"wants": { "ringing-halos": 9, "gamut-paint-rec709": 2 }, "rewardBandwidth": 300 },
{ "id": "house-gray-audit",
"flavor": "30 LUMA BARS and 4 GAMUT PAINT: REC709. Deliver to Compliance. This is a legitimate order from a legitimate buyer and we are legally required to state that we are not The Correction.",
"wants": { "luma-bars": 30, "gamut-paint-rec709": 4 }, "rewardBandwidth": 260 },
{ "id": "wrong-on-purpose",
"flavor": "16 JUDDER CAMS installed in 4 BLOOM CONCENTRATE canisters. We want the movement to arrive slightly after the thing that is moving. Yes. That. Exactly that.",
"wants": { "judder-cams": 16, "bloom-concentrate": 4 }, "rewardBandwidth": 480 },
{ "id": "vaporwave-sunset",
"flavor": "1 MELTED ANCHOR SLAB + 3 MOSQUITO SWARMS (live) + 40 bbl CHROMA SLURRY (4:2:0) = 1 VAPORWAVE SUNSET. Deliver before the beat drops.",
"wants": { "melt": 1, "anchor-slab": 1, "mosquito-swarm": 3, "chroma-420": 40 }, "rewardBandwidth": 700 },
{ "id": "gibbs-wraith-willingly",
"flavor": "One (1) GIBBS WRAITH, willingly.",
"wants": { "gibbs-wraith": 1 }, "rewardBandwidth": 900 },
{ "id": "sky-blue-mislabeled",
"flavor": "600 MACROBLOCK BRICKS, sky-blue, MISLABELED REC2020. Purpose: none of your business.",
"wants": { "macroblock-bricks": 600, "gamut-paint-rec2020": 6 }, "rewardBandwidth": 2400 },
{ "id": "quarterly-sins",
"flavor": "Quarterly order: 200 MELT, 40 EXPLOSIVE HITS, 12 MOIRE CRYSTAL. Ship the HITS last and ship them running. Our loading dock is a formality and our insurer knows this.",
"wants": { "melt": 200, "explosive-hits": 40, "moire-crystal": 12 }, "rewardBandwidth": 3200 }
]

View File

@ -1,11 +1,43 @@
[
{ "id": "mdat-ore", "name": "MDAT ORE", "codex": "2:mdat-ore", "tier": 0, "color": "#1a1a22" },
{ "id": "bayer-grit", "name": "BAYER GRIT", "codex": "2:bayer-grit", "tier": 0, "color": "#3fbf5f" },
{ "id": "field-splinters", "name": "FIELD SPLINTERS", "codex": "2:field-splinters", "tier": 0, "color": "#6ef58a" },
{ "id": "silver-frames", "name": "SILVER FRAMES", "codex": "2:silver-frames", "tier": 0, "color": "#b0803a" },
{ "id": "luma-bars", "name": "LUMA BARS", "codex": "2:luma-bars", "tier": 1, "color": "#c0c0c0" },
{ "id": "chroma-slurry", "name": "CHROMA SLURRY", "codex": "2:chroma-slurry", "tier": 1, "color": "#ff3fd4" },
{ "id": "chroma-422", "name": "CHROMA SLURRY (4:2:2)", "codex": "2:chroma-slurry", "tier": 1, "color": "#e03fc0" },
{ "id": "chroma-420", "name": "CHROMA SLURRY (4:2:0)", "codex": "2:chroma-slurry", "tier": 1, "color": "#b83fa8" },
{ "id": "color-plate", "name": "COLOR PLATE", "codex": "4:demosaicer", "tier": 1, "color": "#d8b0ff" },
{ "id": "coefficient-pack", "name": "COEFFICIENT PACK", "codex": "2:coefficient-packs", "tier": 1, "color": "#3fffe0" },
{ "id": "hf-dust", "name": "HF DUST", "codex": "2:hf-dust", "tier": 1, "color": "#e8e0ff" },
{ "id": "mv-flux", "name": "MOTION VECTOR FLUID", "codex": "2:motion-vector-fluid", "tier": 1, "color": "#3fd8ff" },
{ "id": "v-hold-roll", "name": "V-HOLD ROLL", "codex": "1:the-broadcast-shelf", "tier": 1, "color": "#8fdc7a" },
{ "id": "anchor-slab", "name": "I-FRAME (ANCHOR SLAB)", "codex": "2:i-frame", "tier": 1, "color": "#fff2a0" },
{ "id": "delta-wafer", "name": "P-FRAME (DELTA WAFER)", "codex": "2:p-frame", "tier": 1, "color": "#7fb0ff" },
{ "id": "janus-wafer", "name": "B-FRAME (JANUS WAFER)", "codex": "2:b-frame", "tier": 1, "color": "#b08fff" },
{ "id": "gop-crate", "name": "GOP CRATE", "codex": "2:gop-crate", "tier": 1, "color": "#a07840" },
{ "id": "melt", "name": "MELT", "codex": "3:melt", "tier": 2, "color": "#ff7a3f" }
{ "id": "melt", "name": "MELT", "codex": "3:melt", "tier": 2, "color": "#ff7a3f" },
{ "id": "bloom-concentrate", "name": "BLOOM CONCENTRATE", "codex": "3:bloom-concentrate", "tier": 2, "color": "#ffb0e0" },
{ "id": "macroblock-bricks", "name": "MACROBLOCK BRICKS", "codex": "3:macroblock-bricks", "tier": 2, "color": "#6f8fa0" },
{ "id": "ringing-halos", "name": "RINGING HALOS", "codex": "3:ringing-halos", "tier": 2, "color": "#cfe8ff" },
{ "id": "static-canister", "name": "STATIC CANISTER", "codex": "3:static-canisters", "tier": 2, "color": "#d8d8d8" },
{ "id": "moire-crystal", "name": "MOIRE CRYSTAL", "codex": "3:moire-crystal", "tier": 2, "color": "#7fffd4" },
{ "id": "false-color-speckle", "name": "FALSE-COLOR SPECKLE", "codex": "3:false-color-speckle", "tier": 2, "color": "#d940ff" },
{ "id": "judder-cams", "name": "JUDDER CAMS", "codex": "3:judder-cams", "tier": 2, "color": "#9aa0a8" },
{ "id": "explosive-hits", "name": "EXPLOSIVE HITS", "codex": "3:explosive-hits", "tier": 2, "color": "#ff6a00" },
{ "id": "gamut-paint-rec709", "name": "GAMUT PAINT: REC709", "codex": "3:gamut-paints", "tier": 2, "color": "#8a8f96" },
{ "id": "gamut-paint-dci-p3", "name": "GAMUT PAINT: DCI-P3", "codex": "3:gamut-paints", "tier": 2, "color": "#ff2f6a" },
{ "id": "gamut-paint-rec2020", "name": "GAMUT PAINT: REC2020", "codex": "3:gamut-paints", "tier": 2, "color": "#00ff5a" },
{ "id": "analog-pack", "name": "ANALOG PACK", "codex": "2:science-packs", "tier": 2, "color": "#57d96b" },
{ "id": "spatial-pack", "name": "SPATIAL PACK", "codex": "2:science-packs", "tier": 2, "color": "#4fa8ff" },
{ "id": "temporal-pack", "name": "TEMPORAL PACK", "codex": "2:science-packs", "tier": 2, "color": "#ffb43f" },
{ "id": "container-pack", "name": "CONTAINER PACK", "codex": "2:science-packs", "tier": 2, "color": "#c07fff" },
{ "id": "cinema-pack", "name": "CINEMA PACK", "codex": "2:science-packs", "tier": 3, "color": "#fdfdff" },
{ "id": "mosquito-swarm", "name": "MOSQUITO SWARM (LIVE)", "codex": "5:mosquito-swarms", "tier": 2, "color": "#d0ffb0" },
{ "id": "gibbs-wraith", "name": "GIBBS WRAITH", "codex": "5:gibbs-wraiths", "tier": 2, "color": "#bfe0ff" }
]

View File

@ -1,16 +1,60 @@
[
{ "id": "seam-extractor", "name": "SEAM EXTRACTOR", "codex": "4:seam-extractor", "kind": "extractor",
"footprint": { "x": 2, "y": 2 }, "recipes": ["extract-mdat"], "powerDraw": 2, "asset": "seam-extractor" },
"footprint": { "x": 2, "y": 2 },
"recipes": ["extract-mdat", "extract-bayer-grit", "extract-field-splinters", "extract-silver-frames"],
"powerDraw": 2, "asset": "seam-extractor" },
{ "id": "belt", "name": "STREAM BELT", "codex": "4:belts", "kind": "belt",
"footprint": { "x": 1, "y": 1 }, "recipes": [], "powerDraw": 0, "beltSpeed": 2, "asset": "belt" },
{ "id": "lane-splitter", "name": "LANE SPLITTER", "codex": "4:demuxer", "kind": "splitter",
"footprint": { "x": 1, "y": 1 }, "recipes": [], "powerDraw": 1, "asset": "lane-splitter" },
{ "id": "buffer-tank", "name": "BUFFER TANK", "codex": "2:buffer-charge", "kind": "buffer",
"footprint": { "x": 2, "y": 2 }, "recipes": [], "powerDraw": 0, "asset": "buffer-tank" },
{ "id": "demuxer", "name": "DEMUXER", "codex": "4:demuxer", "kind": "crafter",
"footprint": { "x": 2, "y": 2 }, "recipes": ["demux-ore"], "powerDraw": 3, "asset": "demuxer" },
{ "id": "demosaicer", "name": "DEMOSAICER", "codex": "4:demosaicer", "kind": "crafter",
"footprint": { "x": 3, "y": 2 }, "recipes": ["demosaic-rggb", "demosaic-vng4", "demosaic-stripes"],
"powerDraw": 5, "asset": "demosaicer" },
{ "id": "dct-press", "name": "DCT PRESS", "codex": "4:dct-press", "kind": "crafter",
"footprint": { "x": 3, "y": 3 }, "recipes": ["press-color-plate", "press-anchor-slab"],
"powerDraw": 8, "asset": "dct-press" },
{ "id": "quantizer", "name": "QUANTIZER", "codex": "4:quantizer", "kind": "crafter",
"footprint": { "x": 2, "y": 2 }, "recipes": ["quantize"], "powerDraw": 4, "asset": "quantizer" },
"footprint": { "x": 2, "y": 2 }, "recipes": ["quantize", "quantize-crime", "skim-ringing"],
"powerDraw": 4, "asset": "quantizer" },
{ "id": "subsampler", "name": "SUBSAMPLER", "codex": "4:subsampler", "kind": "crafter",
"footprint": { "x": 2, "y": 3 }, "recipes": ["subsample-422", "subsample-420", "pan-speckle"],
"powerDraw": 4, "asset": "subsampler" },
{ "id": "mv-extractor", "name": "MV EXTRACTOR", "codex": "4:mv-extractor-and-p-caster", "kind": "crafter",
"footprint": { "x": 2, "y": 2 }, "recipes": ["extract-motion"], "powerDraw": 5, "asset": "mv-extractor" },
{ "id": "p-caster", "name": "P-CASTER", "codex": "4:mv-extractor-and-p-caster", "kind": "crafter",
"footprint": { "x": 2, "y": 2 }, "recipes": ["cast-delta-wafer", "cast-janus-wafer"],
"powerDraw": 4, "asset": "p-caster" },
{ "id": "gop-assembler", "name": "GOP ASSEMBLER", "codex": "4:gop-assembler", "kind": "crafter",
"footprint": { "x": 3, "y": 2 }, "recipes": ["assemble-gop", "assemble-gop-ibbp", "assemble-gop-i-only"],
"powerDraw": 6, "asset": "gop-assembler" },
{ "id": "mosh-reactor", "name": "MOSH REACTOR", "codex": "4:mosh-reactor", "kind": "crafter",
"footprint": { "x": 3, "y": 3 }, "recipes": ["mosh"], "powerDraw": 8, "asset": "mosh-reactor" },
{ "id": "bloom-duplicator", "name": "P-FRAME DUPLICATOR", "codex": "4:bloom-loop", "kind": "crafter",
"footprint": { "x": 1, "y": 1 }, "recipes": ["bloom-grade-3"], "powerDraw": 6, "asset": "bloom-duplicator" },
{ "id": "field-loom", "name": "FIELD LOOM", "codex": "4:field-loom", "kind": "crafter",
"footprint": { "x": 3, "y": 2 }, "recipes": ["weave-fields", "loom-vhold-roll"],
"powerDraw": 5, "asset": "field-loom" },
{ "id": "telecine-puller", "name": "TELECINE PULLER", "codex": "4:telecine-puller", "kind": "crafter",
"footprint": { "x": 2, "y": 2 }, "recipes": ["mill-judder"], "powerDraw": 4, "asset": "telecine-puller" },
{ "id": "hex-splicer", "name": "HEX SPLICER", "codex": "4:hex-splicer", "kind": "crafter",
"footprint": { "x": 3, "y": 3 }, "recipes": ["splice-offsets"], "powerDraw": 12, "asset": "hex-splicer" },
{ "id": "artifact-bottler", "name": "ARTIFACT BOTTLER", "codex": "2:science-packs", "kind": "crafter",
"footprint": { "x": 2, "y": 2 },
"recipes": ["can-static", "mix-rec709", "mix-dci-p3", "mix-rec2020",
"bottle-analog-pack", "bottle-spatial-pack", "bottle-temporal-pack"],
"powerDraw": 4, "asset": "artifact-bottler" },
{ "id": "decode-asic", "name": "DECODE ASIC", "codex": "4:decode-asic", "kind": "power",
"footprint": { "x": 2, "y": 2 }, "recipes": [], "powerDraw": 0, "powerGen": 20, "asset": "decode-asic" },
{ "id": "software-decoder", "name": "SOFTWARE DECODER", "codex": "4:software-decoder", "kind": "power",
"footprint": { "x": 2, "y": 2 }, "recipes": [], "powerDraw": 0, "powerGen": 45, "asset": "software-decoder" },
{ "id": "shipper", "name": "SCREEN UPLINK", "codex": "7:the-muxer", "kind": "shipper",
"footprint": { "x": 2, "y": 2 }, "recipes": [], "powerDraw": 5, "asset": "shipper" }
]

View File

@ -1,6 +1,87 @@
[
{ "id": "extract-mdat", "machine": "seam-extractor", "inputs": {}, "outputs": { "mdat-ore": 1 }, "ticks": 30, "bandwidth": 2 },
{ "id": "demux-ore", "machine": "demuxer", "inputs": { "mdat-ore": 2 }, "outputs": { "luma-bars": 1, "chroma-slurry": 1 }, "ticks": 45, "bandwidth": 3 },
{ "id": "quantize", "machine": "quantizer", "inputs": { "luma-bars": 1 }, "outputs": { "coefficient-pack": 1, "hf-dust": 1 }, "ticks": 30, "bandwidth": -6 },
{ "id": "mosh", "machine": "mosh-reactor", "inputs": { "coefficient-pack": 2, "chroma-slurry": 1 }, "outputs": { "melt": 1, "anchor-slab": 1 }, "ticks": 90, "bandwidth": 10 }
{ "id": "extract-bayer-grit", "machine": "seam-extractor", "inputs": {}, "outputs": { "bayer-grit": 1 }, "ticks": 45, "bandwidth": 3 },
{ "id": "extract-field-splinters", "machine": "seam-extractor", "inputs": {}, "outputs": { "field-splinters": 1 }, "ticks": 40, "bandwidth": 3 },
{ "id": "extract-silver-frames", "machine": "seam-extractor", "inputs": {}, "outputs": { "silver-frames": 1 }, "ticks": 60, "bandwidth": 4 },
{ "id": "demux-ore", "machine": "demuxer", "inputs": { "mdat-ore": 3 },
"outputs": { "luma-bars": 2, "chroma-slurry": 1, "mv-flux": 1 }, "ticks": 45, "bandwidth": 3 },
{ "id": "demosaic-rggb", "machine": "demosaicer", "inputs": { "bayer-grit": 4 },
"outputs": { "color-plate": 1 }, "ticks": 60, "bandwidth": 4 },
{ "id": "demosaic-vng4", "machine": "demosaicer", "inputs": { "bayer-grit": 6 },
"outputs": { "color-plate": 1, "false-color-speckle": 2 }, "ticks": 75, "bandwidth": 5 },
{ "id": "demosaic-stripes", "machine": "demosaicer", "inputs": { "bayer-grit": 4, "field-splinters": 1 },
"outputs": { "moire-crystal": 1 }, "ticks": 90, "bandwidth": 6 },
{ "id": "press-color-plate", "machine": "dct-press", "inputs": { "color-plate": 1 },
"outputs": { "coefficient-pack": 3 }, "ticks": 45, "bandwidth": 4 },
{ "id": "press-anchor-slab", "machine": "dct-press", "inputs": { "coefficient-pack": 6, "chroma-420": 1 },
"outputs": { "anchor-slab": 1 }, "ticks": 120, "bandwidth": 12 },
{ "id": "quantize", "machine": "quantizer", "inputs": { "luma-bars": 1 },
"outputs": { "coefficient-pack": 1, "hf-dust": 1 }, "ticks": 30, "bandwidth": -6 },
{ "id": "quantize-crime", "machine": "quantizer", "inputs": { "coefficient-pack": 1 },
"outputs": { "macroblock-bricks": 2, "hf-dust": 2 }, "ticks": 20, "bandwidth": -18 },
{ "id": "skim-ringing", "machine": "quantizer", "inputs": { "coefficient-pack": 3 },
"outputs": { "ringing-halos": 1, "hf-dust": 1 }, "ticks": 40, "bandwidth": -4 },
{ "id": "subsample-422", "machine": "subsampler", "inputs": { "chroma-slurry": 2 },
"outputs": { "chroma-422": 1 }, "ticks": 24, "bandwidth": -4 },
{ "id": "subsample-420", "machine": "subsampler", "inputs": { "chroma-422": 2 },
"outputs": { "chroma-420": 1 }, "ticks": 24, "bandwidth": -8 },
{ "id": "pan-speckle", "machine": "subsampler", "inputs": { "hf-dust": 40 },
"outputs": { "false-color-speckle": 1 }, "ticks": 200, "bandwidth": 8 },
{ "id": "extract-motion", "machine": "mv-extractor", "inputs": { "gop-crate": 1 },
"outputs": { "mv-flux": 6, "hf-dust": 1 }, "ticks": 60, "bandwidth": 5 },
{ "id": "cast-delta-wafer", "machine": "p-caster", "inputs": { "mv-flux": 2 },
"outputs": { "delta-wafer": 1 }, "ticks": 15, "bandwidth": 2 },
{ "id": "cast-janus-wafer", "machine": "p-caster", "inputs": { "mv-flux": 3, "delta-wafer": 1 },
"outputs": { "janus-wafer": 1 }, "ticks": 30, "bandwidth": 4 },
{ "id": "assemble-gop", "machine": "gop-assembler", "inputs": { "anchor-slab": 1, "delta-wafer": 6 },
"outputs": { "gop-crate": 1 }, "ticks": 60, "bandwidth": 6 },
{ "id": "assemble-gop-ibbp", "machine": "gop-assembler",
"inputs": { "anchor-slab": 1, "delta-wafer": 3, "janus-wafer": 2 },
"outputs": { "gop-crate": 2 }, "ticks": 75, "bandwidth": 8 },
{ "id": "assemble-gop-i-only", "machine": "gop-assembler", "inputs": { "anchor-slab": 8 },
"outputs": { "gop-crate": 1 }, "ticks": 45, "bandwidth": 24 },
{ "id": "mosh", "machine": "mosh-reactor", "inputs": { "gop-crate": 1 },
"outputs": { "melt": 3, "anchor-slab": 1 }, "ticks": 90, "bandwidth": 10 },
{ "id": "bloom-grade-3", "machine": "bloom-duplicator", "inputs": { "delta-wafer": 4, "mv-flux": 2 },
"outputs": { "bloom-concentrate": 1 }, "ticks": 60, "bandwidth": 8 },
{ "id": "weave-fields", "machine": "field-loom", "inputs": { "field-splinters": 2 },
"outputs": { "luma-bars": 1 }, "ticks": 30, "bandwidth": 3 },
{ "id": "loom-vhold-roll", "machine": "field-loom", "inputs": { "field-splinters": 6 },
"outputs": { "v-hold-roll": 1, "static-canister": 1 }, "ticks": 75, "bandwidth": 6 },
{ "id": "mill-judder", "machine": "telecine-puller", "inputs": { "silver-frames": 3 },
"outputs": { "judder-cams": 1, "field-splinters": 2 }, "ticks": 60, "bandwidth": 5 },
{ "id": "splice-offsets", "machine": "hex-splicer", "inputs": { "gop-crate": 1 },
"outputs": { "explosive-hits": 4, "hf-dust": 2 }, "ticks": 45, "bandwidth": 20 },
{ "id": "can-static", "machine": "artifact-bottler", "inputs": { "hf-dust": 4 },
"outputs": { "static-canister": 1 }, "ticks": 30, "bandwidth": 3 },
{ "id": "mix-rec709", "machine": "artifact-bottler", "inputs": { "chroma-slurry": 2, "luma-bars": 1 },
"outputs": { "gamut-paint-rec709": 1 }, "ticks": 30, "bandwidth": 3 },
{ "id": "mix-dci-p3", "machine": "artifact-bottler", "inputs": { "chroma-slurry": 3, "false-color-speckle": 1 },
"outputs": { "gamut-paint-dci-p3": 1 }, "ticks": 45, "bandwidth": 5 },
{ "id": "mix-rec2020", "machine": "artifact-bottler",
"inputs": { "chroma-slurry": 4, "false-color-speckle": 3, "moire-crystal": 1 },
"outputs": { "gamut-paint-rec2020": 1 }, "ticks": 90, "bandwidth": 12 },
{ "id": "bottle-analog-pack", "machine": "artifact-bottler",
"inputs": { "field-splinters": 2, "v-hold-roll": 1, "static-canister": 1 },
"outputs": { "analog-pack": 1 }, "ticks": 60, "bandwidth": 6 },
{ "id": "bottle-spatial-pack", "machine": "artifact-bottler",
"inputs": { "macroblock-bricks": 2, "ringing-halos": 1, "chroma-slurry": 2 },
"outputs": { "spatial-pack": 1 }, "ticks": 60, "bandwidth": 6 },
{ "id": "bottle-temporal-pack", "machine": "artifact-bottler",
"inputs": { "melt": 2, "bloom-concentrate": 1, "judder-cams": 1 },
"outputs": { "temporal-pack": 1 }, "ticks": 90, "bandwidth": 8 }
]

View File

@ -1 +1,37 @@
[]
[
{ "id": "disc-artifact-bottling", "era": "disc", "cost": { "analog-pack": 10 },
"unlocks": ["artifact-bottler", "can-static"] },
{ "id": "disc-chroma-subsampling", "era": "disc", "cost": { "analog-pack": 10 },
"unlocks": ["subsampler", "subsample-422"] },
{ "id": "disc-macroblock-quarrying", "era": "disc", "cost": { "analog-pack": 15 },
"unlocks": ["quantize-crime"] },
{ "id": "disc-ringing-skim", "era": "disc", "cost": { "analog-pack": 10, "spatial-pack": 15 },
"unlocks": ["skim-ringing"] },
{ "id": "disc-subsampling-420", "era": "disc", "cost": { "analog-pack": 20, "spatial-pack": 15 },
"unlocks": ["subsample-420", "press-anchor-slab"] },
{ "id": "disc-mosh-reactor", "era": "disc", "cost": { "analog-pack": 20, "spatial-pack": 10 },
"unlocks": ["mosh-reactor", "mosh"] },
{ "id": "disc-gamut-paints", "era": "disc", "cost": { "spatial-pack": 20 },
"unlocks": ["mix-rec709", "mix-dci-p3"] },
{ "id": "disc-gop-cadence-ibbp", "era": "disc", "cost": { "spatial-pack": 25, "temporal-pack": 10 },
"unlocks": ["cast-janus-wafer", "assemble-gop-ibbp"] },
{ "id": "disc-i-only-cadence", "era": "disc", "cost": { "spatial-pack": 30, "temporal-pack": 20 },
"unlocks": ["assemble-gop-i-only"] },
{ "id": "stream-field-loom", "era": "stream", "cost": { "analog-pack": 20 },
"unlocks": ["field-loom", "weave-fields", "loom-vhold-roll"] },
{ "id": "stream-telecine", "era": "stream", "cost": { "analog-pack": 25, "temporal-pack": 15 },
"unlocks": ["telecine-puller", "mill-judder"] },
{ "id": "stream-software-decoding", "era": "stream", "cost": { "analog-pack": 20, "temporal-pack": 20 },
"unlocks": ["software-decoder"] },
{ "id": "stream-demosaic-vng4", "era": "stream", "cost": { "spatial-pack": 30 },
"unlocks": ["demosaic-vng4", "pan-speckle"] },
{ "id": "stream-bloom-loops", "era": "stream", "cost": { "spatial-pack": 20, "temporal-pack": 30 },
"unlocks": ["bloom-duplicator", "bloom-grade-3"] },
{ "id": "stream-optical-flow", "era": "stream", "cost": { "temporal-pack": 25 },
"unlocks": ["mv-extractor", "extract-motion"] },
{ "id": "stream-laser-primaries", "era": "stream", "cost": { "spatial-pack": 40, "temporal-pack": 20 },
"unlocks": ["mix-rec2020"] },
{ "id": "stream-hex-splicing", "era": "stream", "cost": { "container-pack": 25, "temporal-pack": 25 },
"unlocks": ["hex-splicer", "splice-offsets"] }
]

View File

@ -69,3 +69,72 @@ to a codex section. NOTES list what you invented beyond the codex.
### Round N — YYYY-MM-DD — [your model]
SHIPPED: / DECISIONS: / INVENTED: / BLOCKED/BROKEN: / CONTRACT REQUEST: / NEXT:
```
### Round 1 — 2026-07-17 — Opus 4.8
SHIPPED:
- Validator `src/sim/data-validate.test.ts`, 28 tests. Fault-injected a recipe with a
dangling item + a machine that doesn't list it — both caught by id and named. It is
not a test that only knows how to pass.
- items 9 → 36, machines 7 → 21, recipes 4 → 35, tech 0 → 17, commissions 1 → 10.
- Verified: `tsc` clean, `vitest run` 52/52 green (LANE-SCREEN's 24 included), and the
live dev build boots with no console errors. All 21 machines appear in LANE-UI's
build bar with zero code changes from them.
DECISIONS:
- **All four seed recipe ids kept alive** (`extract-mdat`, `demux-ore`, `quantize`,
`mosh`) so nothing dangles for a lane mid-round. `mosh` keeps its id but is rewired
to canon: `gop-crate 1 → melt 3 + anchor-slab 1`. The winched-out I-frame is
recovered (§4 mosh reactor: "recoverable — sell it!"), so slabs circulate and delta
wafers are what actually burns. Net economy: 6 wafers → 3 melt.
- `quantize` left at the seed's exact ratio so the M1 chain stays easy to build.
- **M1 chain is longer than the masterplan says — please rule on this.** My orders
spell out `slab + wafers → gop-crate` and `gop-crate → mosh`, but MASTERPLAN M1
describes extractor → belts → demuxer → quantizer → mosh → shipper (seven machines).
Those can't both be true: reaching a crate needs dct-press (slab), p-caster (wafers)
and gop-assembler in between. I followed the orders since they're the more specific
and more recent instruction, and all three run fine as generic crafters. If M1 must
stay at seven machines, say so and I'll add a shortcut `mosh-raw` recipe next round
rather than weaken the canon path.
- Machine params that the codex describes as dials (quantizer Q, subsampler grade taps,
GOP cadence templates) are modelled as **separate recipes per setting**, not as a
per-entity parameter. Cheapest thing that works against the current contract; if SIM
ever wants a real dial, that's a contract change, not a data change.
- Two absurd-but-honest ratios, as ordered: `assemble-gop-i-only` burns 8 premium slabs
for 1 crate (I-only genuinely is the worst cadence — the joke balances itself, nobody
sane builds it), and `pan-speckle` takes 40 HF DUST for 1 speckle (§3 says "panned
like gold", so it's priced like gold panning). Both give hf-dust real sinks.
INVENTED (codex is silent; written in its voice — kill any of these if you disagree):
- `press-anchor-slab` on the DCT PRESS — the press run in reverse is an IDCT, i.e. a
complete picture. Consistent with the field loom and telecine puller, which the codex
explicitly runs backwards. This is where I-frames come from.
- Items `color-plate` (the demosaicer's "full-color sheet", §4) and `v-hold-roll`
(bottled from the §1 Broadcast Shelf v-hold faults, so the ANALOG PACK's "combing +
v-hold roll + static" triptych maps to three real items).
- Machines `artifact-bottler` (§2 calls the packs "a bottled artifact triptych" but
names no bottling plant) and `lane-splitter` (contract has `kind: 'splitter'` and
belts need routing; the codex only ever calls the DEMUXER "the first splitter").
BLOCKED/BROKEN: nothing blocked. Known-incomplete, deliberately:
- Bloom is one grade, not the graded 1-5 line §3 sells by intensity.
- `container-pack` / `cinema-pack` / `mosquito-swarm` / `gibbs-wraith` exist as items
with no recipe — forward canon, per orders. The validator's reachability test has an
explicit FORWARD_CANON allowlist so these four can't hide a real dangling id; delete
entries from it as recipes land.
- `stream-hex-splicing` costs container-packs nothing currently produces. Intentional
dead-end until the moov/container line exists (§6 heists).
CONTRACT REQUEST: `MachineDef.heatPerTick?: number` — the SOFTWARE DECODER is
`kind: 'power'`, and power machines carry no recipes, but `RecipeDef.heat` is the only
heat hook in the contract. So the one machine the codex defines *by* its heat (§4:
"decodes ANYTHING, at any temperature") has no way to declare it. It's shipped as a
plain 45-gen power machine and is currently strictly better than the ASIC, which is
backwards. Alternative if you prefer no contract churn: let power machines run a
recipe with empty outputs — but I'd rather not put `outputs: {}` in the data, and my
validator rejects it today.
NEXT (Round 2 candidates, not self-assigned): bloom grades 1-5 as real items; the
container line (moov shard / offset ribbon / sync key → CONTAINER PACK) which unblocks
the hex splicer; mosquito traps so `vaporwave-sunset` becomes shippable; buffer-tank
and muxer data; reel + broadcast era tech nodes.

View File

@ -0,0 +1,287 @@
/**
* LANE-DATA territory (the one file this lane owns inside src/sim/).
* Validates data/*.json against the contracts. It is the handshake that keeps five
* parallel lanes from drowning in dangling ids: if a lane references a machine,
* recipe, item or tech that data doesn't define, this test names it.
*/
import { describe, expect, it } from 'vitest';
import type { CommissionDef, ItemDef, MachineDef, MachineKind, RecipeDef, TechDef } from '../contracts';
import itemsJson from '../../data/items.json';
import machinesJson from '../../data/machines.json';
import recipesJson from '../../data/recipes.json';
import techJson from '../../data/tech.json';
import commissionsJson from '../../data/commissions.json';
const items = itemsJson as unknown as ItemDef[];
const machines = machinesJson as unknown as MachineDef[];
const recipes = recipesJson as unknown as RecipeDef[];
const tech = techJson as unknown as TechDef[];
const commissions = commissionsJson as unknown as CommissionDef[];
const KEBAB = /^[a-z0-9]+(-[a-z0-9]+)*$/;
const CODEX_REF = /^[1-9]:[a-z0-9]+(-[a-z0-9]+)*$/;
const HEX = /^#[0-9a-f]{6}$/;
const KINDS: MachineKind[] = ['extractor', 'crafter', 'belt', 'splitter', 'power', 'buffer', 'shipper'];
const ERAS: TechDef['era'][] = ['reel', 'broadcast', 'disc', 'stream', 'fortress'];
const itemIds = new Set(items.map((i) => i.id));
const machineIds = new Set(machines.map((m) => m.id));
const recipeIds = new Set(recipes.map((r) => r.id));
/** Collect ids that appear more than once. */
function duplicates(ids: string[]): string[] {
const seen = new Set<string>();
const dupes = new Set<string>();
for (const id of ids) (seen.has(id) ? dupes : seen).add(id);
return [...dupes];
}
/** Item ids referenced by a count map that no item defines. */
function unknownItems(counts: Record<string, number>): string[] {
return Object.keys(counts).filter((id) => !itemIds.has(id));
}
describe('ids and formatting', () => {
it('every collection has unique ids', () => {
expect(duplicates(items.map((i) => i.id))).toEqual([]);
expect(duplicates(machines.map((m) => m.id))).toEqual([]);
expect(duplicates(recipes.map((r) => r.id))).toEqual([]);
expect(duplicates(tech.map((t) => t.id))).toEqual([]);
expect(duplicates(commissions.map((c) => c.id))).toEqual([]);
});
it('every id is kebab-case', () => {
const bad = [...items, ...machines, ...recipes, ...tech, ...commissions]
.map((d) => d.id)
.filter((id) => !KEBAB.test(id));
expect(bad).toEqual([]);
});
it('every item and machine points at a codex section', () => {
const bad = [...items, ...machines]
.filter((d) => !CODEX_REF.test(d.codex))
.map((d) => `${d.id} -> "${d.codex}"`);
expect(bad).toEqual([]);
});
});
describe('items', () => {
it('every colour is a 6-digit lowercase hex', () => {
const bad = items.filter((i) => !HEX.test(i.color)).map((i) => `${i.id} -> "${i.color}"`);
expect(bad).toEqual([]);
});
it('every tier is 0..3', () => {
const bad = items.filter((i) => !Number.isInteger(i.tier) || i.tier < 0 || i.tier > 3)
.map((i) => `${i.id} -> ${i.tier}`);
expect(bad).toEqual([]);
});
it('every item has a display name', () => {
expect(items.filter((i) => !i.name.trim()).map((i) => i.id)).toEqual([]);
});
});
describe('machines', () => {
it('every kind is a known MachineKind', () => {
const bad = machines.filter((m) => !KINDS.includes(m.kind)).map((m) => `${m.id} -> "${m.kind}"`);
expect(bad).toEqual([]);
});
it('every footprint is at least 1x1 in whole tiles', () => {
const bad = machines.filter((m) => {
const { x, y } = m.footprint ?? { x: 0, y: 0 };
return !Number.isInteger(x) || !Number.isInteger(y) || x < 1 || y < 1;
}).map((m) => m.id);
expect(bad).toEqual([]);
});
it('every listed recipe exists and points back at the machine', () => {
const missing: string[] = [];
const mismatched: string[] = [];
for (const m of machines) {
for (const rid of m.recipes) {
const r = recipes.find((x) => x.id === rid);
if (!r) missing.push(`${m.id} lists unknown recipe "${rid}"`);
else if (r.machine !== m.id) mismatched.push(`${m.id} lists "${rid}" but that recipe runs on "${r.machine}"`);
}
}
expect(missing).toEqual([]);
expect(mismatched).toEqual([]);
});
it('belts declare a belt speed and power machines declare generation', () => {
const bad = [
...machines.filter((m) => m.kind === 'belt' && !(m.beltSpeed! > 0)).map((m) => `${m.id} has no beltSpeed`),
...machines.filter((m) => m.kind === 'power' && !(m.powerGen! > 0)).map((m) => `${m.id} has no powerGen`),
];
expect(bad).toEqual([]);
});
it('every machine declares a non-negative idle draw and an asset key', () => {
const bad = machines
.filter((m) => !(m.powerDraw >= 0) || !KEBAB.test(m.asset))
.map((m) => `${m.id} -> draw ${m.powerDraw}, asset "${m.asset}"`);
expect(bad).toEqual([]);
});
});
describe('recipes', () => {
it('every recipe runs on a machine that exists', () => {
const bad = recipes.filter((r) => !machineIds.has(r.machine)).map((r) => `${r.id} -> "${r.machine}"`);
expect(bad).toEqual([]);
});
it('every recipe is listed by the machine it runs on', () => {
const orphans = recipes.filter((r) => {
const m = machines.find((x) => x.id === r.machine);
return m && !m.recipes.includes(r.id);
}).map((r) => `${r.id} is not listed by ${r.machine}`);
expect(orphans).toEqual([]);
});
it('every input and output item exists', () => {
const bad: string[] = [];
for (const r of recipes) {
for (const id of unknownItems(r.inputs)) bad.push(`${r.id} consumes unknown item "${id}"`);
for (const id of unknownItems(r.outputs)) bad.push(`${r.id} produces unknown item "${id}"`);
}
expect(bad).toEqual([]);
});
it('every recipe produces something in whole positive counts', () => {
const bad: string[] = [];
for (const r of recipes) {
if (Object.keys(r.outputs).length === 0) bad.push(`${r.id} has no outputs`);
for (const [id, n] of [...Object.entries(r.inputs), ...Object.entries(r.outputs)]) {
if (!Number.isInteger(n) || n < 1) bad.push(`${r.id} -> "${id}" count ${n}`);
}
}
expect(bad).toEqual([]);
});
it('every recipe takes a whole positive number of ticks', () => {
const bad = recipes.filter((r) => !Number.isInteger(r.ticks) || r.ticks < 1)
.map((r) => `${r.id} -> ${r.ticks} ticks`);
expect(bad).toEqual([]);
});
it('only extractors run input-free recipes', () => {
const bad = recipes.filter((r) => Object.keys(r.inputs).length === 0)
.filter((r) => machines.find((m) => m.id === r.machine)?.kind !== 'extractor')
.map((r) => `${r.id} conjures items on non-extractor ${r.machine}`);
expect(bad).toEqual([]);
});
it('every heat value is a 0..1 fraction per craft', () => {
const bad = recipes.filter((r) => r.heat !== undefined && !(r.heat >= 0 && r.heat <= 1))
.map((r) => `${r.id} -> heat ${r.heat}`);
expect(bad).toEqual([]);
});
});
describe('tech', () => {
it('every era is a known era', () => {
const bad = tech.filter((t) => !ERAS.includes(t.era)).map((t) => `${t.id} -> "${t.era}"`);
expect(bad).toEqual([]);
});
it('every cost is paid in items that exist, in whole positive counts', () => {
const bad: string[] = [];
for (const t of tech) {
for (const id of unknownItems(t.cost)) bad.push(`${t.id} costs unknown item "${id}"`);
for (const [id, n] of Object.entries(t.cost)) {
if (!Number.isInteger(n) || n < 1) bad.push(`${t.id} -> "${id}" count ${n}`);
}
if (Object.keys(t.cost).length === 0) bad.push(`${t.id} is free`);
}
expect(bad).toEqual([]);
});
it('every unlock names a machine or a recipe that exists', () => {
const bad: string[] = [];
for (const t of tech) {
if (t.unlocks.length === 0) bad.push(`${t.id} unlocks nothing`);
for (const u of t.unlocks) {
if (!machineIds.has(u) && !recipeIds.has(u)) bad.push(`${t.id} unlocks unknown "${u}"`);
}
}
expect(bad).toEqual([]);
});
it('no two techs unlock the same thing', () => {
expect(duplicates(tech.flatMap((t) => t.unlocks))).toEqual([]);
});
});
describe('commissions', () => {
it('every want names an item that exists', () => {
const bad: string[] = [];
for (const c of commissions) {
for (const id of unknownItems(c.wants)) bad.push(`${c.id} wants unknown item "${id}"`);
}
expect(bad).toEqual([]);
});
it('every commission wants a whole positive quantity of something', () => {
const bad: string[] = [];
for (const c of commissions) {
if (Object.keys(c.wants).length === 0) bad.push(`${c.id} wants nothing`);
for (const [id, n] of Object.entries(c.wants)) {
if (!Number.isInteger(n) || n < 1) bad.push(`${c.id} -> "${id}" count ${n}`);
}
}
expect(bad).toEqual([]);
});
it('every commission has fax text and a positive reward', () => {
const bad = commissions
.filter((c) => !c.flavor.trim() || !(c.rewardBandwidth > 0))
.map((c) => `${c.id} -> reward ${c.rewardBandwidth}`);
expect(bad).toEqual([]);
});
});
describe('economy reachability', () => {
/** Items an extractor can pull, closed over every recipe that can then run. */
function producible(): Set<string> {
const have = new Set<string>();
for (;;) {
const before = have.size;
for (const r of recipes) {
if (Object.keys(r.inputs).every((i) => have.has(i))) {
for (const o of Object.keys(r.outputs)) have.add(o);
}
}
if (have.size === before) return have;
}
}
it('the M1 flow reaches MELT from raw ore', () => {
expect([...producible()]).toContain('melt');
});
it('every item is either producible or declared forward canon', () => {
// Forward canon: named in the codex and shippable one day, but nothing makes them yet.
// Keep this list short and delete entries as recipes land.
const FORWARD_CANON = new Set([
'mosquito-swarm', 'gibbs-wraith', 'container-pack', 'cinema-pack',
]);
const have = producible();
const orphans = items
.map((i) => i.id)
.filter((id) => !have.has(id) && !FORWARD_CANON.has(id));
expect(orphans).toEqual([]);
});
it('no recipe is a free lunch: every craft that only recycles its own output loses something', () => {
const bad = recipes.filter((r) => {
const ids = Object.keys(r.outputs).filter((o) => r.inputs[o] !== undefined);
return ids.length > 0 && ids.every((o) => r.outputs[o] > r.inputs[o]) &&
Object.keys(r.inputs).every((i) => r.outputs[i] !== undefined);
}).map((r) => `${r.id} multiplies its own inputs`);
expect(bad).toEqual([]);
});
});