Skip to content

Persist IO FAT progress and export portable handover packages - #114

Merged
masarray merged 11 commits into
mainfrom
agent/io-testing-persistence-handover
Jul 28, 2026
Merged

Persist IO FAT progress and export portable handover packages#114
masarray merged 11 commits into
mainfrom
agent/io-testing-persistence-handover

Conversation

@masarray

@masarray masarray commented Jul 28, 2026

Copy link
Copy Markdown
Owner

Purpose

Make IO List FAT work durable on one laptop and portable across laptops without weakening the existing OFF → ON → OFF evidence rules.

Local persistence

  • one local workspace keyed by ProjectId and source-workbook SHA-256
  • atomic project.snapshot.json plus a local copy of the approved XLSX
  • debounced autosave after scope, result, evidence, or session-state changes
  • manual Save Progress checkpoint and final save when the FAT window closes
  • reopening the same workbook restores PASS, REVIEW, FAIL, ON/OFF evidence, attempts, and remaining scope

Safe continuation

  • completed signals remain visible with their evidence and result
  • restored completed signals are unchecked from the next session so they are not silently reset
  • operators can explicitly check them again when a retest is required
  • unfinished points require a new live baseline
  • saved ON without OFF becomes REVIEW because continuity across application/laptop boundaries cannot be proven
  • saved live value, quality, source, sequence, and connection generation are discarded and reacquired

Portable handover

Adds .arsas-iofat packages containing:

  • manifest.json
  • project.snapshot.json
  • the original XLSX workbook
  • sealed evidence journals
  • report/IO-FAT-Report.html
  • handover README

A package can be opened from Open FAT Handover Package on another ARSAS laptop to continue the remaining scope. Its A4-landscape HTML report can be opened in a browser and printed to PDF without the original testing laptop.

Integrity and safety

  • export is blocked while an IED session is active
  • included evidence journals must pass SHA-256 hash-chain verification
  • manifest SHA-256 checks protect snapshot, workbook, and evidence files
  • import enforces package/entry size limits, entry-count limits, safe paths, schema version, project identity, and workbook hash
  • evidence filename collisions preserve both files
  • snapshots and packages are written through temporary files and atomic replacement
  • no IEC 61850 command or configuration write is introduced

UI

  • Save Progress
  • Export Handover
  • autosave status and last-saved timestamp
  • local snapshot path
  • first-run FAT card supports both new workbook import and portable package import

Regression coverage

  • completed local progress restores on reopening the same workbook
  • partial ON becomes REVIEW after continuity is lost
  • completed points are excluded from continuation scope
  • portable package round-trip restores results and source workbook
  • printable report is included
  • tampered package snapshot is rejected
  • UI contracts protect autosave, handover actions, and OneWay read-only bindings

Automated validation at head a272f028701f55e016c307abdbb04bc87e0a2d26

  • Validate IO List Testing run 25 — success
    • focused WPF/application build
    • all IO List import, session, evidence, persistence, package, and UI regression tests
  • Build ARSAS run 758 — success
    • complete solution build and application regression suite
    • portable Windows publication and artifact upload
  • Validate SV evidence bundles run 57 — success
    • deterministic Sampled Values evidence regression remains intact

Direct result writing back into XLSX and formal signed PDF generation remain follow-up phases.

@masarray
masarray marked this pull request as ready for review July 28, 2026 10:35
@masarray
masarray merged commit 9f81c8a into main Jul 28, 2026
3 checks passed

@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: a272f02870

ℹ️ 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".

}

project.InitializeRuntimeNotifications();
var evidenceDirectory = Path.Combine(evidenceRoot, SanitizePathPart(project.ProjectId));

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 Scope exported evidence to the workbook hash

When two workbook revisions reuse the same ProjectId but have different SHA-256 hashes, their snapshots are separated into hash-qualified directories while both sessions write evidence under the same project-ID-only directory. Exporting either revision therefore enumerates and packages journals belonging to the other workbook, producing a handover with unrelated FAT evidence. Include the workbook hash in the evidence directory or filter each journal by its recorded workbook hash before export.

Useful? React with 👍 / 👎.

throw new InvalidDataException($"Imported evidence failed hash-chain verification: {verification.Error}");
}

await WriteFileAtomicAsync(Path.Combine(localDirectory, "project.snapshot.json"), snapshotBytes, cancellationToken).ConfigureAwait(false);

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 Preserve newer local progress when importing a package

When a handover package targets a project and workbook hash that already exist locally, this unconditionally replaces project.snapshot.json without comparing SavedAtUtc, prompting, or retaining a backup. Opening an older package for the same project can therefore silently discard newer autosaved results, and subsequent workbook opens restore the stale imported state. Detect the collision and merge, prompt, or preserve the existing snapshot before replacement.

Useful? React with 👍 / 👎.

Comment on lines +169 to +173
foreach (var evidence in manifest.EvidenceFiles)
{
cancellationToken.ThrowIfCancellationRequested();
var entry = RequiredEntry(archive, evidence.Entry);
var bytes = await ReadEntryAsync(entry, 100 * 1024 * 1024, cancellationToken).ConfigureAwait(false);

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 Cap the package's total uncompressed evidence size

For a highly compressed or malicious package, the 500 MB archive-size limit and 100 MB per-entry limit still permit thousands of evidence entries to expand onto disk; the existing 10,000-entry cap allows an aggregate approaching 1 TB. Since every entry is read and written without a cumulative uncompressed-size budget, opening such a package can exhaust disk space. Track and reject an aggregate uncompressed size before importing entries.

Useful? React with 👍 / 👎.

Comment on lines +144 to +145
if (!manifest.PackageVersion.Equals(PackageVersion, StringComparison.Ordinal))
throw new InvalidDataException($"Unsupported IO FAT package version '{manifest.PackageVersion}'.");

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 Reject manifests with missing required strings

When manifest.json is syntactically valid but omits packageVersion or sets it to null, deserialization succeeds and this instance call throws NullReferenceException. The launcher only catches the documented import exceptions, so selecting such a malformed or partially written package can escape the async click handler instead of showing the normal import-failed dialog. Validate required manifest members and throw InvalidDataException before dereferencing them.

Useful? React with 👍 / 👎.

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