openspec/changes/archive/2026-09-10-add-cartridge-model/reviews/2026-09-10-advise.md

Advise: add-cartridge-model

ADVISE: send-back READER: agt-fable-5-1-arch-review SPAWN: /Users/dukejones/work/ClientProjects/AllSystemsGo/AICamera/.spawns/aicam-cart.1-1789064442-97604-b2bee6d1

Rigor: architecture. Density: lean. Reader family: Claude (Fable 5.1). Author family: grok. Read order: packet Why → change spec specs/cartridge-runtime/spec.mdproposal.mdsteer.md~/work/Family/web3d-space/docs/all-systems-go/MODELS.md, MODES.mddocs/3090-SIM.mdopenspec/changes/add-sim-box-mix/specs/hardware-kit/spec.mdopenspec/specs/hardware-kit/spec.mdsrc/lib/bay/t4000.ts.grok/skills/3090-loadout/SKILL.md. Blind take written before design.md / tasks.md were opened. No living cartridge-runtime spec exists.

Blind take (written before design.md)

  1. Pin. Cost record splits VRAM and host RAM. That is a 3090 shape. Thor is 64 GB unified; the t4000 column must sum both against one pool, or carry a single unified field. Otherwise a 3090-measured cost (3 GB VRAM + 2 GB host) is seated against the wrong two ceilings on t4000.
  2. Pin. “Null means unmeasured, not free” needs a packer consequence in the spec: a null cost on the target envelope SHALL NOT seat; it may gauge or list. Without that scenario, null becomes free by default in the first packer that sums a column.
  3. Pin. No throughput cost. 3090-SIM says the live stack is a 273 GB/s and 70 W problem, and the 3090 “makes the nets look cheap and the memory envelope look expensive.” A knapsack on VRAM/RAM/W/NVENC alone will seat 9B + klein + SAM2 in 64 GB and miss that 30 fps does not survive. Either the record carries a latency class (LIVE/NEAR from MODELS.md) and the pack counts LIVE slots against a budget, or the spec states plainly that the pack is memory-only and 30 fps is a separate gauge.
  4. Pin. never-on-thor is a shelf value, but it is derivable from the cost record (no t4000 cost, or a t4000 cost over envelope). Two fields that say the same thing drift. Either drop the shelf value and let the per-envelope record decide, or define shelf as an override that wins and say why.
  5. Pin. sam2-maxine-4k is conditional (steer: “SAM2↔Maxine at 4K”). A flat exclusive tag cannot express the condition. MODES.md mode 5 Talent tracking runs SAM2 + Maxine together. A flat tag refuses that preset. Either the tag carries a resolution predicate, or it is dropped this pass and left to measured costs.
  6. Pin. On 3090 co-residency is physically impossible (16 GB host, stages unload, sim-box-mix says swap). The spec must define what “seated” means on a swap envelope: sum of peaks (refuses everything the 3090 actually runs) or max of peaks (what the harness does today). Otherwise the live brick refuses its own working loadout.
  7. Pin. Id namespace. Scenario ids are klein-4b, qwen-27b: model ids. MODELS.md names jobs (“On-set snap”, “AD heavy”, “Shot-calling”) and the spec says job names come from there and shall not fork. A cart needs both: the job it fills (from MODELS.md) and the model that fills it. Without a job field the catalog invents a second namespace, which is the fork the spec forbids.
  8. Refuse. A third copy of costs. 3090 FACT numbers live in docs/3090-SIM.md Measured, and derates already live twice (doc + t4000.ts, flagged on stage-bay). A hand-typed costs.3090 in JSON is copy three. The catalog must cite the doc row (date + stage) per cost, or the doc table must be generated from the catalog. One home.
  9. Refuse. Always-on encode without a counted NVENC cost. T4000 HQ is 2× 4Kp30; mode 11 multicam can exceed. If the always-on cart’s NVENC sessions are not in the record and summed, “a pack SHALL NOT drop encode” is unenforceable and record can silently fail while AI stays seated.
  10. Tradeoff. JSON catalog vs TS module. TS gives types and /bay imports it directly, but the packer’s first host is the Python harness on fractal1. Take JSON with a types.ts mirror and a schema check in CI. Cost: one more file to keep in step. Benefit: Python and Svelte read the same bytes.

Compare against design.md, tasks.md

Shape matches the take. Catalog record is id / kind / shelf / exclusive / costs-per-envelope, JSON, with MODELS.md and MODES.md as the name source and docs/3090-SIM.md as the 3090 FACT home. Tradeoff 10 is decided the way I would decide it. Steer records presets, knapsack plus physics tags, and live-brick envelope as user decisions; I do not contest any of them.

Pin 6 is a contradiction with add-sim-box-mix, and it is on the live brick. Design: “the packer refuses a seat when the live-brick envelope is exceeded.” Sim-box-mix, ACTIVE in the same wave: the 3090 “SHALL emulate that pipeline by loading one slot at a time and unloading it” and residency on 3090 is swap. Sum-of-peaks refuses klein + SAM2 + CLIP, which is exactly the loadout the harness runs today by swapping. The design’s own example (klein 16 GB VRAM + 8 GB host) plus SAM2 already breaks 16 GB host. The spec must say what a pack means on a swap envelope. One sentence, but it has to be written before the actor sums a column.

Pin 5 is a contradiction with the tasks’ own preset list. Tasks seed talent-tracking as a preset and sam2-maxine-4k as an exclusive tag. MODES.md mode 5 is SAM2 plus Maxine. Under the spec’s flat rule (“cartridges that share an exclusive tag SHALL NOT be co-resident”) the seed catalog refuses its own preset. Steelman: the author may intend the tag to sit on sam2-*-4k / maxine-*-4k variants as distinct cart ids, with 720p variants untagged. That is a legitimate way to keep tags flat. It must be written, because the spec as read forbids the mode.

Pin 2 sharpens against always-on. cuVSLAM is Isaac ROS; nothing on fractal1 runs it, so its 3090 cost is null. Always-on plus “null is not free” plus “pack SHALL NOT drop always-on” means every pack on the live brick either seats a null (null became free) or refuses (no pack ever seats). The three rules are individually right and jointly undefined. Needs one scenario: null on the target envelope for an always-on cart is absent, not unseatable, and is logged; null on a slottable cart cannot seat.

Pin 8 is demonstrated by the design’s example. "vram_gb": 16.0 for klein-4b on 3090 is the disk weight size (loadout: text enc 8.05 + transformer 7.75 + VAE 0.17). The measured run used cpu_offload; VRAM peak under offload is not 16 GB, and 3090-SIM does not record a VRAM peak for klein at all. The first hand-typed number is already a guess dressed as FACT. Each cost needs a src (doc anchor plus date) or fact: false, and the doc row stays the home.

Pin 1 unaddressed. t4000 record carries vram_gb and host_ram_gb as two nulls. 3090-SIM: “Do not treat 16 GB host RAM as a T4000 limit — Thor unified 64 GB eats the host half.” The record shape is a 3090 shape applied to a unified box. Local fix: unified_gb on t4000, or a stated rule that the packer sums the two columns against one ceiling on unified envelopes.

Pin 3 unaddressed, and sim-box-mix already has the answer. Sim-box-mix names SMALL and LARGE packs and a resident-30fps target. This catalog carries neither a latency class nor a SMALL/LARGE marker. The two changes describe the same carts with disjoint vocabularies. Not a redesign: add latency (LIVE / NEAR / MIN, from MODELS.md) or pack (SMALL / LARGE) to the record, or state in the spec that this pack is memory-and-power only and 30 fps is sim-box-mix’s gauge.

Pin 4 downgraded to a note. Design keeps never-on-thor. MODES.md “Do not put on the body” is a declared list, and it covers policy (H3 territorial weights) that no measured cost expresses. Shelf as an override that wins is right. Say so in the spec; today it reads as a third fact.

Pin 9 partly answered, and it exposes a gap. nvenc is a field. Good. But no envelope ceilings exist anywhere: VRAM 24 / 64 / 96, host 16, watts 350 / 70, NVENC sessions 1 / 2 HQ. Sim-box-mix names the four boxes; neither change records what they hold. The Why says the catalog is what lets a packer refuse. Costs without ceilings cannot refuse. The home is hardware-kit (sim-box-mix) or an envelopes block beside the catalog; either is fine, but one of the two ACTIVE changes has to own it and today neither does.

Pin 7 stands as a task. Ids are model ids; MODELS.md rows are jobs. One job string per cart pointing at the MODELS.md row keeps the “do not fork” promise honest and makes klein↔27B legible as “same slot, both do not fit” rather than a hand-coded tag.

tasks.md has no acceptance step. The cheapest test this change can carry: every seeded preset seats on its home envelope under the spec’s own rules. That single check catches pins 2, 5, and 6 before any packer exists. Added below.

Steelman for the author. This is a lean contract for a packer that does not exist yet, on a one-person lab brick, steered in one sitting. Every pin above is a sentence or a field, not a redesign, and the author could reasonably say “the packer change answers those.” I agree on 1, 3, 4, 7, 8, 9: they are task pins. I do not agree on 2, 5, 6: those are the spec refusing its own live brick and its own preset, and spec.md is not on this reader’s write path.

Why send-back, not accept

The catalog shape is right and steered. Send-back is narrow: three edits to specs/cartridge-runtime/spec.md before act.

  1. Define a pack on a swap envelope (3090): max-of-peaks across the swap set, or name the rule. Cite sim-box-mix.
  2. Resolve sam2-maxine-4k against the talent-tracking preset: resolution-variant cart ids, or drop the tag this pass.
  3. Add the null scenario: always-on with null cost on the target envelope is absent and logged; slottable with null cost cannot seat.

With those three in the spec, the rest are tasks and act can proceed. Re-advise is not required; a fresh reader can confirm the three lines in the next act verify.