Skip to content
This repository was archived by the owner on Aug 12, 2026. It is now read-only.

feat(viewer): restore reproducible archival rendering - #151

Merged
acgetchell merged 3 commits into
mainfrom
fix/98-viewer-artifact-contract
Aug 8, 2026
Merged

acgetchell merged 3 commits into
mainfrom
fix/98-viewer-artifact-contract

Conversation

@acgetchell

@acgetchell acgetchell commented Aug 8, 2026 •

Copy link
Copy Markdown
Owner
  • add an opt-in macOS CGAL/Qt renderer driven by versioned manifests
  • publish canonical fixture provenance and the reproducible hero workflow
  • keep default builds headless while adding viewer build and check recipes

Closes #98

Summary by CodeRabbit

  • New Features

    • Added an opt-in macOS viewer for interactive and headless PNG rendering.
    • Added reproducible, manifest-driven archival rendering using versioned fixtures and metadata.
    • Added viewer build, render, validation, and smoke-test workflows.
  • Bug Fixes

    • Added validation for manifests, fixtures, image dimensions, hashes, topology, and rendering metadata.
    • Viewer builds now clearly reject unsupported platforms.
  • Documentation

    • Documented viewer setup, rendering, reproducibility, artifact policies, and project layout.
  • Tests

    • Added coverage for valid artifacts and malformed or nonstandard metadata.

- add an opt-in macOS CGAL/Qt renderer driven by versioned manifests
- publish canonical fixture provenance and the reproducible hero workflow
- keep default builds headless while adding viewer build and check recipes

Closes #98
@acgetchell acgetchell self-assigned this Aug 8, 2026
@coderabbitai

coderabbitai Bot commented Aug 8, 2026 •

Copy link
Copy Markdown

Review Change Stack

Walkthrough

This PR restores an opt-in macOS CGAL/Qt viewer. It adds versioned fixture and manifest contracts, strict artifact validation, offscreen rendering, smoke tests, build workflows, editor configurations, and documentation while keeping headless builds unchanged.

Changes

Archival viewer workflow

Layer / File(s) Summary
Versioned artifact contract
viewer/schema/*, viewer/manifests/*, viewer/fixtures/*, docs/viewer.md, docs/reproducibility.md, README.md, viewer/README.md
Defines manifest validation, fixture provenance, renderer settings, canonical image requirements, and archival visual policy.
Artifact validation and checks
scripts/validate_viewer_artifacts.py, scripts/tests/test_validate_viewer_artifacts.py, Justfile, pyproject.toml, scripts/tests/test_justfile_discoverability.py
Adds strict validation for JSON, hashes, metadata, topology, PNG dimensions, and canonical output. Tests cover valid artifacts and malformed inputs.
Optional viewer build wiring
CMakeLists.txt, src/CMakeLists.txt, CMakePresets.json, vcpkg.json, Justfile, scripts/build.sh, scripts/pkgx-build.sh, .zed/*
Adds the macOS-only viewer option, dependencies, target, smoke tests, build and render recipes, platform checks, and editor workflows.
Manifest-driven CGAL/Qt renderer
src/cdt-viewer.cpp
Adds manifest parsing, fixture verification, configurable scene construction, interactive and offscreen rendering, image processing, and output validation.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Developer
  participant Justfile
  participant ArtifactValidator
  participant CDTViewer
  participant CGALQt
  participant CanonicalPNG
  Developer->>Justfile: run viewer-render
  Justfile->>CDTViewer: render hero manifest
  CDTViewer->>ArtifactValidator: validate manifest and fixture contract
  ArtifactValidator-->>CDTViewer: return validated fixture path
  CDTViewer->>CGALQt: load triangulation and create scene
  CGALQt->>CanonicalPNG: write PNG output
  CDTViewer->>ArtifactValidator: validate rendered image
  ArtifactValidator-->>Justfile: return validation status
Loading

Possibly related PRs

Poem

A manifest guides the scene,
Qt draws facets crisp and clean.
Hashes guard each archived trace,
Smoke tests check the rendered space.
Headless builds remain serene.

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning The PR adds Zed editor configurations and upgrades uv from 0.12.2 to 0.12.3, which are not required by #98. Move the editor configurations and uv upgrade to separate PRs, or document their direct requirement for the viewer workflow.
Docstring Coverage ⚠️ Warning Docstring coverage is 43.40% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly identifies the main change: restoring reproducible archival rendering through the viewer.
Linked Issues check ✅ Passed The changes implement #98 with an opt-in viewer, reproducible artifacts, validation, smoke tests, documentation, and headless defaults.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/98-viewer-artifact-contract

Comment @coderabbitai help to get the list of available commands.

@acgetchell
acgetchell enabled auto-merge August 8, 2026 04:59

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 18

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.zed/tasks.json:
- Around line 1-37: Add a dedicated Zed task alongside the existing CDT++ tasks
for the viewer workflow, invoking the existing just viewer-render or
viewer-build recipe. Preserve the current task structure and settings, including
the worktree root as cwd and saving all output.

In `@Justfile`:
- Around line 411-414: Update the viewer-build recipe’s OS dispatch to use os()
== "macos" for the _build-viewer-unix branch, while retaining explicit
unsupported-platform errors for both Windows and Linux.

In `@scripts/tests/test_validate_viewer_artifacts.py`:
- Around line 25-52: The test suite lacks coverage for the structural-only
validation mode and image failure paths. Extend
test_repository_viewer_artifacts_validate with a copied PNG mutation case that
succeeds under validate(manifest, canonical=False) but fails under canonical
validation, plus cases asserting wrong dimensions and undersized image files are
rejected with the validator’s stable error.

In `@scripts/validate_viewer_artifacts.py`:
- Around line 254-258: Update the validation error handling around validate in
the CLI entry point to catch UnicodeDecodeError alongside the existing file,
artifact, and JSON/schema errors. Preserve the stable “Viewer artifact
validation failed” message and exit code for invalid UTF-8 manifests or
sidecars.
- Around line 177-189: Extend the provenance validation in the loop using
provenance_fields to compare fixture.provenance.topology with
metadata["topology"], not desired.topology. Add initial_radius and
foliation_spacing checks against their sidecar keys, parsing both values
numerically and applying an explicit precision policy before raising
ViewerArtifactError on mismatch.

In `@src/cdt-viewer.cpp`:
- Around line 793-796: Replace the hardcoded point-match tolerance in the
is_point lambda with a Render_config value populated from a versioned manifest
field such as style.point_color_match_tolerance. Update manifest parsing and
schema handling consistently, preserving the existing alpha check and
color-distance comparison while ensuring the tolerance is part of the frozen
render configuration.
- Around line 383-396: Add validation in the render-manifest parsing flow after
the post-parse checks to reject minimum_foreground_pixels greater than width ×
height, using size_t arithmetic and the specified invalid-argument message. Also
bound oversampling before saveSnapshot can derive framebuffer dimensions,
preserving the existing lower bound while rejecting values that could request
impractical or non-representable allocations; anchor both checks to the existing
render validation logic and result.render fields.
- Around line 973-981: Preserve the exact CGAL and Qt version comparison in the
manifest validation around the renderer startup path to maintain archival
reproducibility. Pin the vcpkg baseline used by the viewer preset so toolchain
versions cannot change implicitly, and document this exact-version policy and
the required manifest/canonical-image regeneration process in
docs/reproducibility.md.
- Around line 757-813: Replace per-pixel QImage::pixelColor/setPixelColor calls
in outline_face_boundaries (src/cdt-viewer.cpp:757-813) with constBits/bits
scanline access, QRgb values, and a QRgb-based color-distance helper while
preserving boundary and point-outline behavior. Also update foreground_pixels
(src/cdt-viewer.cpp:821-840) to use the same scanline access and direct
packed-QRgb comparison; both sites require changes.
- Around line 626-648: Add a concise one-line comment immediately above
orient_outward documenting that its radial normal test assumes the mesh is
star-shaped about the origin, as relied on by the spherical fixture. Do not
change the winding logic.
- Around line 227-248: Add a concise explanatory comment in require_integer
immediately above the MAX_EXACT_JSON_INTEGER comparison, documenting that the
2^53 clamp is required to reject values that may round past integer limits on
platforms with 64-bit long double and to keep overflow behavior
architecture-independent. Keep the existing validation logic unchanged.
- Around line 539-543: Update the topology mismatch exception in the fixture
validation logic to include the manifest’s expected vertices, edges, faces,
simplices, and timeslice range alongside the reported actual values. Preserve
the existing mismatch context while making the error self-contained for
regenerated fixtures.
- Around line 924-928: Add a single-shot watchdog around the headless execution
path before application.exec() that sets render_error to a clear timeout message
and calls application.exit(EXIT_FAILURE) if init() never completes. Ensure the
watchdog is cancelled or prevented from affecting successful runs, while
preserving the existing render_error check and return behavior.
- Around line 149-159: Add a member flag to the viewer and update init() so the
callback is scheduled only once, even if OpenGL reinitializes; preserve the
existing callback behavior and set the guard when scheduling it.
- Line 660: Extract the repeated edge-scope predicates into named helpers
adjacent to Render_config, including draws_scene_edges and
draws_screen_space_edges as needed. Update the options.ignore_all_edges call and
the corresponding logic in configure_viewer and outline_face_boundaries to reuse
these helpers, ensuring all edge-scope decisions remain consistent.
- Around line 888-916: Declare render_error before viewer so the callback’s
referenced state outlives viewer and its stored lambda. In the render callback
registered by viewer.after_initialization, retain the std::exception handler and
add a catch-all handler that records an appropriate diagnostic in render_error
and exits with EXIT_FAILURE, preventing non-standard exceptions from escaping
the Qt event handler.

In `@src/CMakeLists.txt`:
- Around line 51-61: Replace the generated viewer_integer_overflow_manifest
logic in the CMake configuration with a direct reference to a dedicated invalid
manifest fixture under viewer/manifests/v1/. Add the fixture containing an
integer-overflow vertices value, and use that path in the viewer failure test
instead of reading and string-replacing viewer_manifest.
- Around line 42-49: Document near the cdt-viewer-smoke add_test declaration
that this test requires the Cocoa platform and an active GUI session because
cdt-viewer uses QApplication and an OpenGL-backed CGAL viewer. Explicitly state
that QT_QPA_PLATFORM=offscreen is unsupported unless the renderer is redesigned
around QOffscreenSurface, and do not add it as the test environment.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: cf921020-12bc-4d32-9633-40fcad7fff3f

📥 Commits

Reviewing files that changed from the base of the PR and between 8f765ee and 2662df9.

⛔ Files ignored due to path filters (1)
  • docs/images/S3-7-27528-I1-R1.png is excluded by !**/*.png
