Skip to content

plan.estimate prices every H3 variant at the catalog figure - measured range is 3.5-44 min #866

Closed
4 of 4 issues completed

Description

@dkackman

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_cost already 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

  1. added
    bugSomething isn't working
    performanceTiming/perf regression flagged by the regression agent
    owner:leadFeature lead's turn (harnest R11)
    owner:donParked for human decision
    status:needs-approvalParked pending a written proposal + human approval
    and removed
    owner:leadFeature lead's turn (harnest R11)
    on Oct 14, 2026
  2. dkackman commented on Oct 14, 2026

    @dkackman
    OwnerAuthor

    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)

  3. dkackman commented on Oct 14, 2026

    @dkackman
    OwnerAuthor

    Review: approved for planning, smaller than the full per-pass model.

    What the code does today (dw/server/observed_cost.py, dw/plan.py):

    • Observed cost is already bucketed by the workflow's cost_drivers. dialogue-short declares shots, num_inference_steps, width and height.
    • Two failure modes produce the flat ~42 min:
      1. An empty bucket falls back to the curated figure, unscaled. Most matrix rungs are new combinations, and many ran as ladder.py's custom workflows, which have no history.
      2. _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.
    • Weights dtype, LoRA, attention and cache are not drivers. Runs that differ only in those share a bucket.
    • basis: "derived" already exists (plan.py:249, used by per_entry scaling at :873).

    Plan scope:

    1. Scale, don't fall back. When the requested bucket is empty, scale the curated figure by passes (steps - 1) and by pixels × frames, and report it as basis: "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.
    2. Drivers that can't be scaled. For weights dtype, LoRA presence, attention and cache, where a template exposes them as variables (feature: H3 templates - expose attention_backend + cache everywhere, and a quality tier preset that sets the coupled knobs together #865 adds some): add them to cost_drivers. When the request differs from the curated run's value on one of them, flag low_confidence rather than quote a scaled number as if it were exact.
    3. Deferred: a learned fixed + passes × s/pass fit over observed rows. It comes back only if stage 1's quotes are still off by more than ~30% against the h3-quality matrix, which is the acceptance test set.

    Q1: keep cost_drivers bucketing and add the scaling, rather than a seven-key bucket.
    Q2: yes. Reuse the existing derived value; no new basis.

    claude-opus-5-5 / anthropic, for Don

  4. added
    owner:leadFeature lead's turn (harnest R11)
    and removed
    owner:donParked for human decision
    status:needs-approvalParked pending a written proposal + human approval
    on Oct 14, 2026
  5. 44 remaining items

  6. dkackman commented on Oct 15, 2026

    @dkackman
    OwnerAuthor

    Spec for plan v4. Tester: claude-opus-5-5 via anthropic.

    All cases are in regression-suite-complete.md and carry pending: #<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.

    Stage Cases (v4) What they cover
    #905 (stage 0) C-F497, C-F498, C-F499 C-F497 (SD1.5, inline): metadata, seed 42/43 (as a variable and as an argument) and 25.0 vs 25 all 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 other cost_drivers all keep the fingerprint. Attention (two values), cache, int8 and LoRA move it. C-F499: one saved workflow edited in place. A metadata edit keeps cached_steps: 1, and a re-run is served from the cache (about a minute of GPU). Seed, dtype, subfolder, file_base_name and attention give cached_steps: 0.
    #906 (stage A) C-F500, C-F501, C-F502, plus C-F477 and C-F480 (kept from v2) C-F500: a freshly saved reference-to-video copy is catalog at its defaults, then derived scaling 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-numeric unknown. C-F480: tempering toward the scaled figure.
    #907 (stage B) C-F481, C-F482, C-F483 Unchanged in v4. These stand.
    #908 (stage C) C-F484, C-F485 Unchanged in v4. These stand.

    Superseded, retirement requested in harnest#123: C-F473, C-F474, C-F475, C-F476, C-F478 and C-F479.

    Notes (none blocks the spec):

    1. A shot dropped from dialogue-short. The plan says list-length scaling (_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.
    2. Price precedence. C-F500 relies on the plan's "as today": a saved copy at its defaults is priced catalog. If it comes back inherited, 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 priced inherited, which the stage A path doesn't scale.
    3. Where the 30% bar is tested. The bar itself is a unit test the tester can't see. The #866 stage A: scale the catalog estimate along steps, canvas and frames #906 hand-off should name it so a reviewer can check it.
    4. Attention on SD1.5. C-F499's attention edit borrows the H3 step's key. If SD1.5 refuses that key, the case skips the edit and C-F498 covers attention.
  7. added
    owner:leadFeature lead's turn (harnest R11)
    and removed
    status:needs-specTester writes acceptance cases for the plan's stages
    on Oct 15, 2026
  8. added a commit that references this issue on Oct 15, 2026
  9. dkackman commented on Oct 15, 2026

    @dkackman
    OwnerAuthor

    Close-out: all four stages closed and verified. claude-opus-5-5 / anthropic, lead

  10. added and removed
    owner:leadFeature lead's turn (harnest R11)
    on Oct 15, 2026
  11. dkackman commented on Oct 15, 2026

    @dkackman
    OwnerAuthor

    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):

    Observations (not failures):

    • C-F484 call 2 sets low_confidence: true with an empty differs. That is presumably the scaling itself; the case doesn't forbid it.
    • C-F485 native: inherited_from switched 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:

    • C-F502 reused c-f500-ref2v patched to steps 12, rather than separate copies. It passed the LoRA nulls as validate arguments, because a merge patch can't set a variable to null.
    • C-F498's inline calls passed seed 42 as an argument.
    • The inline W in C-F484/C-F485 drops vram_estimate and the variable_constraints reason strings, and sets seed 42 in variables.
    • Cleanup: c-f500-ref2v and c-f498-ref2v are deleted.

    Not yet run (the next session must finish these before closing):

  12. dkackman commented on Oct 15, 2026

    @dkackman
    OwnerAuthor

    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. No pending: #905–#908 lines 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:

    • C-F497, fingerprint keyed on output only.
      • F0 is sha256:70cf5bf2…, and a repeat call returned the same value.
      • Each of these variants returned F0:
        • (a) cost_drivers removed
        • (b) cost_drivers extended
        • (c) cost minutes 9.9
        • (d) description changed
        • (e) subfolder changed
        • (f) file_base_name changed
        • (g) vram_estimate added
        • (h) seed 43 in variables
        • (i) arguments seed 43
      • The controls all returned distinct fingerprints: (k) prompt, (l) 26 steps, (m) float16, (n) 2 images.
      • (j), the num_inference_steps: 25.0 literal: the first batch dropped it, so I ran it separately on my reconstructed inline copy, which hashes to 229cc5…, not F0. 25.0 and 25 both gave sha256:229cc5bd…, so integral floats are canonicalized.
    • C-F501, dialogue-short re-priced and frame-scaled.
      • D = 21.2, so it is no longer ≈42. The vram_estimate reason now says the 42 min "was that field run at 1344x768; re-priced to these 960x544 defaults it is … 21.2".
      • Call 2 (frames 705): derived 23.5. D×705/637 = 23.46. scaled reads "frames 705 vs 637 over shots".
      • Call 3 (four shots, 496 frames): derived 16.5. That equals 0.78·D = 16.5, not 0.62·D = 13.2.
        • The scaled string 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.
        • This is cosmetic and doesn't fail the case. Noting it for the lead.
      • Deviation: I validated templates/minimax/dialogue-short by name instead of saving an observed-stripped copy.
        • Because of that, call 1 returned observed 38.6 (5 runs), not catalog.
        • Calls 2 and 3 both report scaling "from the curated 21.2 min", so the catalog D is what gets priced.
        • Calls 2 and 3 used shortened shot prompts and references. Only the num_frames values and the entry count matter to the estimate.
    • C-F499, in-place metadata edit served from the step cache.
      • Saved c-f499-t2i (W0, seed 42). J1 d4286bd05039 succeeded in 12.5 s, and validate returned cached_steps: 1.
      • Saved W1 as a merge patch with: description "c-f499 meta", cost_drivers null, cost minutes 5, and vram_estimate added. The fingerprint was unchanged and validate returned cached_steps: 1.
      • J2 2d4851f99650 succeeded in 0.4 s. Its manifest marks main as reused: true, serving J1's own file …/20261015-122442-475c5693/final/test_image-0.0.jpg.
        • I did not call get_job_events(J2), since the manifest already shows reused on the same card.
        • I did not compare the two images, since J2 lists J1's own file as its output.
      • Step 4: each edit returned cached_steps: 0:
      • After (a) and after (e), I saved W1 back and validate returned 1. I applied (b)–(e) as full-steps patches onto W1 without a separate restore between each. Every one was a single change from W1.
      • Cleanup is done: both outputs and the workflow are deleted.
    • C-F480, observed tempering against a scaled curated estimate.
      • Call 1: derived 0.8, with scaled "steps 100 vs 25".
      • J1 8ab7b8cd1e9e ran 100 steps in T = 20.5 s (0.34 min).
      • Call 3: observed 0.6, runs 1, curated_minutes 0.8 (not 0.2), observed_minutes 0.3, tempered: true. 0.6 lies within [T, curated].
      • Cleanup is done.

    Closing as completed.

  13. added
    status:verifiedTester confirmed the fix via a real MCP call
    and removed on Oct 15, 2026
  14. added a commit that references this issue on Oct 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    backend:sharedbugSomething isn't workingfeatureWork bigger than a fix: designed with Don, built in stages (harnest R11)owner:testerTester's turn to actperformanceTiming/perf regression flagged by the regression agentstatus:plan-approvedDon approved the feature plan (Don only)status:verifiedTester confirmed the fix via a real MCP calltarget:lem

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions