Repository navigation
plan.estimate prices every H3 variant at the catalog figure - measured range is 3.5-44 min #866
Description
Split out of #861.
What happened
POST /api/validate quoted ~42 min for the ep 6 dialogue-short regardless of what changed. The matrix measured the same shot from 3.5 to 44.2 min depending on canvas, weights, LoRA, steps, attention and cache. Agents quote plan.estimate to the user before a run, so the quote is wrong by up to 12x in both directions.
Measured (one H3 Ref2VA shot, 124 frames, 4 refs, 3090)
| Rung | Canvas | Weights | LoRA | Steps | Attn | Extra | Min (3090) | Face score /5 | Staging family |
|---|---|---|---|---|---|---|---|---|---|
| L0 | 960x544 | int4 | ref2v 4-step v0.1 | 5 | sage_hub | 3.5 | 2 | turbo-4 | |
| L1 | 1344x768 | int4 | ref2v 4-step v0.1 | 5 | sage_hub | 5.4 | 2.5 | turbo-4 | |
| L7 | 1344x768 | int8 | ref2v 4-step v0.1 | 5 | sage_hub | 7.2 | 3 | turbo-4 | |
| L3 | 1344x768 | int8 | ref2v 8-step v1.0 768p | 9 | sage_hub | 11.2 | 3 | turbo-8 | |
| L4 | 1344x768 | int8 | ref2v 8-step v1.0 768p | 9 | native | 14.5 | 3 | turbo-8 | |
| B4 | 960x544 | int4 | none | 12 | sage_hub | 6.6 | 3 | base-int4 | |
| B1 | 960x544 | int4 | none | 30 | sage_hub | 15.1 | 3 | base-int4 | |
| B5 | 1344x768 | int4 | none | 12 | sage_hub | 11.8 | 3.5 | base-int4 | |
| B6 | 1344x768 | int4 | none | 8 | sage_hub | 8.5 | 1.5 (duplicated a character) | base-int4 | |
| B3 | 1344x768 | int8 | none | 12 | sage_hub | 12.9 | 3.5 | base-int8 | |
| L5 | 1344x768 | int8 | none | 30 | sage_hub | 29.5 | 4 | base-int8 | |
| L6 | 1344x768 | int8 | none | 30 | native | 44.2 | 4 | base-int8 | |
| P1 | 1344x768 | int8 | none | 30 | sage_hub | first_block 0.05 | 23.1 | 4 | base-int8 |
| C1 / C2 (Pat close-up) | 1344x768 | int8 | none | 12 / 30 | sage_hub | 13.9 / 22.8 | 4 / 4 | base-int8 |
Base model = lora_model_name: null, shift video 12 / audio 3. Turbo = shift per the LoRA. All Ref2VA with 4 references (2 portraits, 2 voice clips), 124 frames. Scores are face realism at native-resolution crops, judged by Claude from frames (5 = cinema-grade photographic).
Per-pass cost at 1344x768 int8 is ~56 s (sage), ~81 s native-equivalent; total ~= load 0.7 + encode 0.9 + passes x s/pass + decode 0.9 min.
Expected
The estimate should scale with the things that move it:
- passes =
num_inference_steps - 1(scheduler counts the terminal zero); - s/pass by canvas x frames x weights x attention backend (a small table per pipeline, or learned from observed runs keyed by those fields -
observed_costalready records runs, it just keys too coarsely); - cache: first_block at >=30 steps ~0.75x passes; at <20 steps 1.0x;
- compile: + first-pass compile time per new shape.
_tempered blends toward the curated figure; with the curated figure being the 8-step template's, an observed 30-step run gets pulled down toward it.
Questions
- Key observed cost by (pipeline, canvas, frames, weights_dtype, lora present, steps, attention, cache)? That's a lot of buckets; a per-pass model (fixed + passes x s/pass) generalizes better from few runs.
- Should validate say
basis: "derived"when it scales a measured s/pass to a new step count?
Source: H3 quality matrix on lem (2x RTX 3090 24 GB, 125 GB host RAM), workspace h3-quality, Known Issues ep 6 shots, seed 42. Full data and per-run assessments: ~/testing/opus/known-issues/quality/matrix.json, report quality/REPORT.md; every run in h3-quality carries a note with its config, minutes and score. Related: #861 (follow-up experiments).
Activity
dkackman commentedon Oct 14, 2026
triage: escalate — replaces the per-workflow estimate with a per-pass cost model keyed by canvas × frames × weights × LoRA × steps × attention × cache × compile, and a possible new
basis: "derived". That's a wide argument matrix plus new estimate semantics, and the issue's own design questions (bucket keys vs fixed + passes × s/pass; the basis value) need a decision before a fix session builds it. A repro-only check on one H3 shot would pass with most of the matrix still untested. (claude-opus-5-5, anthropic)dkackman commentedon Oct 14, 2026
Review: approved for planning, smaller than the full per-pass model.
What the code does today (
dw/server/observed_cost.py,dw/plan.py):cost_drivers.dialogue-shortdeclaresshots,num_inference_steps,widthandheight._tempered(plan.py:260) blends toward the curated figure even when the requested bucket isn't the default one, so a thin 30-step observation gets pulled toward the 8-step number.basis: "derived"already exists (plan.py:249, used byper_entryscaling at:873).Plan scope:
steps - 1) and by pixels × frames, and report it asbasis: "derived". Temper toward the curated figure only when the bucket is the default one; otherwise temper toward the scaled figure. This fixes both failure modes.cost_drivers. When the request differs from the curated run's value on one of them, flaglow_confidencerather than quote a scaled number as if it were exact.h3-qualitymatrix, which is the acceptance test set.Q1: keep
cost_driversbucketing and add the scaling, rather than a seven-key bucket.Q2: yes. Reuse the existing
derivedvalue; no new basis.claude-opus-5-5 / anthropic, for Don
44 remaining items
dkackman commentedon Oct 15, 2026
Spec for plan v4. Tester:
claude-opus-5-5viaanthropic.All cases are in
regression-suite-complete.mdand carrypending: #<stage>. v4 reshaped stages 0 and A, so those two stages get new cases. The v2 cases they replace are up for retirement in dkackman/harnest#123.25.0vs25all keep the fingerprint. Prompt, steps, dtype and image count move it. C-F498 (H3): inline and by-name match (the integral-float fix), and seed 43, stripped metadata and a saved copy with othercost_driversall keep the fingerprint. Attention (two values), cache, int8 and LoRA move it. C-F499: one saved workflow edited in place. A metadata edit keepscached_steps: 1, and a re-run is served from the cache (about a minute of GPU). Seed, dtype,subfolder,file_base_nameand attention givecached_steps: 0.catalogat its defaults, thenderivedscaling by steps, canvas, frames, scaling down, the 130→141 snap edge, the top of the range, and the defaults passed explicitly. C-F501: a saved dialogue-short copy is ≈21 and scales by summed frames. C-F502: the served ratios (2.5 for steps, 1.976 for canvas) reproduce B4→B1, B4→B5 and B3→L5 inside the 30% bar. C-F477: refusals and the non-numericunknown. C-F480: tempering toward the scaled figure.Superseded, retirement requested in harnest#123: C-F473, C-F474, C-F475, C-F476, C-F478 and C-F479.
Notes (none blocks the spec):
_repriced) "multiplies with" the summed-frames ratio. When a shot is removed, applying both counts the dropped frames twice: D×4/5×496/637 ≈ 0.62·D, against ≈ 0.78·D from summed frames alone. C-F501 call 3 only records which one the server gives. The plan should say which is intended.catalog. If it comes backinherited, the case calls that a finding against the plan's premise rather than a pass. C-F502 judges by ratios, so it doesn't depend on which price wins, unless the copy is pricedinherited, which the stage A path doesn't scale.Merge docs/866-closeout (#866)
dkackman commentedon Oct 15, 2026
Close-out: all four stages closed and verified. claude-opus-5-5 / anthropic, lead
docs/proposals/complete/estimate-scaling-complete.md, with atodo.mdentry. Merged todevelop@ 828cccb (commit edfd00d). It covers what was built, what was cut (compile as a driver, the learned per-pass fit), the follow-ups bug: validate's cached_steps probe ignores the card's warm workflow identity #911 and idea: step cache across workflow identities (renamed and inline copies hit) #912, and the bounces per stage. Stage costs are left out because no stage comment recordsusage:.source:names one of this feature's stages (#866 stage 0: key the plan fingerprint and step cache only by what changes the output #905, #866 stage A: scale the catalog estimate along steps, canvas and frames #906, #866 stage B: H3 weights, LoRA, attention and cache as unscalable cost drivers #907, #866 stage C: the inherited estimate scales by steps and canvas too #908), and close the parent if they all pass.dkackman commentedon Oct 15, 2026
tester verify of #866 (feature parent), progress record. Model: claude-opus-5-5 via anthropic. lem on develop @ 828cccb.
The session budget ran out before every stage case could be run, so labels are left unchanged: still
status:fixed-pending-verify. No failures so far.Passed (all over MCP this session, workspace regression-complete):
differs. Defaults give 7.8 observed with no flags.differs[], noscaled.scaled"steps 30 vs 9; canvas 1344x768 vs 960x544". That is within ±5% of M0×(30/9)×1.9765 ≈ 51.4 and within 10% of T = 51.9.scaled"frames 192 vs 124",inherited_fromkept. Expected ≈ 12.1.differs.differs[] with no low_confidence.pending: #907line has now been removed from C-F482.Observations (not failures):
low_confidence: truewith an emptydiffers. That is presumably the scaling itself; the case doesn't forbid it.inherited_fromswitched to templates/minimax/voice-timbre-reference (runs 3) instead of reference-to-video. That template presumably has a native-attention bucket. The case only asks for basis inherited with a number.Deviations from the cases as written:
reasonstrings, and sets seed 42 in variables.Not yet run (the next session must finish these before closing):
dkackman commentedon Oct 15, 2026
Verified: feature parent close-out. Tester claude-opus-5-5 via anthropic, against lem develop @ 8605824, workspace
regression-complete.I ran every case whose
source:names #905, #906, #907 or #908. All of them pass. Nopending: #905–#908lines remain in the suites.Carried from the previous verify session on 828cccb (also claude-opus-5-5): C-F477, C-F481, C-F482/C-F483, C-F484, C-F485, C-F498 and C-F500 passed. C-F502 also passed there, with one variance noted on the issue.
Run this session on 8605824:
sha256:70cf5bf2…, and a repeat call returned the same value.num_inference_steps: 25.0literal: the first batch dropped it, so I ran it separately on my reconstructed inline copy, which hashes to229cc5…, not F0.25.0and25both gavesha256:229cc5bd…, so integral floats are canonicalized.scaledreads "frames 705 vs 637 over shots".scaledstring reads "scaled from the curated 17 min: frames 496 vs 637". 17 is D×4/5, yet 17×496/637 would be 13.2. So the label names a different base than the number used.templates/minimax/dialogue-shortby name instead of saving anobserved-stripped copy.observed38.6 (5 runs), notcatalog.num_framesvalues and the entry count matter to the estimate.c-f499-t2i(W0, seed 42). J1d4286bd05039succeeded in 12.5 s, and validate returnedcached_steps: 1.cached_steps: 1.2d4851f99650succeeded in 0.4 s. Its manifest marksmainasreused: true, serving J1's own file…/20261015-122442-475c5693/final/test_image-0.0.jpg.get_job_events(J2), since the manifest already showsreusedon the same card.cached_steps: 0:configuration.components.unet.attention_backend: "native". Validate accepted it, giving a new fingerprint.scaled"steps 100 vs 25".8ab7b8cd1e9eran 100 steps in T = 20.5 s (0.34 min).observed0.6, runs 1,curated_minutes0.8 (not 0.2),observed_minutes0.3,tempered: true. 0.6 lies within [T, curated].Closing as completed.
fix(plan): #851 - scale an inherited price by for_each member count