Issue #87 shipped a multi-pass averaging UI, but every chartread invocation still writes {basename}.ti3. Pass 2 overwrites pass 1 on disk. run_average is then invoked with duplicate identical filenames (["foo.ti3", "foo.ti3"] → foo.ti3), so Argyll average never sees two distinct measurement sets.
This is a functional failure of the closed#87 acceptance criteria. The average binary is bundled under src-tauri/argyll/. The defect is exclusively file identity.
chartread process id is always chartread_${basename}.
run_chartread always uses config.basename as the Argyll inoutfile.
#btnMeasureAnotherSheet immediately re-clicks Start Measurement against the same basename.
Backend build_average_args is already correct:
-v infile1.ti3 infile2.ti3 outfile.ti3
Proposed solution
Do not change the chartread inoutfile to _passN during the read. printtarg / .ti2 still use basename. Always read as basename, then snapshot the .ti3 after a successful exit.
After each successful chartread exit (code 0):
Copy {cwd}/{basename}.ti3 to {cwd}/{basename}_pass{N}.ti3 where N is 1-based and never reused in the session.
Record that unique filename in recordedPasses.
Keep {basename}.ti3 as the canonical Stage 4 input:
Single pass: copy basename_pass1.ti3 → basename.ti3 (or keep the original and also keep a pass copy).
Multi-pass: average -v basename_pass1.ti3 basename_pass2.ti3 … basename.ti3.
Guard against a second chartread_${basename} spawn while the previous child is still in the process map. Measure Another Sheet must wait until the previous process has exited and the pass file has been snapshotted.
Copies {cwd}/{basename}.ti3 → {cwd}/{basename}_pass{N}.ti3 and returns the new filename (relative, so average args stay relative to cwd).
Do not implement this via invoke('read_file_base64') plus a rewrite. It is a filesystem copy.
Files
src/js/chartread.js — pass snapshot, average inputs, button enablement
src-tauri/src/commands.rs — optional snapshot_ti3 command and unit test
src-tauri/src/lib.rs — register the command if added
Acceptance criteria
Two successive sheet measurements produce two distinct files on disk (*_pass1.ti3 and *_pass2.ti3) whose contents differ if the readings differ.
average -v {basename}_pass1.ti3 {basename}_pass2.ti3 {basename}.ti3 is the actual argv (unit test on build_average_args plus a UI log line).
After a successful average, {basename}.ti3 exists and is the file Stage 4 / verify_stage_artefacts uses.
A single-pass Finish still yields {basename}.ti3 and does not invoke average.
Re-running pass 2 does not destroy pass 1.
Manual test note on the PR: Windows and Linux, two dummy or mock passes (chartread.mock if present).
Dependencies
None. Do this before any other Stage 3 work. Implement with issue 02 in the same PR.
# bug(stage3): Multi-pass averaging overwrites the same .ti3 file
| Field | Value |
|---|---|
| Labels | `Kind/Bug`, `Priority/Critical`, `Reviewed/Confirmed` |
| Priority | Critical |
| Milestone | v0.3.3 hotfix |
| Related | #87 (closed, incomplete) |
| Branch | `fix/averaging-unique-ti3` (from `development`) |
| Pair with | [02-stage4-unlock.md](02-stage4-unlock.md) — same PR |
## Description
Issue #87 shipped a multi-pass averaging UI, but every `chartread` invocation still writes `{basename}.ti3`. Pass 2 overwrites pass 1 on disk. `run_average` is then invoked with duplicate identical filenames (`["foo.ti3", "foo.ti3"]` → `foo.ti3`), so Argyll `average` never sees two distinct measurement sets.
This is a functional failure of the closed #87 acceptance criteria. The `average` binary **is** bundled under `src-tauri/argyll/`. The defect is exclusively file identity.
## Current behaviour
On `process:exit` code 0 in `src/js/chartread.js`:
```javascript
recordedPasses.push({
index: currentPassIndex,
filename: `${basename}.ti3`,
time: new Date().toLocaleTimeString(),
});
```
- `chartread` process id is always `chartread_${basename}`.
- `run_chartread` always uses `config.basename` as the Argyll inoutfile.
- `#btnMeasureAnotherSheet` immediately re-clicks Start Measurement against the same basename.
Backend `build_average_args` is already correct:
```text
-v infile1.ti3 infile2.ti3 outfile.ti3
```
## Proposed solution
Do **not** change the `chartread` inoutfile to `_passN` during the read. `printtarg` / `.ti2` still use `basename`. Always read as `basename`, then snapshot the `.ti3` after a successful exit.
After each successful `chartread` exit (code 0):
1. Copy `{cwd}/{basename}.ti3` to `{cwd}/{basename}_pass{N}.ti3` where N is 1-based and never reused in the session.
2. Record that unique filename in `recordedPasses`.
3. Keep `{basename}.ti3` as the **canonical** Stage 4 input:
- Single pass: copy `basename_pass1.ti3` → `basename.ti3` (or keep the original and also keep a pass copy).
- Multi-pass: `average -v basename_pass1.ti3 basename_pass2.ti3 … basename.ti3`.
4. Guard against a second `chartread_${basename}` spawn while the previous child is still in the process map. **Measure Another Sheet** must wait until the previous process has exited and the pass file has been snapshotted.
Preferred helper (Rust, atomic, cross-platform):
```text
snapshot_ti3(cwd, basename, pass_index) -> Result<String>
```
Copies `{cwd}/{basename}.ti3` → `{cwd}/{basename}_pass{N}.ti3` and returns the new filename (relative, so `average` args stay relative to cwd).
Do not implement this via `invoke('read_file_base64')` plus a rewrite. It is a filesystem copy.
## Files
- `src/js/chartread.js` — pass snapshot, average inputs, button enablement
- `src-tauri/src/commands.rs` — optional `snapshot_ti3` command and unit test
- `src-tauri/src/lib.rs` — register the command if added
## Acceptance criteria
- [ ] Two successive sheet measurements produce two distinct files on disk (`*_pass1.ti3` and `*_pass2.ti3`) whose contents differ if the readings differ.
- [ ] `average -v {basename}_pass1.ti3 {basename}_pass2.ti3 {basename}.ti3` is the actual argv (unit test on `build_average_args` plus a UI log line).
- [ ] After a successful average, `{basename}.ti3` exists and is the file Stage 4 / `verify_stage_artefacts` uses.
- [ ] A single-pass Finish still yields `{basename}.ti3` and does not invoke `average`.
- [ ] Re-running pass 2 does not destroy pass 1.
- [ ] Manual test note on the PR: Windows and Linux, two dummy or mock passes (`chartread.mock` if present).
## Dependencies
None. Do this before any other Stage 3 work. Implement with issue 02 in the same PR.
gronod
added this to the v0.3.3 Hot fixes milestone 2026-08-26 14:36:41 +01:00
Resolved and verified in milestone v0.3.3 Hot fixes via PR #118 (Fix/averaging unique ti3).
Resolution Summary:
Implemented pass snapshotting logic to ensure each measurement iteration creates a unique {basename}_pass{N}.ti3 file.
Prevented premature overwriting of {basename}.ti3.
Updated Argyll average invocation args to reference all distinct pass files before generating the final canonical {basename}.ti3 output for Stage 4.
Resolved and verified in milestone `v0.3.3 Hot fixes` via PR #118 (`Fix/averaging unique ti3`).
### Resolution Summary:
- Implemented pass snapshotting logic to ensure each measurement iteration creates a unique `{basename}_pass{N}.ti3` file.
- Prevented premature overwriting of `{basename}.ti3`.
- Updated Argyll `average` invocation args to reference all distinct pass files before generating the final canonical `{basename}.ti3` output for Stage 4.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
bug(stage3): Multi-pass averaging overwrites the same .ti3 file
Kind/Bug,Priority/Critical,Reviewed/Confirmedfix/averaging-unique-ti3(fromdevelopment)Description
Issue #87 shipped a multi-pass averaging UI, but every
chartreadinvocation still writes{basename}.ti3. Pass 2 overwrites pass 1 on disk.run_averageis then invoked with duplicate identical filenames (["foo.ti3", "foo.ti3"]→foo.ti3), so Argyllaveragenever sees two distinct measurement sets.This is a functional failure of the closed #87 acceptance criteria. The
averagebinary is bundled undersrc-tauri/argyll/. The defect is exclusively file identity.Current behaviour
On
process:exitcode 0 insrc/js/chartread.js:chartreadprocess id is alwayschartread_${basename}.run_chartreadalways usesconfig.basenameas the Argyll inoutfile.#btnMeasureAnotherSheetimmediately re-clicks Start Measurement against the same basename.Backend
build_average_argsis already correct:Proposed solution
Do not change the
chartreadinoutfile to_passNduring the read.printtarg/.ti2still usebasename. Always read asbasename, then snapshot the.ti3after a successful exit.After each successful
chartreadexit (code 0):{cwd}/{basename}.ti3to{cwd}/{basename}_pass{N}.ti3where N is 1-based and never reused in the session.recordedPasses.{basename}.ti3as the canonical Stage 4 input:basename_pass1.ti3→basename.ti3(or keep the original and also keep a pass copy).average -v basename_pass1.ti3 basename_pass2.ti3 … basename.ti3.chartread_${basename}spawn while the previous child is still in the process map. Measure Another Sheet must wait until the previous process has exited and the pass file has been snapshotted.Preferred helper (Rust, atomic, cross-platform):
Copies
{cwd}/{basename}.ti3→{cwd}/{basename}_pass{N}.ti3and returns the new filename (relative, soaverageargs stay relative to cwd).Do not implement this via
invoke('read_file_base64')plus a rewrite. It is a filesystem copy.Files
src/js/chartread.js— pass snapshot, average inputs, button enablementsrc-tauri/src/commands.rs— optionalsnapshot_ti3command and unit testsrc-tauri/src/lib.rs— register the command if addedAcceptance criteria
*_pass1.ti3and*_pass2.ti3) whose contents differ if the readings differ.average -v {basename}_pass1.ti3 {basename}_pass2.ti3 {basename}.ti3is the actual argv (unit test onbuild_average_argsplus a UI log line).{basename}.ti3exists and is the file Stage 4 /verify_stage_artefactsuses.{basename}.ti3and does not invokeaverage.chartread.mockif present).Dependencies
None. Do this before any other Stage 3 work. Implement with issue 02 in the same PR.
Resolved and verified in milestone
v0.3.3 Hot fixesvia PR #118 (Fix/averaging unique ti3).Resolution Summary:
{basename}_pass{N}.ti3file.{basename}.ti3.averageinvocation args to reference all distinct pass files before generating the final canonical{basename}.ti3output for Stage 4.