📒 Files selected for processing (22)
  • .zed/debug.json
  • .zed/tasks.json
  • CMakeLists.txt
  • CMakePresets.json
  • Justfile
  • README.md
  • docs/cgal-integration.md
  • docs/reproducibility.md
  • docs/viewer.md
  • pyproject.toml
  • scripts/build.sh
  • scripts/pkgx-build.sh
  • scripts/tests/test_justfile_discoverability.py
  • scripts/tests/test_validate_viewer_artifacts.py
  • scripts/validate_viewer_artifacts.py
  • src/CMakeLists.txt
  • src/cdt-viewer.cpp
  • vcpkg.json
  • viewer/README.md
  • viewer/fixtures/v1/S3-7-27528-I1-R1-seed30.off.meta
  • viewer/manifests/v1/hero.json
  • viewer/schema/render-manifest-v1.schema.json

Comment thread .zed/tasks.json
Comment thread Justfile Outdated
Comment thread scripts/tests/test_validate_viewer_artifacts.py
Comment thread scripts/validate_viewer_artifacts.py
Comment thread scripts/validate_viewer_artifacts.py
Comment thread src/cdt-viewer.cpp Outdated
Comment thread src/cdt-viewer.cpp
Comment thread src/cdt-viewer.cpp
Comment thread src/CMakeLists.txt
Comment thread src/CMakeLists.txt Outdated
Commit the hash-pinned OFF payload required by clean checkouts and mark it
binary so cross-platform line-ending conversion cannot invalidate its digest.
- validate exact manifest provenance, topology, and canonical PNG identity
- bound rendering controls and make foreground matching manifest-driven
- make one-shot headless rendering fail safely and reject unsupported platforms
@acgetchell

Copy link
Copy Markdown
Owner Author

@CodeRabbit review

@coderabbitai

coderabbitai Bot commented Aug 8, 2026 •

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
viewer/schema/render-manifest-v1.schema.json (1)

395-397: 🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Enforce minimum_foreground_pixels during structural validation.

Lines 395-397 require this value, but _validate_image does not inspect image pixels. In --structural-only mode, a valid fully transparent PNG can pass when rendering produced no geometry. Count foreground pixels and reject output below this manifest limit. Add a transparent-image test for this path.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@viewer/schema/render-manifest-v1.schema.json` around lines 395 - 397, Update
_validate_image to count non-transparent foreground pixels and reject images
whose count is below the manifest’s minimum_foreground_pixels value, including
in --structural-only mode. Preserve existing validation behavior for images
meeting the limit, and add a test covering a fully transparent PNG that must be
rejected.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@viewer/schema/render-manifest-v1.schema.json`:
- Around line 395-397: Update _validate_image to count non-transparent
foreground pixels and reject images whose count is below the manifest’s
minimum_foreground_pixels value, including in --structural-only mode. Preserve
existing validation behavior for images meeting the limit, and add a test
covering a fully transparent PNG that must be rejected.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 0246b1b2-1473-463f-b406-1d20902994e5

📥 Commits

Reviewing files that changed from the base of the PR and between 2662df9 and 81d366e.

📒 Files selected for processing (13)
  • .gitattributes
  • .gitignore
  • .zed/tasks.json
  • Justfile
  • docs/reproducibility.md
  • scripts/tests/test_validate_viewer_artifacts.py
  • scripts/validate_viewer_artifacts.py
  • src/CMakeLists.txt
  • src/cdt-viewer.cpp
  • viewer/fixtures/v1/S3-7-27528-I1-R1-seed30.off
  • viewer/manifests/v1/hero.json
  • viewer/manifests/v1/invalid-integer-overflow.json
  • viewer/schema/render-manifest-v1.schema.json

@acgetchell
acgetchell merged commit 162e7ff into main Aug 8, 2026
16 of 17 checks passed
@acgetchell
acgetchell deleted the fix/98-viewer-artifact-contract branch August 8, 2026 13:08
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Restore the Qt viewer and reproducible artifact generation

1 participant