Pilot readiness v1.2.1: fix five release blockers, add IOTA notification pipeline and Epic chart filing - #224
Merged
Merged
Conversation
Both specs logged in with TRANSTRACK_INITIAL_ADMIN_PASSWORD but never passed it to the app they launched. The variable was only ever set at the CI workflow level, so on a developer machine the app generated a random setup token instead and every step after login failed. The suite was therefore effectively CI-only, which is how a broken production build reached the repository unnoticed. Each spec now owns the password and passes it into the launched app, so local and CI runs behave identically. The value only seeds a throwaway database in a temporary directory. Co-authored-by: Cursor <cursoragent@cursor.com>
PRAGMA table_info() returns an empty result for a table that does not exist, so the existing "does this column already exist" guard passed and the follow-on ALTER TABLE then failed. Eight migrations shared the defect. Fixed systemically with addColumn()/tableExists() helpers rather than patching only the migration that happened to surface it, and ehrMigration.test.cjs is rewritten to be version-agnostic so it exercises the whole migration chain against a partial database instead of asserting against a pinned schema version. Also adds the CMS IOTA evidence tables (migration 17) and the notice body column (migration 18). Migration 17 stored only a content hash, on the theory that a deterministic generator makes the body reproducible; that holds only until a centre edits its template, leaving no way to reprint a filed notice. The hash stays frozen by trigger and authoritative, so an altered body remains detectable. Co-authored-by: Cursor <cursoragent@cursor.com>
A migration failing partway left the database partially upgraded with no restore point, which is the worst outcome available at a pilot site: the data is intact but unusable, and recovery depends on whatever backup the site happened to take. runMigrationsSafely() copies the database before any pending migration runs, verifies the copy, and fails closed if it cannot be made. The restore path is surfaced on failure, and older pre-migration copies are pruned so the safety net does not grow without bound. Co-authored-by: Cursor <cursoragent@cursor.com>
The pilot runbook directed administrators to a System Health screen and a support bundle that did not exist; the only way to read a health snapshot was to call the IPC method from DevTools. Free text is withheld from the bundle by default rather than scrubbed. Redacting names out of prose is not reliable, and a bundle that claims to be PHI-free while leaking a name in a log message is worse than one that omits the message. Including full text is a separate explicit choice that labels the bundle as potentially containing PHI. phiRedaction.cjs becomes the single source of truth for redaction and logger.cjs now delegates to it. Its key list deliberately excludes a bare "name", which was over-redacting non-PHI values such as migration names. supportBundle.test.cjs feeds deliberately PHI-laden input and asserts no sensitive literal survives, while also asserting that diagnostic detail is preserved. Co-authored-by: Cursor <cursoragent@cursor.com>
The notice generator existed but nothing could reach it. This makes it a working obligation tracker for CMS IOTA Model 512.442(d). Recording a waitlist status transition whose impact blocks organ offers creates the notification obligation in the same operation, so a duty cannot exist without a tracked deadline. Where the centre's template is not yet configured the transition is still recorded and the obligation reported as unmet: the transition is what proves when the statutory clock started, and discarding it would be worse than a missing notice. Delivery tracking distinguishes a notice delivered late from one delivered on time and from one still open, because a surveyor asks different questions about each. Where a copy is owed to a dialysis facility or referring provider but none is on file, the obligation is flagged rather than shown as discharged. Chart filing (chartFiling.cjs) builds the FHIR R4 DocumentReference in three modes: dry_run builds and validates but transmits nothing, manual records a filing done by another route, and fhir_documentreference performs a real create. Outbound transmission requires an explicitly injected transport, so no configuration value alone can cause this offline-first application to reach an external endpoint. A body that no longer matches its frozen hash is refused. fhirPost deliberately does not retry, unlike fhirGet: Epic may persist a resource before a response fails, so replaying a create can file a second copy of a clinical document. The notification idempotency key travels in DocumentReference.identifier so a site can reconcile what was filed. system/DocumentReference.write is excluded from the default scopes. Requesting write access the application does not use fails customer security review. The Epic README documents the four steps a site's Epic team must complete and the HL7 MDM^T02 alternative that avoids write scopes entirely. Co-authored-by: Cursor <cursoragent@cursor.com>
The Disaster Recovery screen called createBackup, verifyBackup and restoreBackup, but the Electron client only exposed getStatus and listBackups. Those controls were inert in packaged builds: the feature looked present and did nothing. Adds the missing methods, registers the System Health and IOTA pages, and exposes both namespaces through the preload bridge. rendererBridgeCoverage.test.mjs prevents the whole class of defect by statically scanning every api.<namespace>.<method>() call in the renderer and checking it against the real preload surface, loaded by injecting a fake electron module rather than a hand-written stub that could drift. buildEntryIntegrity.test.mjs asserts index.html still loads the source entry module. It had been overwritten by a build artifact referencing a hashed bundle that no longer existed, which broke the production build outright. Co-authored-by: Cursor <cursoragent@cursor.com>
A high-severity React Router advisory was blocking the release gate. The advisory does not apply to this application, which uses a client-side HashRouter and no RSC mode, but the only options available were to take a risky major upgrade or to weaken the gate. Exceptions are now documented with a written justification, a CycloneDX VEX analysis state, and a review date. The gate fails on an undocumented finding, an expired exception, an exception that no longer matches any real finding, and a severity increase — so a waiver cannot quietly become permanent. auditExceptions.test.mjs drives the script with synthetic audit reports to prove each of those failure modes actually fails. Co-authored-by: Cursor <cursoragent@cursor.com>
The application version was a hardcoded literal in two IPC handlers, so it would drift from package.json on any bump and disagree with the integrity monitor, which reads app.getVersion() to decide whether an upgrade should re-baseline. Both now resolve from a single helper. The installer check accepted any version, so the gate passed happily against a binary built several releases earlier. A green gate sitting next to a stale artifact is a dangerous signal: the thing a customer installs would not be the thing that was tested. It now compares the installer version against package.json and says what to do about a mismatch. Registers the new suites, adds requirements TT-R129 through TT-R142 with traceability, and records the release in the changelog. Co-authored-by: Cursor <cursoragent@cursor.com>
|
Strix is installed on this repository, but we couldn't run this PR security review because this workspace's trial has ended. Add a card to resume code reviews here. |
Two CI failures on #224, both in the support bundle work. The build failed because supportBundle.test.cjs requires logger.cjs, which destructures `app` and `crashReporter` from `electron` at load time. CI installs with the Electron binary download skipped, so that require throws there while succeeding locally. The suite now injects an `electron` stub into the require cache the way healthCheck.test.cjs already does, keeping it a pure Node test. CodeQL flagged readLogTail for a check-then-use race and for opening a predictably-named temp path. The race is real rather than theoretical: logger.cjs rotates the files readLogTail reads, so the log can be renamed between existsSync and openSync. It now opens once and measures the descriptor with fstat, so size and bytes always come from the same file, and returns empty if the file went away instead of throwing. The temp path came from the tests themselves, which now work inside a per-run mkdtemp sandbox. Co-authored-by: Cursor <cursoragent@cursor.com>
5 tasks
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.
Summary
Pilot-readiness work for v1.2.1, split into eight reviewable commits. Five
release-blocking defects are fixed, three safety controls added, and the CMS
IOTA notification pipeline is wired end to end with an Epic chart-filing path.
All defects below were pre-existing on
main.Release blockers fixed
index.htmlhad been overwritten by abuild artifact referencing a hashed bundle that no longer existed, so the app
could not be packaged at all. Guarded by
buildEntryIntegrity.test.mjs.createBackup,verifyBackupandrestoreBackup; the Electron clientexposed only
getStatusandlistBackups. Now proven by a livebackup/restore round trip in the e2e critical path.
migration failed partway.
migrationSafety.cjstakes a verified backup firstand fails closed if it cannot.
PRAGMA table_info()returns empty for a missing table, so the column guard passed and the
ALTER TABLEthen failed. Fixed systemically, not per-migration.could not run locally — which is how the broken build reached the repo.
Added
could reach it. Recording a status transition that blocks organ offers now
creates a tracked obligation. Delivery distinguishes late from on-time from
open. An obligation owed to an unnamed dialysis facility is flagged rather
than shown as discharged.
DocumentReferenceindry_run,manual, or live mode. Outbound transmission requires an explicitly injectedtransport, so no configuration value alone lets this offline-first app reach
the network.
fhirPostdeliberately does not retry, because replaying acreate can file a second copy of a clinical document.
runbook, previously nonexistent. Free text is withheld by default rather than
scrubbed, since name redaction in prose is unreliable.
justification and an expiry; the gate fails on undocumented, expired, stale,
or newly escalated findings.
api.<ns>.<method>()callagainst the real preload surface, closing the class of defect that made
Disaster Recovery dead.
Version
Bumped to 1.2.1. The app version was a hardcoded literal in two IPC
handlers and would have drifted from
package.json, disagreeing with theintegrity monitor's upgrade check. The release gate also now refuses to pass
against an installer whose version does not match the source.
Test plan
backup → restore
npm audit— 0 unresolved (1 documented exception)certificate; the gate correctly blocks on the stale 1.2.0 binary)
Note for reviewers
Historical compliance records (OQ/PQ protocols, the internal security
assessment) still cite v1.2.0 deliberately — that is the version they were
executed against, and rewriting them would falsify validation evidence.