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

Re-advise: add-cartridge-model

ADVISE: send-back READER: fable-5-1-arch-review SPAWN: /Users/dukejones/work/ClientProjects/AllSystemsGo/AICamera/.spawns/add-cartridge-model-1789067169-80747-804d786b

Rigor: architecture. Density: lean. Reader family: Claude (Fable 5.1). Author this pass: grok. Read order: packet Why → specs/cartridge-runtime/spec.mdsim/3090/catalog.jsonsim/3090/check_catalog.py (run, exit 0) → proposal.mdsteer.md. Blind take written and saved before design.md, tasks.md, or 2026-09-10-advise.md were opened. Then docs/3090-SIM.md, docs/references/T4000.md, living openspec/specs/hardware-kit/spec.md.

Prior send-back asked for three spec pins. All three are in the spec and the checker exercises them. This pass is not about those.

Blind take (written before design.md)

  1. Pin: watts semantics. Spec says the packer is memory + RAM + watts + NVENC, and lists watts as a ceiling. The seed has sam2-tiny at 400 W on a 350 W brick, and every t4000 home preset sums past 70 W (ar 90, talent-tracking 110). The checker never reads watts. Spec, seed, and acceptance disagree on whether power packs.
  2. Pin: what a watts number is. 390-407 W is board draw under one cart, not a per-cart increment; unified-SoC watts are module TDP. Sum or max of those against a ceiling is not a physics the GPU obeys; it caps and throttles, it does not refuse.
  3. Pin: swap formula. Always-on carts are resident alongside the one swapped slot, so swap is Σ(always-on) + max(slottable), not max over everything. Immaterial at 0.3 GB today, wrong in kind, and wrong the day cuvslam gets a 3090 cost.
  4. Refuse: “python3 check_catalog.py exits 0” as proof the catalog seats presets, while the spec’s own watts rule would bounce every t4000 preset. The check proves memory + NVENC only; it must say so or enforce watts.
  5. Refuse: residency living in catalog.json without a src. Spec assigns swap-vs-resident to add-sim-box-mix; the catalog mirrors it silently and will go stale.
  6. Tradeoff: pack on watts (static refuse, fact:false derates) versus sag on watts (runtime drop, already in spec). Static watts double-handles what sag already owns and refuses on numbers nobody trusts. I would make watts a record like latency this pass and let sag be the enforcement.
  7. Kept: max-of-peaks on swap, sam2-maxine-4k on 4K ids only, always-on null = absent, t4000 unified_gb, fact/src, never-on-thor override. All present and the checker exercises the ones it can.
  8. Note: never-on-thor refuses on 3090 too. Fine because 3090 is the Thor sim, but the shelf name encodes an envelope the spec never names.
  9. Note: preset “home” envelopes live only in the checker. That is acceptable for a seed; it is not a catalog fact.
  10. Note: absent always-on is “logged” per spec; the checker prints OK with no absence line. Packer concern, not catalog.

Compare against design.md, tasks.md, prior advise

The three prior pins are closed. Swap max-of-peaks (spec + checker swap branch), sam2-maxine-4k only on sam2-4k / maxine-4k with sam2-tiny / maxine untagged so talent-tracking seats (checker OK on t4000), always-on null = absent (cuvslam null on 3090, master-capture and snap seat on 3090 without it). unified_gb, job, src / fact, never-on-thor override, envelope ceilings, latency-as-record: all present. Take line 7 stands.

Take 1 and 4 are one contradiction, and the acceptance box hides it. The spec says in two places that power packs: “This packer SHALL be memory, host/unified RAM, watts, and NVENC only” and “The catalog SHALL list ceilings per envelope: … watts”. tasks.md ticks “every seeded preset seats on its home envelope under the spec’s own rules”. The checker reads vram_gb, host_ram_gb, unified_gb, nvenc. It never reads watts. Under the spec’s own sum rule on t4000 (ceiling 70 W):

presett4000 Σ wattsseats under spec
master-capture25yes
snap95no
ad65yes
ar90no
talent-tracking110no

On 3090 (ceiling 350 W, max rule) sam2-tiny alone is 400 W. So the seed catalog, read by a packer that does what the spec says, refuses three of five presets on the camera and refuses the one cart that was measured running on the brick. The checker exits 0 because it is a memory + NVENC checker. The acceptance line in tasks.md is not true as written.

Take 2 is why the numbers cannot simply be fixed. 390–407 W in docs/3090-SIM.md is nvidia-smi board draw with SAM2 running, not SAM2’s increment over idle. The 3090 row says “350 W” as TDP. T4000.md says the module defaults to 70 W and “clocks throttle if module power exceeds 90 W”. Power on both boxes is a cap the silicon enforces by slowing down. It does not refuse a load. Summing per-cart wattages against a TDP is a model the hardware does not follow, and the t4000 numbers are fact: false derates of a board-draw measurement, so a refusal on them is a refusal on a guess. The living hardware-kit spec already owns the real power path: “GIVEN thermal or power headroom is gone → AI is dropped and record continues”, and this change’s own “Always-on reserved” scenario restates it as sag.

Steelman. The author can say: watts is listed so the packer can refuse when a number is honest, and the checker is a seed smoke test, not the packer. Fair on the second half. Not on the first: the spec does not say “watts when fact”, it says the packer is watts, and the downstream change (add-cartridge-pack) will implement the spec, not the checker. The contract this change exists to fix is exactly which axes a packer refuses on. Leaving watts ambiguous hands the packer a rule that bounces the camera’s own presets.

Take 3 stands as a sentence. “Max of per-slot peaks” is written over the whole set. Always-on carts are not slots; they are resident beside whichever slot is loaded. Swap should read Σ(always-on) + max(slottable) against the ceiling. The checker does max over everything, which is the same answer today only because nvenc-hevc is 0.3 GB and cuvslam is null on 3090. The packer will copy the sentence, not the coincidence.

Take 5 withdrawn. Living hardware-kit: “Pack and residency default by envelope and SHALL be overridable.” The catalog carrying residency as the envelope default, with src, is that rule, not a fork. Note only: when add-sim-box-mix flips a default, this file is the second place.

Takes 8, 9, 10 stay notes. No spec change asked.

Tradeoff (take 6), with a recommendation. Two legal shapes:

  • (a) Watts is a record, like latency. Pack is memory, host/unified RAM, and NVENC. Sag (hardware-kit) is the power enforcement. Seed and checker are already this shape; the spec changes two clauses and the acceptance line becomes true.
  • (b) Watts packs. Then watts must be defined as per-cart increment over idle, the 3090 sam2-tiny row must be re-measured as a delta or marked fact: false, the t4000 derates must be reconciled to 70 W, and the checker must enforce it.

Take (a). It matches the numbers that exist, the physics both datasheets describe, and the sag path the living spec already owns. (b) is a measurement campaign dressed as a spec edit.

Why send-back, not accept

Narrow. Two sentences in specs/cartridge-runtime/spec.md, no code, no reseed:

  1. Power axis. Either strike watts from “Latency vs pack” and “Envelope ceilings” as a pack axis and add one line: watts is recorded per cost and per envelope; power enforcement is sag (hardware-kit), not seat/bounce. Or keep it and define watts as increment-over-idle, fix the seed rows that exceed their ceiling, and make the acceptance command check it. The acceptance box in tasks.md is unticked below until one of these is true.
  2. Swap formula. “Max of per-slot peaks” → Σ(always-on) + max(slottable) against the ceiling on a swap envelope. One sentence; the checker may stay as is this pass.

With those, the catalog is a contract a packer can implement without bouncing the camera. Not required for accept: takes 8, 9, 10.