Skip to content

Chantier: post-audit reliability hardening #102

Closed
8 of 8 issues completed
Closed

Description

@LeadcodeDev

Context

A full audit of the codebase (read-only, commit 452596e) targeted three areas: JSON scenario
generation quality, component overflow handling, and the engine's ability to produce the
"dark premium" marketing style of the reference videos.

It found 2 critical, 8 high and 9 medium issues. The headline conclusion: the render
engine is markedly stronger than its authoring surface. Real 3D (M44 with perspective divide),
keyframed camera, multi-plane parallax, both blur kinds and the spring/cubic-bezier easing solver
all work correctly. What is broken is the chain of trust around them — the validator green-lights
scenarios that render invisible text, column-less grids, and content spilling out of its card.

Every finding below was reproduced against a release build, not inferred from reading code.

Decisions taken at kickoff

Question Decision Rationale
grid component Wire tracks through to taffy taffy supports CSS Grid natively and the schema already models tracks; a KPI row is the single most common marketing layout
Unknown top-level attributes Error by default, and --strict-attrs made available to render This is the class of failure that produces a silently wrong video; all 7 repo examples must still validate
Delivery cadence Wave 1 (critical + high), review, then wave 2 (medium) Keeps each review sitting reviewable
Base branch origin/main All code findings exist there; the in-flight 1600-template branch lands separately

Frozen contracts

Read-only for every workstream. Changing one requires updating this issue first.

  1. Colour parsing entry point — crates/rustmotion-core/src/engine/renderer/colors.rs exposes:
    pub fn parse_css_color(input: &str) -> Option<(u8, u8, u8, u8)>
    parse_hex_color stays as a thin infallible wrapper for existing call sites.
  2. The geometry validator never mutates schema types. cli/commands/geometry.rs reads the box
    tree and CSS; any schema change belongs to the schema workstream.
  3. File ownership is disjoint. No file is written by two workstreams. Reads may overlap freely.
  4. css/style.rs and schema/style.rs are different files owned by different workstreams.

Workstreams

