Skip to content

macOS/arm64 ParaView artifact tier with clone-safe receipts - #28

Open
aurascoper wants to merge 3 commits into
feat/lattice-viewerfrom
docs/paraview-macos-arm64-2026-09-11
Open

macOS/arm64 ParaView artifact tier with clone-safe receipts#28
aurascoper wants to merge 3 commits into
feat/lattice-viewerfrom
docs/paraview-macos-arm64-2026-09-11

Conversation

@aurascoper

@aurascoper aurascoper commented Sep 11, 2026

Copy link
Copy Markdown
Owner

First ParaView read of this exporter's output on Apple Silicon, plus the artifact-provenance tier around it. Docs-only; scripts and receipts, no .vti/.png/.mp4/evidence.

The hole this closes

verify_artifacts.py read derived_manifest.sha256 from beside the data. A clone tampered consistently — bit flipped, manifest regenerated, receipt regenerated — passed every check, because the manifest it was checked against was the one the tamperer rewrote. Reproduced: one bit flipped in signal_mcs000050.vti at byte 1506123, both manifest files regenerated, yields 0 of 209 artifact hashes altered and "manifest matches its own receipt: True".

make_render_manifest.py writes render_manifest.json beside the renderers, freezing derived_manifest_sha256, parent_manifest_sha256, pvd_sha256 and the artifact count, alongside the rendering recipe (camera, slice as a full numeric coordinate, threshold predicate, adjacency rule, LUT ranges, ray-tracing status). --receipt pins both verifiers to it.

The pin that catches it is not the obvious one. That tamper leaves the .pvd byte-identical, so pinning the .pvd alone misses it entirely. Only the pinned derived_manifest_sha256 bites.

Controls are runnable, not narrated

controls.py — seven controls on APFS copy-on-write clones, 12 s total, each mutation asserting n == 1. Control 5 is the consistent tamper and is the only one whose expected result includes a pass: unpinned it must pass, or the hole it documents is not real.

Baseline (pinned, real evidence) Result
verify_artifacts.py 16 checks, 0 failures
verify_state.py 20 checks, 0 failures
controls.py --with-state 7 controls, 0 did not fire, 0 blocked

Two things deliberately not presented as controls

ParaView 6.1.1 does not serialise EnableRayTracing into a .pvsm at all — verified by saving one state at the default 0 and another explicitly at 1 and grepping both. A reloaded state always reports 0 regardless of how frames were rendered, so that assertion is kept and relabelled as documentation of an invariant. The render-time value is the evidence and lives in build_report.json.

Protected CSVs are recorded by git hash-object against HEAD:, not as a before/after pair — a before/after pair only proves this pipeline was innocent.

The macOS pvpython account lives in one place now (6951f29)

The four ParaView-6.1.1 issues and the absent libopenvkl_module_cpu_device.dylib were stated
in full both here and inline in docs/visualization/lattice_viewer.md on #27, where they were
inlined to repair a reference to a path that does not exist on that branch's base. Nothing linked
the two copies and they had drifted before either branch merged, "Preset is" against "The preset
is" and "both views" against "the views".

The account now lives in lattice_viewer.md and the section here points at it. That direction
because lattice_viewer.md is already on feat/lattice-viewer, the base both branches share,
so the pointer is at worst early: if this branch merges before #27 the section is not there yet
and the pointer does not resolve until #27 lands, which the text says plainly. A pointer the
other way would have been to a file absent from #27's base entirely, which is the finding that
started this. The heading here has read "macOS notes for the doc" since it was written.

render_manifest.json keeps the same environment fact as a note field
(make_render_manifest.py:126) and is left alone: a receipt recording the conditions it was
produced under is not a third copy of the account, and editing frozen evidence to tidy prose
would be the wrong trade. No receipt hashes any .md, here or at the stack tip, so nothing was
invalidated: verify_artifacts.py --receipt render_manifest.json still reports 16 checks, 0
failures, all four frozen pins matching.

🤖 Generated with Claude Code

https://claude.ai/code/session_01V1GLLCekStea7UHjRF51aw

Freeze expected hashes in render_manifest.json beside the renderers.
verify_artifacts.py read derived_manifest.sha256 from beside the data,
so a consistently tampered clone -- data altered, manifest and receipt
regenerated -- passed all 209 hash checks and certified itself.

Make the negative controls runnable rather than narrated: controls.py,
7 controls on APFS clones, each mutation asserting it changed exactly
one site. Control 5 is the consistent tamper; it must pass unpinned or
the hole it documents is not real.

Pinned baselines: 16 and 20 checks, 0 failures. Both protected CSVs
verified against HEAD, unmodified.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V1GLLCekStea7UHjRF51aw
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 11, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-11T10:01:58.175605Z 11a3553 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 11a3553950

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +11 to +13
if "--receipt" in sys.argv:
_rp = sys.argv[sys.argv.index("--receipt") + 1]
if os.path.isfile(_rp):

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Refuse an unreadable state receipt

When --receipt names a missing file, this leaves RECEIPT as None and continues with the unpinned 18-check path, so a typo can produce a successful verification while silently dropping the frozen-manifest protection that this commit adds. Match verify_artifacts.py by failing closed whenever the option was supplied but could not be loaded.

AGENTS.md reference: AGENTS.md:L176-L184

Useful? React with 👍 / 👎.

Comment on lines +193 to +195
m = re.search(r'(<Property name="RGBPoints".*?)(9)(\b)', text, re.S)
assert m, "control could not locate the signal LUT upper bound in the state"
sub_once(state, re.escape(m.group(0)), (m.group(1) + "5" + m.group(3)).replace("\\", "\\\\"), expect=1)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Mutate the LUT upper-bound element specifically

For a normal serialized Viridis transfer function, this regex replaces the first standalone digit 9 anywhere after the first RGBPoints property, rather than selecting the signal LUT's final x-coordinate. The committed control log confirms that the alleged 0–5 mutation reloads as 0–0.004874, so the test fires after corrupting unrelated RGBPoints structure and does not prove that changing the actual upper bound is detected; locate the signal LUT and assert that its final coordinate became exactly 5.

AGENTS.md reference: AGENTS.md:L62-L68

Useful? React with 👍 / 👎.

Comment on lines +162 to +164
rc_u, nf_u, log_u = run_artifacts(c, None, wd) # unpinned: expected to PASS
rc_p, nf_p, log_p = run_artifacts(c, receipt, wd) # pinned: must FAIL
verdict = "FIRES" if nf_p else "DID-NOT-FIRE"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Require both consistent-tamper outcomes

The control's stated contract requires the unpinned run to have zero failures and the pinned run to fail, but the verdict consumes only nf_p. If the unpinned verifier later begins failing—or merely fails for an unrelated reason—this control still records FIRES and all_fired: true, so it no longer demonstrates the self-certification hole documented in the README; require nf_u == 0 as well as a nonzero pinned failure count.

AGENTS.md reference: AGENTS.md:L78-L85

Useful? React with 👍 / 👎.

Comment on lines +54 to +55
ck("Species_1to7 filter present", thr_sp is not None, thr_sp is not None)
ck("species threshold 1..7", thr_sp.LowerThreshold == 1 and thr_sp.UpperThreshold == 7,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Guard missing proxies before dereferencing them

When a state lacks Species_1to7, the presence check records a failure but the next line immediately dereferences None; Interior_mask_1 has the same ordering. The intended missing-filter abort below is therefore unreachable for these inputs, and the verifier exits with a traceback without writing its machine-readable failure receipt. Move all proxy-dependent checks after the existing None guard.

Useful? React with 👍 / 👎.

Comment on lines +44 to +47
m = _re.search(r"^threshold\s*=\s*([0-9.]+)", open(cfg).read(), _re.M)
if m and abs(float(m.group(1)) - THRESHOLD) > 1e-12:
raise SystemExit("FATAL: THRESHOLD=%s but %s declares %s" % (THRESHOLD, cfg, m.group(1)))
print("threshold reconciled against", cfg)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Refuse a threshold that was not parsed

When config.toml exists but the threshold is absent or written in a TOML-valid form outside [0-9.]+ (for example scientific notation), m is false and execution still prints that the threshold was reconciled before rendering every frame with the hard-coded 5.0 label. Require a successfully parsed threshold before claiming reconciliation, otherwise the movie and receipt can silently misstate the producer's declared endpoint.

AGENTS.md reference: AGENTS.md:L176-L184

Useful? React with 👍 / 👎.

…tated

The LUT control fired for four commits without performing its stated mutation.
Its regex (<Property name="RGBPoints".*?)(9)(\b) with re.S is leftmost-first and
lazy, so in a 1024-element Viridis table it matched the `9` in <Element index="9"/>
-- an index attribute, eight elements in -- and rewrote it to index="5", producing
a duplicate index and destroying index 9. The array was mangled, the verifier read
`signal LUT range 0..9 -> 0.0..0.004874`, and the control counted one failure and
looked healthy. README.md reported `0.0..5.0`, which no stored run ever produced;
the committed receipt said 0.004874 the whole time.

It now mutates through the parsed document and asserts the element it changed was
at x=9.0 before and x=5.0 after. sub_once asserts a count; a count cannot tell you
the edit landed on the right element.

Control 5 asserted half its contract. Its expectation is "unpinned PASSES, pinned
FAILS" but the verdict read `"FIRES" if nf_p else ...`, so the unpinned result fed
a display string and nothing else. A regression breaking the unpinned tier, or
closing the hole the control documents, would have left it green. Now requires
nf_u == 0 and nf_p.

verify_state.py loaded its receipt only `if os.path.isfile(...)` with no else, and
printed nothing about pinning, so a typo'd --receipt ran UNPINNED with output
byte-identical to a deliberate unpinned run. It now shares verify_artifacts.py's
contract: load, validate the four required pinned keys, or exit 2. Each run
declares its mode -- pinned or self-consistency -- in the log and the receipt.
Unpinned 18 checks, pinned 20.

Two adjacent repairs. verify_state.py establishes every proxy's presence before
reading any property on it; it used to read thr_sp.LowerThreshold immediately after
the presence check and abort only later, so a missing filter raised AttributeError
instead of reporting a failure. And animate_4d.py parses config.toml as TOML and
refuses a missing or undeclared threshold -- its regex form did `if m and ...: raise`
then printed "threshold reconciled" unconditionally, claiming a reconciliation it
had not performed whenever the regex failed to match.

7 controls fire, 0 blocked. Baselines 16 and 20 checks, 0 failures.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V1GLLCekStea7UHjRF51aw
The four ParaView-6.1.1-on-macOS issues and the absent libopenvkl dylib were stated
in full in two files: here, and inline in docs/visualization/lattice_viewer.md on
docs/phase2-macos-pipeline (#27), where they were inlined to repair a reference to a
path that does not exist on that branch's base. Nothing linked the two copies and
they had already drifted, "Preset is" against "The preset is" and "both views"
against "the views", before either branch merged.

The account stays in lattice_viewer.md and this file points at it. That direction,
not the other one: lattice_viewer.md is already on feat/lattice-viewer, the base both
branches share, so the pointer is at worst early. If this branch merges first the
section is not there yet and the pointer does not resolve until #27 lands, which the
text says plainly. A pointer the other way would have been to a file absent from
#27's base entirely, which is the finding that started this.

The heading here has read "macOS notes for the doc" since it was written. The notes
have now gone to the doc.

Left alone deliberately: render_manifest.json carries the same environment fact as a
note field (make_render_manifest.py:126). A receipt recording the conditions it was
produced under is not a third copy of the account, and editing frozen evidence to
tidy prose would be the wrong trade.

No receipt hashes any .md, on this branch or at the stack tip, so nothing was
invalidated. verify_artifacts.py --receipt render_manifest.json still reports 16
checks, 0 failures, with all four frozen pins matching.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V1GLLCekStea7UHjRF51aw
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant