The analysis
04 — The convergence honesty problem, and the build
Scope: the second honesty problem — the solver that ran 24 iterations with no convergence test — what the port changed and how the change was proved; then the build itself: the engine, the six nodes, the two workflow graphs, the director's panel, and the three verification harnesses.
Source root: as 01-premise-and-reaim.md §0.
Method: static reading of the engine, the nodes, the graphs, the panel and the three harnesses. Three commands were re-run during the writing of this file and their outputs are quoted verbatim: the differential check, the unit suite, and the panel parity check. Nothing was rendered and no ComfyUI instance was started.
1. The convergence honesty problem
1.1 What the Go original does
The port's own docstring states the defect it was written to fix:
"The Go original runs exactly 24 iterations and returns the 24th iterate's report unconditionally. There is no convergence test and no early exit, so nothing guarantees `
overall_pass— the prior internal report's claim that it 'solves a compliant camera pose' is overstated." —app/cinematographer/autoframe.py:6-9`
The README states it in the same terms:
"The internal Go solver this pack ports runs exactly 24 iterations and returns the 24th iterate unconditionally — no convergence test, no early exit, no guarantee the result passes." —
app/README.md:90-91
The spec's non-goal list makes the correction mandatory:
"Claiming 38 rules, or claiming the solver converges when it does not." —
01-SPEC.md:311
1.2 Why it is a problem worth fixing
The prior teardown sold the solver as a feature:
"
AutoFrameCamerasolves a compliant camera pose in 24 geometric iterations at no AI cost." —client-reports/higgsfield-cinema-studio-4.0/analysis/00-SYNTHESIS.md:179-180
"Solves a compliant camera pose" is not what the code does. It runs a fixed number of iterations and returns whatever it has. The word "compliant" is doing work the code does not support.
This is the same class of defect as the "38 rules" claim (03-differentiator-and-rule-count.md §4): a number that describes the intent presented as a number that describes the behaviour.
1.3 What the port changed
Four changes, all in auto_frame_camera() (autoframe.py:95-193):
| # | Change | Code |
|---|---|---|
| 1 | Early exit as soon as the frame passes | autoframe.py:164-166 |
| 2 | converged flag, set from the report's own verdict | autoframe.py:190 |
| 3 | iterations — the count actually used, not the budget | autoframe.py:141, :191 |
| 4 | residual — the weighted rules still failing when it does not converge | autoframe.py:184-186 |
The early exit is three lines:
# app/cinematographer/autoframe.py:164-166
# --- the fix: stop as soon as the frame is actually compliant ---
if early_exit and rep.overall_pass:
break
The converged flag is not a separate computation. It is the report's verdict:
# app/cinematographer/autoframe.py:187-193
return AutoFrameResult(
camera=cam,
report=rep,
converged=rep.overall_pass,
iterations=iterations,
residual=residual,
)
That is the design decision that makes the flag trustworthy: converged cannot disagree with the report, because it is the report. There is no second code path that could drift.
The residual is filtered to the rules that actually gate the verdict:
# app/cinematographer/autoframe.py:184-186
residual = [
f"{s.rule}={s.value:g}" for s in rep.failures() if s.weight >= 0.8
]
1.4 The escape hatch that makes the proof possible
The port keeps a parameter whose only purpose is verification:
# app/cinematographer/autoframe.py:107-109
Set ``early_exit=False`` to reproduce the original Go behaviour exactly
(always run the full iteration budget). That is used by the differential
test to prove this port is faithful before the fix is applied.
This is the single most important design decision in the verification story. Without it, the port could not be differentially compared to Go at all — the early exit would make every autoframe fixture diverge by construction.
The differential check uses it exactly as documented:
# verification/differential_check.py:141-142
# The Go solver always runs its full budget. Run ours with early_exit=False
# to prove the port is faithful, then separately confirm the fix engages.
# verification/differential_check.py:158-160
faithful = auto_frame_camera(
to_subject(fx["subject"]), shot_type, intent, early_exit=False
)
And then, in the same loop, separately asserts the fix:
# verification/differential_check.py:175-181
# The fix: with early exit on, we must never do more work than Go.
fixed = auto_frame_camera(to_subject(fx["subject"]), shot_type, intent)
checks += 1
if fixed.iterations > 24:
failures.append(f"{label}: early-exit ran {fixed.iterations} > 24 iterations")
if fixed.converged != fixed.report.overall_pass:
failures.append(f"{label}: converged flag disagrees with the report")
So the 289-check run proves two different things in one pass: fidelity (with the fix disabled) and the fix itself (with it enabled). That is a genuinely elegant structure and it is worth naming.
1.5 How the change was proved
Four independent lines of evidence.
1. The unit tests. Four tests pin the behaviour (app/tests/test_autoframe.py):
| Test | What it pins | Line |
|---|---|---|
test_solver_exits_early_rather_than_always_running_24_iterations | iterations < MAX_ITERATIONS on a solvable frame — "the headline behavioural difference" | :31-39 |
test_converged_flag_agrees_with_the_report | converged == report.overall_pass across 9 combinations (3 shot types × 3 intents) | :48-53 |
test_unsolvable_intent_is_reported_honestly_not_hidden | A degenerate subject (zero height) returns converged is False with a non-empty residual | :64-83 |
test_solved_pose_reproduces_a_passing_report_when_re_evaluated | The returned pose actually passes when fed back through the evaluator | :125 |
The early-exit test's docstring states the regression condition:
"This is the headline behavioural difference: if this ever equals
MAX_ITERATIONSon a solvable frame, the early exit has regressed." —app/tests/test_autoframe.py:34-35
2. The differential check's early-exit assertion. 18 checks, one per autoframe fixture (differential_check.py:177-181).
3. The adversarial review's 1800-call invariant sweep. This is the strongest evidence, because it was produced by someone trying to falsify the claim:
"C — Solver convergence honesty — CONFIRMED. 1800 solver calls:
convergednever disagrees withreport.overall_pass. No counterexample found." —verification/ADVERSARIAL-REVIEW.md:35
And the falsification log:
| 8 | 1800-call solver invariant sweep | FAILED to falsify — zero anomalies | —
verification/ADVERSARIAL-REVIEW.md:641
| 9 | Hunt for
converged=Truewith a failing weighted rule | FAILED to falsify — zero cases | —verification/ADVERSARIAL-REVIEW.md:642
4. The verification README's own statement.
"On the reference fixture the solver converges in 3 iterations; the Go original would run all 24 and return the 24th unconditionally." —
verification/README.md:88-89
1.6 The one thing the review found that the entry had not disclosed
The review's attempt #10 succeeded, and the finding is a genuine gap in the entry's own framing:
| 10 | Hunt for
converged=Truewith a failing non-weighted rule | SUCCEEDED — 24 cases; documented as intended | —verification/ADVERSARIAL-REVIEW.md:643
The panel README documents the general case (app/web/README.md:201-202), and the review accepts it as intended. But the review also records the sharper version as defect #17:
| 17 | MINOR |
converged=Truecan coexist with a badly failing 0.7-weight rule and off-frame feet; the panel's success text does not hint at this. |app/web/app.js:676-678;framing.py:104| —verification/ADVERSARIAL-REVIEW.md:626
"Off-frame feet" is the detail worth pausing on. in_frame checks the subject's face, not their feet (framing.py:102-103), which is correct for a close-up and is documented in the code. But it means a frame can pass with the subject's feet outside the frame and a 0.7-weight rule badly failing, and the panel will say CONVERGED.
The convergence flag is honest about what it measures. It is not a claim that the frame is good. The entry's success text does not make that distinction, and the review flagged it.
1.7 The second-order finding: the Go failure mode is undemonstrated
The entry sells the fix by contrast with the Go original's failure mode. The review checked whether that failure mode is actually demonstrated by any captured fixture:
| 11 | Hunt for a Go fixture whose 24th iterate fails | SUCCEEDED — none exist; the failure mode is undemonstrated | —
verification/ADVERSARIAL-REVIEW.md:644
| H10 | "The Go original … returns the 24th iterate unconditionally" |
app/README.md:90-91| True of the Go code, but no fixture demonstrates it failing — all 18 Go autoframe fixtures end on a passing frame. Presented beside measured claims without that distinction. | —verification/ADVERSARIAL-REVIEW.md:581
| 16 | MINOR | The Go solver's failure mode is sold but not demonstrated by any captured fixture. |
app/README.md:90-91;go-reference-dump.json| —verification/ADVERSARIAL-REVIEW.md:625
This is the most interesting finding in the whole review, and it is a finding about the entry's rhetoric rather than its code. The claim is true of the Go source. But the entry presents it beside measured claims, and the measured evidence does not contain a single instance of the failure. A judge who asks "show me a Go solve that returned a failing frame" cannot be answered from the captured dump.
The honest fix is one clause: "true of the Go source; no captured fixture exercises it." It was not written.
2. The build
2.1 The frozen spec as the contract
The spec was frozen before the build started (01-SPEC.md:3), and it carries the node API (01-SPEC.md:6), the layout (01-SPEC.md:8), and the acceptance criteria (01-SPEC.md:9). The build was split into parallel lanes against it.
The frozen-spec discipline shows in the artefacts. The node module resolves engine entry points by name with a fallback, so both the spec's CamelCase names and the Pythonic snake_case names work:
"Engine entry points are resolved by name with a fallback, so both the spec's CamelCase names (`
EvaluateFraming) and the Pythonic snake_case names the engine actually ships (evaluate_framing) work." —app/cinematographer/nodes.py:18-20`
That is a small piece of defensive engineering that exists because the spec and the implementation were written in parallel.
2.2 The engine — six modules, pure stdlib
| Module | Lines | Role |
|---|---|---|
app/cinematographer/geometry.py | 388 | Vectors, CamPose, Subject, ShotIntent, the projector, anchors, shot bands, weights |
app/cinematographer/framing.py | 240 | The 9 per-shot rules, evaluate_framing(), summary() |
app/cinematographer/scene.py | 250 | The 3 scene rules, PlacedEntity, SceneGraph, scene_framing_extras() |
app/cinematographer/continuity.py | 155 | The 2 cross-cut rules, evaluate_continuity() |
app/cinematographer/autoframe.py | 206 | The solver, mood_to_intent(), autoframe_summary() |
app/cinematographer/reliability.py | 103 | The reliability zone |
| Total engine | 1,342 |
The engine's hard constraint is stated in three places:
"Everything here is pure stdlib and has no ComfyUI dependency — that is a hard requirement (spec §8), because it is what makes the rules testable and reusable." —
app/cinematographer/geometry.py:6-7
"The engine imports with zero ComfyUI dependency — that is a deliberate design constraint, so the geometry is testable and reusable outside ComfyUI." —
app/README.md:50-51
"The
app/tree has no Magieva imports and its tests run with zero Magieva dependency — that was a deliberate design constraint, and it pays off exactly here." —03-SUBMISSION.md:72-73
Three constraints, one decision: the engine must be importable with nothing installed. That is what makes it testable without ComfyUI, publishable without Magieva, and reusable by the panel.
2.3 The six nodes
app/cinematographer/nodes.py (698 lines) is the only module permitted to touch ComfyUI, and even there the dependency is optional:
"This is the only module in the package permitted to touch ComfyUI, and even here the dependency is optional: the probe below is wrapped, so this module -- and every node class in it -- imports cleanly on a machine with no ComfyUI." —
app/cinematographer/nodes.py:7-11
The probe:
# app/cinematographer/nodes.py:47-53
try: # pragma: no cover - only true inside a ComfyUI install
import comfy.utils as _comfy_utils # noqa: F401
COMFYUI_AVAILABLE = True
except Exception: # noqa: BLE001 - a half-installed ComfyUI can raise anything
_comfy_utils = None
COMFYUI_AVAILABLE = False
Note the except Exception with an explicit justification. A half-installed ComfyUI can raise anything, so catching ImportError alone would be wrong.
The six nodes (nodes.py:682-689):
| Node | What it does | README line |
|---|---|---|
CinematographerEvaluate | Scores a camera + subject against the 9 per-shot rules. Returns a report, an overall pass, and a one-line summary | app/README.md:59 |
CinematographerAutoFrame | Solves a camera pose for a shot type and intent. Reports whether it actually converged | app/README.md:60 |
ChorosContinuity | Checks a shot list for 180°-axis breaks and sub-30° jump cuts | app/README.md:61 |
SceneFramingExtras | The multi-entity rules: size = story importance, dirty-frame foreground, eyeline | app/README.md:62 |
CameraReliabilityZone | Grades a requested angle against the view-steering model's trained envelope | app/README.md:63 |
CinematographerCorrection | Turns a report into a plain list of correction levers | app/README.md:64 |
All six are registered in NODE_CLASS_MAPPINGS (nodes.py:682-689) and NODE_DISPLAY_NAME_MAPPINGS (nodes.py:691-698), under the category Cinematographer.
The I/O contract is deliberately dumb: "All JSON in, all JSON out — the nodes compose with anything." (app/README.md:66)
2.4 The engine loader
The engine modules are imported lazily and defensively (nodes.py:60-92), with a subtlety worth recording:
# app/cinematographer/nodes.py:70-79
except ImportError as exc:
# Only fall back when the *target file* is missing. An ImportError raised
# from inside the engine module (a real bug, or a missing third-party
# dependency) must propagate rather than be silently retried.
if exc.name not in (name, package + "." + name):
raise
That comment is the difference between a robust loader and a bug-hiding one. A naive except ImportError: retry would swallow a genuine ImportError from inside an engine module and report a misleading "module missing" error.
2.5 The two workflow graphs
The spec requires two graphs (01-SPEC.md:8). Both are ComfyUI API-format JSON — nodes keyed by id, each with class_type and inputs.
| Graph | Stack | Nodes | Links |
|---|---|---|---|
app/workflows/reshoot_ltx_crossview.json | LTX 2.3 + CrossView-Warp IC-LoRA + MoGe-2 | — | — |
app/workflows/reshoot_wan22_fun_control.json | Wan 2.2 Fun-Control + CrossView-Warp + MoGe-2 | — | — |
Both carry a _README node documenting provenance, deliberate deltas, required node packs, required weights, licence, and measured status. The LTX graph's _README is the model for the convention.
The provenance discipline is the most reusable thing in the package. The LTX graph's _README names the exact upstream file it was derived from, with the read date:
"Derived from the CrossView-Warp v2 example workflow shipped by the node pack's own author:
cseti007/ComfyUI-CrossViewWarp example_workflows/ltx2.3-ic-lora-crossview-warp-v2.json(Apache-2.0; 119 UI-format nodes; read 2026-10-29)" —app/workflows/reshoot_ltx_crossview.json_README
And it enumerates seven deliberate deltas from that example, each with a reason. The list is worth reproducing in summary because it shows what "documented divergence" looks like:
| # | Delta | Reason given |
|---|---|---|
| 1 | Two-stage latent-upscale pass removed; single 1376×768 pass | Fewer moving parts, one fewer weight, and the framing arithmetic is resolution-independent |
| 2 | Comparison plumbing dropped (SetNode/GetNode/AddLabel/ImageConcatMulti/ReverseImageBatch) | It exists to show a side-by-side in the author's demo, not to make a clip |
| 3 | VHS_LoadVideo/VHS_VideoCombine → core LoadVideo/GetVideoComponents/CreateVideo/SaveVideo | One fewer custom node pack in the critical path |
| 4 | Camera keyframed inside the green reliability zone (|azimuth| ≤ 40°, elevation 0…+15°) instead of the example's −24.4 → +50.4° swing | The +50.4° endpoint is outside the ±45° range the LoRA was checked on |
| 5 | keep_source_aim ON | The node's own README says it scores better and it is what a re-shoot wants |
| 6 | preview_size 384 → 512; frame_index 49 → 1 | — |
| 7 | filename_prefix → cinematographer/* | Namespace the output |
Delta 4 is the interesting one: the graph was authored to stay inside the reliability envelope the entry's own CameraReliabilityZone node grades. The two halves of the entry are consistent with each other.
2.6 The structural validator
app/workflows/validate_graphs.py (236 lines) is the only check the graphs get, and it is explicit about its boundary:
"CHECKED — the file parses as JSON; `
_READMEis a list of strings; every node hasclass_typeandinputs; everyclass_type` is in the verified output-slot table below; every link resolves to an existing node id and an output slot that node actually declares; the graph is acyclic; every node is reachable from a SaveVideo.NOT CHECKED — that ComfyUI accepts the prompt, that the node packs and weights are installed, that the graph runs, or that it produces a clip. None of the graphs have ever been executed. Do not read a pass here as 'the workflow works'." —
app/workflows/validate_graphs.py:11-20
The load-bearing part is the output-slot table (validate_graphs.py:36-80), which maps 33 class_types to their output-slot counts, each annotated with the upstream source it was read from. The claim is explicit:
"The output-slot table is the load-bearing part. Each count was read from the node source named in the graph's own `
_READMEPROVENANCE block, not guessed." —app/workflows/validate_graphs.py:22-24`
The adversarial review falsified that claim. One entry is wrong — see 05-adversarial-review.md §5.
2.7 The director's panel
app/web/ is a single-page panel with no build step, no framework, and no backend (app/web/README.md:8). It runs the same geometry in the browser.
| File | Lines | Role |
|---|---|---|
app/web/cinematographer.js | 843 | A line-by-line JS mirror of the Python engine |
app/web/app.js | 729 | The panel UI |
app/web/panel_state.js | 121 | State |
app/web/parity_check.py | 377 | The three-way parity harness |
app/web/tools/gen_fixtures.py | 466 | Fixture generator |
app/web/parity_fixtures.json | 2,554 | 28 frozen fixtures |
The panel's central claim is that its numbers are real:
"A panel whose numbers disagree with the engine is worse than no panel, so the mirror is proven, not asserted." —
app/web/README.md:29-30
The panel README also carries an explicit "What is real, and what is mocked" section (app/web/README.md:89-95), which the adversarial review singled out as exemplary (verification/ADVERSARIAL-REVIEW.md:40).
2.8 The porting notes — a small gem
The panel README documents four Python↔JS semantic mismatches that a naive port would get wrong (app/web/README.md:208-215):
| Note | The mismatch |
|---|---|
pyRound | Python's round() is ties-to-even; JS Math.round is ties-away-from-zero |
pyFixed | Python's format(x, '.2f') renders negative zero as -0.00; JS toFixed drops the sign |
pyG | Python's '%g' formatting, used in the Dutch target string |
| Unicode targets | Written as \u escapes so transport encoding cannot corrupt them; they are the Go source's strings verbatim (≥, –, ±, ≈, °) |
The negative-zero note is the one that matters: "The frame preview's NDC readouts depend on this." (app/web/README.md:212) A port that missed it would show 0.00 where the engine shows -0.00, and the parity check would fail on a formatting difference that looks like a geometry difference.
3. The differential verification — 289 checks
3.1 How the Go side was produced
The original implementation lives in the Magieva monorepo at internal/contentstudio/cinematography*.go (verification/README.md:19-20). A temporary Go harness was compiled inside that module, ran the original Go functions over a fixed fixture set, and dumped the results as JSON (verification/README.md:21-23).
# verification/README.md:26-32
cp verification/go-harness/zz-cine-dump.go cmd/zz-cine-dump/main.go
GOWORK=off go run ./cmd/zz-cine-dump > /tmp/go-cine-dump.json
rm -rf cmd/zz-cine-dump
python verification/differential_check.py /tmp/go-cine-dump.json
The captured output is preserved at verification/go-reference-dump.json, so the comparison can be re-run without the Magieva checkout (verification/README.md:35-36).
3.2 What is compared
| Surface | Fixtures | Compared |
|---|---|---|
EvaluateFraming | 9 | rule list, order, pass, value, weight, target, correction, overall_pass (verification/README.md:48) |
AutoFrameCamera | 18 (3 shot types × 6 intents) | camera eye, target, fov, roll, and the full report (verification/README.md:49) |
EvaluateContinuity | 2 sequences | rule, shot pair, pass (verification/README.md:50) |
SceneFramingExtras | 2 scenes | rule, pass, value (verification/README.md:51) |
Tolerance is 1e-6 on every numeric field (verification/README.md:53; differential_check.py:39).
3.3 The decomposition of 289 — never stated by the entry
The counter is incremented at five places in differential_check.py. Decomposing it:
| Source | Count | Line |
|---|---|---|
| Framing rule-level field comparisons (9 fixtures × 9 rules) | 81 | :66 |
| Autoframe rule-level field comparisons (18 fixtures × 9 rules) | 162 | :66 (via :173) |
| Autoframe camera-pose checks (eye, target, fov, roll) | 18 | :163 |
| Autoframe early-exit / convergence checks | 18 | :177 |
| Continuity issue checks (4 + 2) | 6 | :204 |
| Scene-extras checks (2 + 2) | 4 | :234 |
| Total | 289 |
The units are not homogeneous. 243 of the 289 are per-rule field comparisons; 46 are whole-object or invariant checks. The entry's phrase "289 differential checks" reads as 289 independent comparisons, and it is not.
This is the adversarial review's attempt #2, and it succeeded:
| 2 | Decompose the 289 counter | SUCCEEDED — heterogeneous units, not field comparisons; 2 slots near-vacuous | —
verification/ADVERSARIAL-REVIEW.md:635
3.4 The deliberate skips
Two fields are skipped on purpose, and the code says so.
size_importance's value — because the port uses role-derived half-extents rather than Magieva's dimension resolver:
# verification/differential_check.py:241-244
# size_importance is expected to differ: the port uses role-derived
# half-extents, not Magieva's full dimension resolver. See scene.py.
if r == "size_importance":
continue
The verification README discloses this (verification/README.md:56-62).
Two more fields are skipped silently — the scene correction and the continuity detail. The review found that these do diverge:
| H2 | "reproduces the original Go cinematography implementation value-for-value" |
verification/README.md:12-13| False for the skipped fields, which do diverge: scenecorrection(em dash vs hyphen), continuitydetail(°/≥/—vsdeg/>=/-). | —verification/ADVERSARIAL-REVIEW.md:573
The divergences are cosmetic (em dash vs hyphen, ° vs deg), but the word "value-for-value" is false as written. See 05-adversarial-review.md §4.
3.5 The result, re-run
$ cd higgsfield-comfyui-os
$ PYTHONDONTWRITEBYTECODE=1 python3 verification/differential_check.py verification/go-reference-dump.json
differential checks run: 289
ALL FIXTURES AGREE with the original Go implementation.
exit=0
Reproduced during the writing of this file. The number is exact.
3.6 The hole in the chain
The review is blunt about the largest weakness, and it is inherent to the setup:
"No Magieva Go checkout. I could not re-run
go-harness/zz-cine-dump.go. I verified the Python side against the frozengo-reference-dump.jsononly. The dump's own provenance is unverifiable from here — if it were fabricated or stale, none of my claim-A work would detect it. This is the single largest hole in the verification chain, and it is inherent to the setup." —verification/ADVERSARIAL-REVIEW.md:673-676
The 289-check claim reduces to: the Python port agrees with a JSON file. Whether that JSON file was produced by the Go code it claims to have been produced by is not checkable from inside this package. The harness source is preserved (verification/go-harness/zz-cine-dump.go), so the provenance is auditable — but it was not audited.
This is the honest framing of the entry's strongest claim, and it is developed in 06-unverified-and-method.md §3.
4. The panel parity check — 28 fixtures
4.1 The three-way structure
The panel README states the design:
"It compares three ways: (1) the JS mirror against the frozen fixtures, (2) the Python engine against the same fixtures, (3) the Python engine against the JS mirror, live, field by field." —
app/web/README.md:66-70
Leg [3] is what makes the check non-circular. The review tested that explicitly:
| 16 | Test whether parity is circular | FAILED to falsify — leg [3] is a live two-engine comparison | —
verification/ADVERSARIAL-REVIEW.md:649
And the verdict:
"D — Panel parity — CONFIRMED — and not circular. Three-way check with a live Python↔JS leg; JS mutations are caught. Same boundary-blindness as A." —
verification/ADVERSARIAL-REVIEW.md:36
4.2 The staleness guard
The fixture file records a sha256 over the six engine modules, and the checker recomputes it:
"It exits non-zero on any mismatch, on a structural problem (fewer than five framing fixtures, or a fixture without all nine rules), and on stale fixtures — the fixture file records a sha256 over the six engine modules, and the checker recomputes it, so an engine edit cannot silently invalidate the proof." —
app/web/README.md:72-75
That is the right guard. Without it, an engine edit would leave the fixtures proving something about a version of the engine that no longer exists.
4.3 The result, re-run
$ PYTHONDONTWRITEBYTECODE=1 python3 app/web/parity_check.py
Cinematographer parity check
fixtures : app/web/parity_fixtures.json
generated : 2026-10-30T00:06:38+00:00 (engine rev unknown)
engine hash : 24621deb23c66c8f… (matches live engine)
JS engine : app/web/cinematographer.js (node v24.18.0)
Python engine : app/cinematographer/ (importable)
[1/3] JS vs frozen fixtures
framing 12/12 OK
continuity 4/4 OK
reliability 7/7 OK
autoframe 5/5 OK
[2/3] Python engine vs frozen fixtures
framing 12/12 OK
continuity 4/4 OK
reliability 7/7 OK
autoframe 5/5 OK
[3/3] Python engine vs JS (live)
framing 12/12 OK
continuity 4/4 OK
reliability 7/7 OK
autoframe 5/5 OK
RESULT: PASS — 28 fixtures, 0 mismatches
exit=0
Reproduced during the writing of this file.
4.4 The chain back to Go
Two of the 28 fixtures reproduce poses from the original Go test:
"Two of them (
go-test-centered-closeup,go-test-offset-closeup) reproduce the poses in the original Go testcinematography_test.go, so the chain traces back past the Python port to the Go source." —app/web/README.md:83-85
So the verification chain is: Go test → fixture → Python engine → JS mirror → panel. Four links, three of them mechanically checked.
4.5 The same boundary blindness
The review found that the parity check shares the differential check's weakness:
"Same boundary-blindness as A." —
verification/ADVERSARIAL-REVIEW.md:36
| 15 | 10 JS mutations vs the parity check | PARTIALLY SUCCEEDED — 6 caught, 4 missed (same boundary blindness) | —
verification/ADVERSARIAL-REVIEW.md:648
Both headline numbers measure value fidelity and neither measures threshold fidelity. That is the systemic finding, developed in 05-adversarial-review.md §6.
5. The unit suite — 69 tests
5.1 The distribution
| File | Tests |
|---|---|
app/tests/test_autoframe.py | 22 |
app/tests/test_geometry.py | 18 |
app/tests/test_framing.py | 15 |
app/tests/test_continuity.py | 14 |
| Total | 69 |
5.2 The result, re-run
$ cd app && PYTHONDONTWRITEBYTECODE=1 /tmp/cine-venv/bin/python -m pytest tests/ -q -p no:cacheprovider
..................................................................... [100%]
69 passed in 0.19s
Reproduced during the writing of this file.
5.3 The hand-computation claim
The verification README claims the expected values are derived from first principles rather than recorded:
"The tests derive their expectations from first principles rather than from a recorded run. For the reference camera (35 mm focal, 36 mm sensor, 16:9):
vfov = 2*atan((36/(16/9)) / (2*35)) f = (2*focal)/(sensor/aspect) = 70/20.25 = 3.45679012345679so
ndcY(top) = 0.048 * f = 0.1659259andheadroom = 0.8340750. The suite asserts those numbers, not a snapshot." —verification/README.md:104-113
The review re-derived three of them and confirmed the arithmetic:
| 13 | Re-derive 3 test expected values from first principles | FAILED to falsify — arithmetic is correct | —
verification/ADVERSARIAL-REVIEW.md:646
But it found one constant wrong:
| 7 | MINOR | Hand-computed constant
F = 3.4567701is wrong (correct3.4567901); contradictsverification/README.md:109. |app/tests/test_framing.py:13| —verification/ADVERSARIAL-REVIEW.md:616
And it found the coverage claim misleading:
| H5 | "69 unit tests, every expected value hand-computed" |
verification/README.md:98| True of the values asserted; misleading about coverage. 11/24 injected defects survive, andtest_framing.py:13'sFconstant is itself miscomputed. | —verification/ADVERSARIAL-REVIEW.md:576
The headroom = 0.8340750 figure is the one the demo script puts on screen as 0.834 (02-DEMO-SCRIPT.md:59). So the demo's headline number is traceable to a hand computation, and the hand computation is right — but a different hand-computed constant in the same suite is wrong in the fifth decimal.
6. The publishability constraint
The submission checklist records that the publication decision was still open when it was written (03-SUBMISSION.md:49-51), and that the constraint is that the Magieva monorepo cannot be published (03-SUBMISSION.md:54-55).
The recommended split (03-SUBMISSION.md:70-73):
| Item | Origin | Publishable? |
|---|---|---|
app/cinematographer/*.py | new port, written for this entry | ✅ yes |
app/web/* | new | ✅ yes |
app/tests/* | new | ✅ yes |
app/workflows/*.json | graph structure modelled on Magieva's internal graphs | ⚠️ review |
verification/go-harness/zz-cine-dump.go | new harness; calls Magieva internal packages | ⚠️ review |
verification/go-reference-dump.json | output of Magieva internal code | ⛔ likely not |
00-BREAKDOWN.md, 01-SPEC.md, analysis/* | internal competitive analysis | ⛔ no |
The recommended publish set is app/ only, as a standalone Apache-2.0 repo (03-SUBMISSION.md:70-71).
6.1 The pre-publication grep
The checklist specifies a mechanical check:
# 03-SUBMISSION.md:77-80
cd app
grep -rn "magieva\|Magieva" . --include=*.py --include=*.js --include=*.json --include=*.md
And it flags the expected hits in advance:
"Any hit needs a decision. (The README and
scene.pycurrently reference Magieva's dimension resolver as the source of a documented simplification — that is a factual attribution, not a code dependency, but confirm you are comfortable with it.)" —03-SUBMISSION.md:82-84
The two expected hits are app/README.md:130-131 and app/cinematographer/scene.py:15-20. Both are attributions, not dependencies. The distinction is the right one, and the checklist states it before the grep is run rather than after.
6.2 The publishability constraint is why the engine has no imports
This is the payoff of the design decision in §2.2. Because the engine is pure stdlib and has no Magieva imports, the publish set is a directory move rather than a refactor. The constraint was adopted at design time (03-SUBMISSION.md:72-73) and it paid off at publication time.
7. What the build did not do
| Not done | Consequence |
|---|---|
| Run ComfyUI | The nodes have never been loaded by ComfyUI. The install path was broken (see 05 §3). |
| Execute either workflow graph | Both _READMEs say NOT VERIFIED |
| Produce a render | No LTX or Wan output exists |
| Measure VRAM or timing | "MEASURED: nothing. No timing, no VRAM figure, no output." |
Run --disable-partner-nodes | The open-weights-only property is argued from JSON, not proven by a flag |
| Re-run the Go harness | The 289-check claim rests on a frozen JSON file |
| Add boundary fixtures | The 12-constant hole is open |
Differentially verify eyeline | No fixture has two characters |
| Read any third-party licence | The licence argument is quoted from the recon |
| Record the demo video | "⛔ not recorded" (03-SUBMISSION.md:39) |
| Post the social post | "⛔ not posted" (03-SUBMISSION.md:40) |
| Publish the repo | "⛔ blocked" (03-SUBMISSION.md:38) |
| Obtain a comfy.org login | "We do not have a comfy.org login." (03-SUBMISSION.md:10) |
The last four are submission blockers, not build defects, and they are stated as blockers in the checklist itself (03-SUBMISSION.md:8-30).
8. Decision register — the build phase
| # | Decision | Evidence |
|---|---|---|
| B1 | Freeze the spec before building | 01-SPEC.md:3 |
| B2 | Keep the engine pure stdlib with no ComfyUI import | geometry.py:6-7; app/README.md:50-51 |
| B3 | Keep app/ free of Magieva imports | 03-SUBMISSION.md:72-73 |
| B4 | Make the ComfyUI import optional and wrapped | nodes.py:47-53 |
| B5 | Resolve engine entry points by name with a fallback | nodes.py:18-20 |
| B6 | Propagate real ImportErrors rather than retrying | nodes.py:70-79 |
| B7 | Add early_exit=False as a verification escape hatch | autoframe.py:107-109 |
| B8 | Set converged from the report, not a separate computation | autoframe.py:190 |
| B9 | Filter the residual to gating rules | autoframe.py:184-186 |
| B10 | Author the LTX graph inside the reliability envelope | LTX graph _README, delta 4 |
| B11 | Document every deliberate delta from upstream | LTX graph _README |
| B12 | Mark both graphs NOT VERIFIED in their own _README | Both graphs' _README |
| B13 | Build a three-way parity check with a live leg | app/web/README.md:66-70 |
| B14 | Hash the engine into the fixture file | app/web/README.md:72-75 |
| B15 | Document the Python↔JS semantic mismatches | app/web/README.md:208-215 |
| B16 | Disclose the size_importance simplification in code and in the check | scene.py:15-20; differential_check.py:241-244 |
9. Evidence index for this file
| Claim | Primary source |
|---|---|
| The Go solver's 24 unconditional iterations | autoframe.py:6-9; app/README.md:90-91 |
| The prior "solves a compliant pose" claim | client-reports/higgsfield-cinema-studio-4.0/analysis/00-SYNTHESIS.md:179-180 |
| The four changes | autoframe.py:164-166, :184-186, :190-191 |
The early_exit=False escape hatch | autoframe.py:107-109; differential_check.py:141-142, :158-160 |
| The early-exit assertion | differential_check.py:175-181 |
| The four convergence tests | app/tests/test_autoframe.py:31-39, :48-53, :64-83, :125 |
| The 1800-call sweep | verification/ADVERSARIAL-REVIEW.md:35, :641-642 |
| The 3-iteration convergence | verification/README.md:88-89 |
converged=True with a failing 0.7-weight rule | verification/ADVERSARIAL-REVIEW.md:626, :643 |
in_frame checks the face | framing.py:102-103 |
| The Go failure mode is undemonstrated | verification/ADVERSARIAL-REVIEW.md:581, :625, :644 |
| The engine module sizes | wc -l over app/cinematographer/*.py |
| The pure-stdlib constraint | geometry.py:6-7; app/README.md:50-51; 03-SUBMISSION.md:72-73 |
| The optional ComfyUI probe | nodes.py:47-53 |
| The six nodes | nodes.py:682-698; app/README.md:57-66 |
| The engine loader's ImportError discipline | nodes.py:70-79 |
| The LTX graph's provenance | LTX graph _README |
| The seven deliberate deltas | LTX graph _README |
| The validator's boundary | validate_graphs.py:11-20 |
| The output-slot table | validate_graphs.py:36-80 |
| The panel's file sizes | wc -l over app/web/* |
| The three-way parity structure | app/web/README.md:66-70 |
| The staleness guard | app/web/README.md:72-75 |
| The porting notes | app/web/README.md:208-215 |
| The 289 decomposition | Derived from differential_check.py:66, :163, :177, :204, :234 |
| The deliberate skip | differential_check.py:241-244 |
| The silent skips | verification/ADVERSARIAL-REVIEW.md:573 |
| The dump's provenance hole | verification/ADVERSARIAL-REVIEW.md:673-676 |
| The 69 tests | app/tests/*.py; re-run output in §5.2 |
| The hand-computation claim | verification/README.md:104-113 |
The wrong F constant | verification/ADVERSARIAL-REVIEW.md:616 |
| The publishability split | 03-SUBMISSION.md:49-84 |
| The submission blockers | 03-SUBMISSION.md:8-30, :38-40 |