Wave 1 — critical + high

  • L1 · Geometry validator — cli/commands/{geometry,validate}.rs — C1, H3, H4, H6, H7 + regression tests
  • L2 · Value parsing — engine/renderer/colors.rs, engine/paint_pass.rs, css/units.rs — C2
  • L3 · Grid layout — css/taffy_bridge.rs, css/style.rs — H1
  • L4 · Schema surface — core/schema/**, cli/commands/{schema,validation}.rs — H2, H5 (fonts), dead LayerStyle
  • L6 · Documentation — CLAUDE.md, .claude/skills/rustmotion/** — H8, M7

Wave 2 — medium (after review of wave 1)

  • L5 · Dead features & components — components/{box_builder,intrinsic,text}.rs, engine/animator.rs — M1, M2, M3
  • L4b · Diagnostics hardening — validate_attrs.rs, cli/lib.rs, render.rs, scenario.rs — M4, M5, M6

Findings index

ID Severity Summary Workstream
C1 Critical --fix writes style.wrap, a field CssStyle rejects — the component is dropped from the render and validation then reports clean L1
C2 Critical #fff, #FFF, white, rgb(...) all fall back to SColor::BLACK silently; unparseable lengths fall back to 0px L2
H1 High grid-template-columns never reaches taffy, yet validate_schema.rs:217 makes it mandatory L3
H2 High Exported JSON Schema types children as items: true — zero component coverage, no constrained generation possible L4
H3 High --fix indexes the post-filter child vector against raw JSON, patching the wrong node when a sibling fails to deserialize L1
H4 High _parent_clips is computed and never read: parent overflow is never detected, clipped subtrees are false positives L1
H5 High Geometry ignores static transform and scene.camera; custom fonts are never loaded before measuring L1 + L4
H6 High --strict-anim samples 7 fixed points, discards t=0 via an opacity guard, and forks the renderer's effect resolution L1
H7 High Deliberate frame-bleeding typography — the reference style's signature — is rejected as a viewport overflow L1
H8 High The authoring guide teaches a dead style model (LayerStyle); its canonical Example 1 produces a dropped component L4 + L6

Verification (applies to every workstream)

rtk cargo build --release
rtk cargo test --workspace
for f in examples/*.json; do ./target/release/rustmotion validate -f "$f"; done

All 7 example scenarios must still validate after every workstream.

Activity

  1. LeadcodeDev commented on Aug 1, 2026

    @LeadcodeDev
    OwnerAuthor

    Wave 1 integrated — PR #108

    All five wave-1 workstreams are implemented, reviewed, integrated and verified.
    One commit per workstream on chantier/audit-fiabilisation.

    Workstream Issue Commit Status
    L1 · Geometry validator #103 bcbec07 integrated
    L2 · Value parsing #104 26f5cc6 integrated
    L3 · Grid layout #105 de58803 integrated
    L4 · Schema surface #106 7eeb4b9 integrated
    L6 · Documentation #107 cb166a3 integrated

    cargo test --workspace → 534 passed / 0 failed. Independent acceptance harness:
    3/11 green on the pre-fix binary, 11/11 after.

    Decision log

    Non-obvious choices made during this wave, with the rejected alternative.

    1. Unresolvable colour renders opaque magenta, not black. Rejected: keeping a black
      fallback. Black is a legitimate authored colour and disappears into the dark frames
      this engine targets, which is precisely the silent failure C2 describes. Magenta
      never blends in. A validation-time error is the better answer and is deferred to
      wave 2 — this wave only makes the failure impossible to miss.

    2. Alpha rounds in both notations. Rejected: preserving truncation for bare floats
      to keep a pre-existing test passing byte-for-byte. That test asserted an artefact of
      the old formula, not a behaviour anyone depends on, and the asymmetry made 0.5 and
      50% disagree on the same value.

    3. Bare-number grid tracks now mean fr, not px. Rejected: preserving the old
      meaning. [1,1,1] previously produced three 1px tracks — never a usable intent —
      because GridTrack's untagged variant order made Fr and Keyword unreachable.

    4. Single fr values use taffy's flex() (minmax(0, Nfr)), not fr(). Rejected:
      spec-exact minmax(auto, Nfr), whose content-based minimum yields unequal columns
      with uneven content. Predictable track sizing matters more in a video renderer.

    5. Clipped content is exempt from every overflow check, including the new one.
      Deliberate bleed under a clipping parent is a composition technique — it is how the
      reference style gets giant type running off frame — and rejecting it made that
      composition unauthorable while validate was mandatory.

    6. The content_overflows_box check does not fire for an unbreakable long word in an
      auto-width box.
      That is ordinary CSS min-width: auto behaviour on a flex item, and
      an element escaping a non-clipping intermediate container while staying inside the
      viewport is not a defect. It does fire when the author set an explicit width.

    7. No follow-up issue filed for the font registration bug. register_font_file
      discards the Typeface from FontMgr::new_from_data, so declared families never
      resolve by name. Rediscovered independently here, but already fixed by 208e8d1 on
      docs/1600-brutalist-style. Filing an issue would duplicate work in flight. See the
      merge-order note in PR Post-audit reliability hardening — wave 1 (critical + high) #108.

    Carried into wave 2

    • Validation-time error for unresolvable colours (completes C2).
    • schema::GridTrack removal — needs traits/container.rs, outside wave 1's partition.
    • schema::style::Spacing and GridPlacement, dead since LayerStyle was removed.
    • The original wave-2 scope: M1 (white-space has no render effect), M2 (rich_text
      invisible in flow), M3 (glow is a no-op), M4 (no legibility floor), M5 (strict-attrs
      hardening — decided at kickoff, error by default plus availability on render),
      M6 (SceneEntry untagged diagnostics).
  2. LeadcodeDev commented on Aug 1, 2026

    @LeadcodeDev
    OwnerAuthor

    Chantier complete — all 19 audit findings addressed

    Wave PR Squash Scope
    1 #108 38596ca 2 critical + 8 high
    2 #112 2ec0659 9 medium + 3 deferred from wave 1

    Verified on merged main: 15 test suites green, clippy clean, all 7 example
    scenarios validate, and the independent acceptance harness reports 13/13 —
    including wave-1 non-regression checks.

    What actually changed

    The audit's conclusion was that the render engine was markedly stronger than the
    chain of trust around it. That chain now holds in both directions.

    validate could previously lie by saying yes — a scenario with invisible
    text (#fff resolved to black), a grid with no columns, content spilling out of
    its card — and by saying no, rejecting the deliberately frame-bleeding
    typography that is the signature of the reference style this project targets.
    Both directions are now covered, and covered by a harness that measures rendered
    pixels rather than trusting unit tests.

    The worst single defect is gone: --fix used to write a property CssStyle
    rejects, which dropped the component from the render — and then reported the
    result as clean. It turned text that overflowed into text that was absent, with
    a green light on top.

    What was deliberately left

    Four follow-ups, each surfaced by a workstream that reported it rather than
    reaching outside its file partition:

    On the third audit objective

    The marketing-video goal is no longer blocked by the engine. 3D, camera,
    multi-plane depth, both blur kinds and the easing solver were already there;
    this chantier made rich_text usable, glow real, and lifted the validator
    rule that made frame-bleeding typography unauthorable.

    What remains for that objective is a second template in the dark-premium
    register — radial blooms, product mockups in perspective, kinetic typography,
    World view for continuity across shots. That is design work, not engine work.
    The existing 1600-style.json is a coherent brutalist piece, but it is not the
    language of the reference videos.

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

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions