Stop leaking host paths into calibrated pictures, and let provenance through (#241) - #260
Merged
Conversation
#241 reports an absolute path, and a university email address inside it, in every calibrated picture. Reading the code, the leak is wider than the pcomb line it was found on: the LDR branch of prepareInputs hands hdrgen host paths too, and only the RAW branch escapes because conversion forced it to invent new names. The design gives every file the pipeline names a sanitized work path that keeps its basename, done once at the staging boundary so the orchestrator, the filter stage and the release bookkeeping are untouched. With no host path left in any argv, -h stops being load-bearing and comes off the fourth correction stage, which is the consistency follow-up the tutorial-conformance spec parked in its §9. Also records where -h came from, since that decided whether removing it is a change or a correction: not the tutorial, whose Table 3 step 9 is pcomb -s factor, but extra/ldr-to-hdr.sh, transcribed into Rust in 9825c4b with no comment and ported to TypeScript byte for byte. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Five tasks: a pure naming layer, the staging boundary that applies it, collapsing the photometric stage into pcombCalArgs now that -h holds nothing back, calibration warnings that name the file rather than its staging path, and a manual header check, which is the only step that can prove any of it, since nothing in the suite asserts on header content. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ctions Task 4 edits the text of a warning inside warnIfResolutionDependent, which the constraint as written forbade outright. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Radiance tools write their own argv into the header of the picture they produce, so a path handed to a tool is a path published in the output. The naming layer keeps the basename, which carries the meaning, and drops the directory, which is what leaks. Not wired up yet. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
collectOutputs scans /work after every tool and files whatever it finds as something that tool produced. Sources staged under /work/src and /work/cal would appear there as directory entries and be collected as zero-byte outputs, and WORK_DIR's own comment says the prefix is for intermediates. The runner already expects sources to live elsewhere, and makeParentDirs creates whatever depth they need, so /src and /cal cost nothing and keep the invariant. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The hdrgen and LibRaw rows already named theirs; these two pointed only at our fork, so a reader could not tell where the code came from. Both confirmed against the GitHub API rather than assumed: radiantlab/Radiance forks LBNL-ETA/Radiance, radiantlab/panlib forks radiance-org/panlib. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
… for outputs collectOutputs scans /work after every tool and files whatever it finds as something that tool produced. Sources staged under /work/src and /work/cal would appear there as directory entries and be collected as zero-byte outputs. The runner already expects sources to live elsewhere, and makeParentDirs creates whatever depth they need, so /src and /cal cost nothing and keep the invariant. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The desktop staged every file under the absolute path the native dialog returned, so hdrgen and pcomb were handed host paths and wrote them into the headers of both output pictures. The browser never had the problem, because vfs.ts already hands out synthetic paths; this gives the desktop the same shape. Done at the staging boundary, so prepareInputs, the filter stage and the release bookkeeping are untouched: they treat a path as an opaque key into the virtual filesystem, which is what makes this containable. Closes #241 for the leak; the header flag follows. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The photometric adjustment was the only one of the four corrections to pass pcomb -h, so it threw away everything the three before it had accumulated: the camera, the frames hdrgen merged, the capture date, and the crop and resize lines. A picture processed with calibration files recorded less than one processed without, which is backwards for outputs that go into papers. The flag traces to extra/ldr-to-hdr.sh rather than to the tutorial, whose Table 3 step 9 is pcomb -s factor. With the paths sanitized it was no longer holding anything back, so the stage collapses into pcombCalArgs and the special case goes. Closes #241. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
… path Staged files are named /cal/<slot>-<file> now, which means nothing to a user reading a status log. The run transcript is stored with the run, so this is also the last place a full host path was being written down. The runner's own read-failure text also embedded the staged path (e.g. "no such file /cal/<name>"), so that detail is scrubbed too -- otherwise the warning would still spell out the path one sentence after naming the file. Also adds a regression test pinning all four corrections (fisheye, vignetting, neutral density, photometric) to the same pcomb argument shape with no -h flag, closing a coverage gap left by #241/#254: nothing previously exercised all four cal files in one run to confirm the fourth correction's -h flag stays gone. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Round 1 fix for task 4: the previous scrub (`String(error).replaceAll(calPath,
name)`) only worked because both ToolRunner implementations happen to embed
calPath as an exact substring, and they already disagree on phrasing ("no
such file " vs "no such file: "). A future runner that quotes, prefixes, or
normalizes the path would make the scrub a silent no-op, and no orchestrator
test exercises WasmRunner to catch it.
Instead of scrubbing, the read-failure warning now drops the underlying
error's text entirely and is built only from the label and the staged file's
basename -- values this function already controls. Nothing actionable is
lost: the correction stage that follows will fail on its own and surface the
real error if the file is genuinely unreadable.
Strengthened "names the file when it cannot be read either" to assert the
full staged path never appears in the message, not just its "/cal/" prefix,
so the invariant is pinned rather than today's wording.
Also renamed calWarning's second parameter from `path` to `name`, since every
caller now passes a basename. Naming only; signature and behaviour unchanged.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Nothing in the suite looked inside a header, which is where #241 lived: every Radiance tool appends its own command line to the picture it writes, so a path handed to a tool is a path published in the output. The existing JPEG test already downloads both pictures and reads their bytes, so the check costs no extra run time. Two assertions. Every absolute path in the header must sit under /src, /cal or /work, and a Windows path is checked separately because it does not start with a slash. And there must be exactly one active VIEW= line, which is the question removing `pcomb -h` raised: hdrgen writes its own VIEW= from EXIF, and the worry was that it would compete with the one the pipeline writes. Both were validated against real Radiance output before being committed. A header from the full stage sequence with an unsanitised cal path trips the first four times, once per pcomb stage, and carries exactly one active VIEW=; a header from the shipped wasm hdrgen with sources staged under /src trips neither. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The design asserted that hdrgen writes host paths into its provenance line, so the LDR merge was a second leak surface. Checked rather than reasoned about, after the fact: hdrgen strips the directory and writes basenames only, and the response function is not named in the header at all. The leak is exactly the pcomb -f cal path #241 reported. Re-pathing the merge inputs is kept for determinism across machines, not because it fixes anything. Also replaces the planned manual inspection with the automated assertions in pipeline.spec.ts, and records what they were validated against: a header from the full stage sequence with an unsanitised cal path trips the path check four times and carries exactly one active VIEW=, which settles the question that removing -h raised. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The new header guard asserted exactly one active VIEW= in both downloads, but falsecolor composes its output with pcompos -h, so that file inherits no header and carries none. The assertion would have failed on every CI run. This branch's own plan already records the false-colour map as header-stripped, which is what makes the mistake plain: the guard was written as though a run produced one file. Scoped to the picture. Both path assertions still cover both files, and the map's missing provenance is left unpinned rather than fixed in place, since it is filed separately. Also sweeps four comments and a loop variable still saying "work path" for things now staged under /src and /cal, and corrects the design's claim that error paths are mapped back to the original: they are not, and the basename surviving is the whole mitigation. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Three phrases in the design itself still described staged files as living on a work path, in the same document the fix wave edited. Raised as an out-of-scope observation by the re-review; prose only, no behaviour. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
…stead
Removing -h broke the pipeline outright, which CI caught and no local test
could. Radiance tools indent an inherited header with tabs, and evalglare
refuses any picture whose header has EXPOSURE= and a tab on the same line:
pictool.c:214 if (strstr(s, EXPOSSTR) && strstr(s, "\t")) { ... exit(1) }
pcompos writes an EXPOSURE= line during the crop. With -h on the fourth
correction that line stays at column zero and evalglare is content; without it
every correction nests it one tab deeper and the glare stage exits producing
nothing. Measured against the shipped wasm binary, not inferred, which is also
why the uncalibrated path kept working and the calibrated one did not. The flag
was load-bearing for EXPOSURE, not for the stale VIEW line everyone tested.
So provenance cannot be inherited. It is re-stated instead, after evalglare has
run, through the same getinfo -a the pipeline already uses for VIEW and the
illuminance values: camera, capture date, the merged frame list, the lens-flare
note, and the calibration basenames. Entries are built as NAME= value rather
than passed through verbatim, because hdrgen's own frame line begins with an
argv[0] that WebAssembly reports as ./this.program. Nothing emitted may carry a
tab or an exposure entry, since someone will run evalglare on the output.
CAPDATE is carried as hdrgen resolved it. It names one frame's EXIF timestamp,
the first hdrgen lists, and header.c:44 makes it a standard identifier parsed
as UTC, so it has to stay a single well-formed time rather than become a range.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
setTextInputValue assigned element.value directly. React replaces the value property with its own accessor and keeps a tracker of the last value it wrote; on an input event it compares the two and drops the event when they match. A direct assignment updates the DOM and the tracker together, so React concludes nothing changed and the form never hears. The failure is silent and convincing: the field displays the path and any assertion on getValue() passes. All five fields were set this way, so "generates an HDR image" has been running an unconfigured pipeline with no calibration, no response function and a default lens mask. Reproduced against the real app before changing anything: the DOM showed the .cal path, the run completed, and not one correction stage executed. That is why this suite stayed green through a change that broke calibration on every platform. Fixing the setter is half of it; the other half is that the test now reads the finished picture's header, which is the only evidence here of what actually ran. It asserts the run recorded the calibration it was given, and that no host path reached the output -- the desktop is the host #241's leak came from, so that assertion belongs here. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The premise was that -h could be removed. It could not: evalglare exits rather than read a header with EXPOSURE= and a tab on one line, and every correction without -h nests that line one tab deeper. The correction sits above the original reasoning rather than replacing it, because the reasoning is why the mistake was made and worth keeping legible. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Merged
This was referenced Aug 6, 2026
adulbrich
added a commit
that referenced
this pull request
Aug 7, 2026
…through (#241) (#260) * docs: design for sanitizing pipeline paths and restoring provenance #241 reports an absolute path, and a university email address inside it, in every calibrated picture. Reading the code, the leak is wider than the pcomb line it was found on: the LDR branch of prepareInputs hands hdrgen host paths too, and only the RAW branch escapes because conversion forced it to invent new names. The design gives every file the pipeline names a sanitized work path that keeps its basename, done once at the staging boundary so the orchestrator, the filter stage and the release bookkeeping are untouched. With no host path left in any argv, -h stops being load-bearing and comes off the fourth correction stage, which is the consistency follow-up the tutorial-conformance spec parked in its §9. Also records where -h came from, since that decided whether removing it is a change or a correction: not the tutorial, whose Table 3 step 9 is pcomb -s factor, but extra/ldr-to-hdr.sh, transcribed into Rust in ed8ac04 with no comment and ported to TypeScript byte for byte. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * docs: implementation plan for the pipeline header provenance fix Five tasks: a pure naming layer, the staging boundary that applies it, collapsing the photometric stage into pcombCalArgs now that -h holds nothing back, calibration warnings that name the file rather than its staging path, and a manual header check, which is the only step that can prove any of it, since nothing in the suite asserts on header content. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * docs: bind the opaque-path constraint to path handling, not whole functions Task 4 edits the text of a warning inside warnIfResolutionDependent, which the constraint as written forbade outright. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * feat(pipeline): name staged files without their directories Radiance tools write their own argv into the header of the picture they produce, so a path handed to a tool is a path published in the output. The naming layer keeps the basename, which carries the meaning, and drops the directory, which is what leaks. Not wired up yet. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * docs: stage sources outside /work, which the runner reserves for outputs collectOutputs scans /work after every tool and files whatever it finds as something that tool produced. Sources staged under /work/src and /work/cal would appear there as directory entries and be collected as zero-byte outputs, and WORK_DIR's own comment says the prefix is for intermediates. The runner already expects sources to live elsewhere, and makeParentDirs creates whatever depth they need, so /src and /cal cost nothing and keep the invariant. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * docs: name the upstream for Radiance and panlib in the source column The hdrgen and LibRaw rows already named theirs; these two pointed only at our fork, so a reader could not tell where the code came from. Both confirmed against the GitHub API rather than assumed: radiantlab/Radiance forks LBNL-ETA/Radiance, radiantlab/panlib forks radiance-org/panlib. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(pipeline): stage sources outside /work, which the runner reserves for outputs collectOutputs scans /work after every tool and files whatever it finds as something that tool produced. Sources staged under /work/src and /work/cal would appear there as directory entries and be collected as zero-byte outputs. The runner already expects sources to live elsewhere, and makeParentDirs creates whatever depth they need, so /src and /cal cost nothing and keep the invariant. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(pipeline): stage source files under names that carry no directory The desktop staged every file under the absolute path the native dialog returned, so hdrgen and pcomb were handed host paths and wrote them into the headers of both output pictures. The browser never had the problem, because vfs.ts already hands out synthetic paths; this gives the desktop the same shape. Done at the staging boundary, so prepareInputs, the filter stage and the release bookkeeping are untouched: they treat a path as an opaque key into the virtual filesystem, which is what makes this containable. Closes #241 for the leak; the header flag follows. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(pipeline): let the calibrated picture keep its provenance The photometric adjustment was the only one of the four corrections to pass pcomb -h, so it threw away everything the three before it had accumulated: the camera, the frames hdrgen merged, the capture date, and the crop and resize lines. A picture processed with calibration files recorded less than one processed without, which is backwards for outputs that go into papers. The flag traces to extra/ldr-to-hdr.sh rather than to the tutorial, whose Table 3 step 9 is pcomb -s factor. With the paths sanitized it was no longer holding anything back, so the stage collapses into pcombCalArgs and the special case goes. Closes #241. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(pipeline): name the calibration file in warnings, not its staging path Staged files are named /cal/<slot>-<file> now, which means nothing to a user reading a status log. The run transcript is stored with the run, so this is also the last place a full host path was being written down. The runner's own read-failure text also embedded the staged path (e.g. "no such file /cal/<name>"), so that detail is scrubbed too -- otherwise the warning would still spell out the path one sentence after naming the file. Also adds a regression test pinning all four corrections (fisheye, vignetting, neutral density, photometric) to the same pcomb argument shape with no -h flag, closing a coverage gap left by #241/#254: nothing previously exercised all four cal files in one run to confirm the fourth correction's -h flag stays gone. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(pipeline): stop the read-failure warning depending on error wording Round 1 fix for task 4: the previous scrub (`String(error).replaceAll(calPath, name)`) only worked because both ToolRunner implementations happen to embed calPath as an exact substring, and they already disagree on phrasing ("no such file " vs "no such file: "). A future runner that quotes, prefixes, or normalizes the path would make the scrub a silent no-op, and no orchestrator test exercises WasmRunner to catch it. Instead of scrubbing, the read-failure warning now drops the underlying error's text entirely and is built only from the label and the staged file's basename -- values this function already controls. Nothing actionable is lost: the correction stage that follows will fail on its own and surface the real error if the file is genuinely unreadable. Strengthened "names the file when it cannot be read either" to assert the full staged path never appears in the message, not just its "/cal/" prefix, so the invariant is pinned rather than today's wording. Also renamed calWarning's second parameter from `path` to `name`, since every caller now passes a basename. Naming only; signature and behaviour unchanged. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * test(web): assert the finished pictures name no host path Nothing in the suite looked inside a header, which is where #241 lived: every Radiance tool appends its own command line to the picture it writes, so a path handed to a tool is a path published in the output. The existing JPEG test already downloads both pictures and reads their bytes, so the check costs no extra run time. Two assertions. Every absolute path in the header must sit under /src, /cal or /work, and a Windows path is checked separately because it does not start with a slash. And there must be exactly one active VIEW= line, which is the question removing `pcomb -h` raised: hdrgen writes its own VIEW= from EXIF, and the worry was that it would compete with the one the pipeline writes. Both were validated against real Radiance output before being committed. A header from the full stage sequence with an unsanitised cal path trips the first four times, once per pcomb stage, and carries exactly one active VIEW=; a header from the shipped wasm hdrgen with sources staged under /src trips neither. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * docs: record the header verification, and correct a claim that was wrong The design asserted that hdrgen writes host paths into its provenance line, so the LDR merge was a second leak surface. Checked rather than reasoned about, after the fact: hdrgen strips the directory and writes basenames only, and the response function is not named in the header at all. The leak is exactly the pcomb -f cal path #241 reported. Re-pathing the merge inputs is kept for determinism across machines, not because it fixes anything. Also replaces the planned manual inspection with the automated assertions in pipeline.spec.ts, and records what they were validated against: a header from the full stage sequence with an unsanitised cal path trips the path check four times and carries exactly one active VIEW=, which settles the question that removing -h raised. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(test): do not demand a VIEW= line from the false-colour map The new header guard asserted exactly one active VIEW= in both downloads, but falsecolor composes its output with pcompos -h, so that file inherits no header and carries none. The assertion would have failed on every CI run. This branch's own plan already records the false-colour map as header-stripped, which is what makes the mistake plain: the guard was written as though a run produced one file. Scoped to the picture. Both path assertions still cover both files, and the map's missing provenance is left unpinned rather than fixed in place, since it is filed separately. Also sweeps four comments and a loop variable still saying "work path" for things now staged under /src and /cal, and corrects the design's claim that error paths are mapped back to the original: they are not, and the basename surviving is the whole mitigation. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * docs: finish the work-path terminology sweep in the design Three phrases in the design itself still described staged files as living on a work path, in the same document the fix wave edited. Raised as an out-of-scope observation by the re-review; prose only, no behaviour. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(pipeline): restore pcomb -h, and write provenance deliberately instead Removing -h broke the pipeline outright, which CI caught and no local test could. Radiance tools indent an inherited header with tabs, and evalglare refuses any picture whose header has EXPOSURE= and a tab on the same line: pictool.c:214 if (strstr(s, EXPOSSTR) && strstr(s, "\t")) { ... exit(1) } pcompos writes an EXPOSURE= line during the crop. With -h on the fourth correction that line stays at column zero and evalglare is content; without it every correction nests it one tab deeper and the glare stage exits producing nothing. Measured against the shipped wasm binary, not inferred, which is also why the uncalibrated path kept working and the calibrated one did not. The flag was load-bearing for EXPOSURE, not for the stale VIEW line everyone tested. So provenance cannot be inherited. It is re-stated instead, after evalglare has run, through the same getinfo -a the pipeline already uses for VIEW and the illuminance values: camera, capture date, the merged frame list, the lens-flare note, and the calibration basenames. Entries are built as NAME= value rather than passed through verbatim, because hdrgen's own frame line begins with an argv[0] that WebAssembly reports as ./this.program. Nothing emitted may carry a tab or an exposure entry, since someone will run evalglare on the output. CAPDATE is carried as hdrgen resolved it. It names one frame's EXIF timestamp, the first hdrgen lists, and header.c:44 makes it a standard identifier parsed as UTC, so it has to stay a single well-formed time rather than become a range. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * test(desktop): make the suite actually fill the form, and prove it did setTextInputValue assigned element.value directly. React replaces the value property with its own accessor and keeps a tracker of the last value it wrote; on an input event it compares the two and drops the event when they match. A direct assignment updates the DOM and the tracker together, so React concludes nothing changed and the form never hears. The failure is silent and convincing: the field displays the path and any assertion on getValue() passes. All five fields were set this way, so "generates an HDR image" has been running an unconfigured pipeline with no calibration, no response function and a default lens mask. Reproduced against the real app before changing anything: the DOM showed the .cal path, the run completed, and not one correction stage executed. That is why this suite stayed green through a change that broke calibration on every platform. Fixing the setter is half of it; the other half is that the test now reads the finished picture's header, which is the only evidence here of what actually ran. It asserts the run recorded the calibration it was given, and that no host path reached the output -- the desktop is the host #241's leak came from, so that assertion belongs here. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * docs: record that -h is load-bearing, against what this design assumed The premise was that -h could be removed. It could not: evalglare exits rather than read a header with EXPOSURE= and a tab on one line, and every correction without -h nests that line one tab deeper. The correction sits above the original reasoning rather than replacing it, because the reasoning is why the mistake was made and worth keeping legible. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #241.
Every Radiance tool appends its own command line to the header of the picture it writes. The pipeline named files by whatever string the host used to find them, and on the desktop that is an absolute path from the native file dialog, so it ended up inside the output. The reported case:
A university email address, in every calibrated picture, in files that go into papers as supplementary material.
What changed
Sources are staged under sanitised names.
/src/<n>-<basename>for frames,/cal/<slot>-<basename>for calibration files. The basename carries the meaning (CF_f5d6.calnames the aperture); the directory is the part that leaks. Done at the single staging boundary inexecuteInWorker, soprepareInputs, the filter stage and thereleasebookkeeping are untouched: they treat a path as an opaque key into the virtual filesystem. The caller's params are copied rather than mutated, so run history and the form still show the files you picked.Deliberately outside
/work, whichcollectOutputsscans after every tool to gather what that tool produced.pcomb -his gone from the fourth correction. It was the only one of four that passed it, so that stage discarded everything the three before it accumulated: the camera, the merged frame list, the real capture date,PRIMARIES,EXPOSURE, and the crop and resize lines. A calibrated picture recorded less than an uncalibrated one. The flag traces toextra/ldr-to-hdr.sh, transcribed into Rust in9825c4bwithout a comment and ported across for parity with a file that no longer exists; Table 3 step 9 of Pierson et al. (2019) does not call for it. With the paths sanitised it was holding nothing back, so the special case and itssuppressHeaderflag are deleted and all four corrections use one builder.Calibration warnings name the file, not its staging path. Run transcripts are stored with the run, so that was the last place a full host path was written down.
Before and after
Before, a real calibrated output from this app:
After, the same stage sequence through the shipped wasm binaries:
A correction, recorded rather than buried
The design originally claimed hdrgen also leaks host paths through its provenance line, making the merge a second leak surface. That is wrong, and was checked only after the code was written: hdrgen strips directories and writes basenames. The leak is exactly the
pcomb -fcal path #241 reported. Re-pathing the merge inputs is kept because it costs nothing and makes headers identical across machines, not because it fixes anything. The design doc carries this correction in place.Verification
pipeline.spec.tsgains two assertions on both finished pictures: every absolute path in the header must sit under/src,/calor/work, and no Windows path may appear. The picture must carry exactly one activeVIEW=line, which is the question removing-hraised: hdrgen writes its own from EXIF, and each copying stage indents the inherited header one tab deeper, so the stale one ends up deactivated. The false-colour map is exempt from the view check becausefalsecolorcomposes it withpcompos -hand it inherits no header at all.Both were validated against real Radiance output before being committed. A header from the full stage sequence with an unsanitised cal path trips the path check four times, once per
pcombstage, and carries exactly one activeVIEW=; a real 18-frame merge from the shipped wasm hdrgen with sources under/srctrips neither.Please treat the Chromium job as the merge gate. These assertions have never executed: the browser e2e does not complete on the machine this was developed on, for reasons unrelated to this change. Local gates are green (424 tests,
tscclean at the root and ine2e-web, lint clean).Not in scope, filed separately
The false-colour map carries no provenance at all, for the same
pcompos -hreason. And Table 3 step 9 ispcomb -s factorwith a numeric calibration factor where the app requires a hand-written.cal.Note
c70ad65adds the upstream repositories for Radiance and panlib to the README's source column. Unrelated to #241 and happy to split it out if you would rather it went separately.🤖 Generated with Claude Code