Skip to content

Keryx requirements-to-test traceability ​

Approved: 17 September 2026

This document is the P4-01 coverage packet for Keryx for SafeCall. It lists launch-relevant requirements, maps each to evidence class and current path, defines per-commit / per-release / per-launch cadence, and records regression ownership.

It does not claim that VoiceOver/TalkBack or OEM soak passed, that store-track testers approved a freeze build, or that #112 signed compile/SBOM/Swift CI exists. An unsigned GitLab CI first slice is documented in ci.md. An in-repo accessibility slice is accessibility.md.

Purpose and non-goals ​

Purpose

  • Give G3 (client readiness) a single requirements-to-test map.
  • Record what is already proven in-repo versus what remaining GitLab items own.
  • Tell #112 which checks to run. The unsigned first slice is ci.md; signed compile, SBOM, and Swift remain on that issue.
  • Open implementation issues only for automated gaps that existing P4/P5 items do not already own.

Non-goals

  • Adding signed compile, SBOM, or a Mac runner (#112 residual; unsigned YAML is ci.md).
  • Completing VoiceOver/TalkBack or a WCAG pass/fail (#116 closed 20 September 2026 without an AT/WCAG claim; in-repo denial UI is accessibility.md).
  • Running the physical OEM / killed-state / Doze matrix (#117).
  • Proving upgrade, reboot, cache, rescan, or server migration (#118).
  • Closing the phone-side guide (#121), client pilot (#122), or TestFlight / Play Internal soak (#124).
  • Treating Nav bun test:contracts as Keryx QR coverage.

Approved decisions ​

TopicApproved position
Owner and approverThomas Minitsios, 17 September 2026 (P0-05 RACI)
Policy sourceExisting GTM packets only; this close does not invent product, privacy, or store rules
Evidence classesautomated (Dart / Bun / Playwright), native (Kotlin / Swift unit), manual-device, soak, pilot, external
G3 automated barPriority slice below must have automated or native evidence, or an open implementation issue, before go/no-go
CI consumptionUnsigned first slice (ci.md) runs the per-commit set plus bun test:keryx-native --android. Signed compile, SBOM, and Swift remain #112
Secrets in evidenceGitLab notes and NAS evidence/keryx/ hold checksums and pass/fail only. No QR photos, tk_… tokens, or client names

Evidence classes ​

ClassMeaningTypical command or record
automatedHeadless, repeatable, no phonecd keryx && flutter test; cd server && bun test; bun test:e2e (mobile-access.spec.ts)
nativeVendored ntfy JVM / XCTestbun test:keryx-native (both); --android / --ios slices
manual-deviceOperator on a supported phone/tabletmanual-testing.md §2.7; #117/#118 scripts
soakStore-installed candidate over duration#124 / #117 long-running rows
pilotNamed client administrators#122
externalCounsel, store forms, live HTTPS#107 deploy, #125 console, OA-legal-cs

Current inventory (17 September 2026) ​

GitLab #115 still said “three Dart test files.” The tree is larger:

SurfaceWhat exists
Dart (keryx/test/)11 files: QR v2 parse/reject, config store (locale survives reset), notification store and archive filters, alert state/policy, envelope parse against shared fixtures, recover, notification-open, delivery-log redaction, fuzzy search, widget/l10n (onboarding, pause, jargon hiding, Czech ARB keys, category cards)
Shared fixtureslib/src/ntfy_alert_envelope.fixtures.json (Dart + TypeScript parsers); lib/src/keryx_qr.fixtures.json (Dart KeryxConfig.fromQrPayload + server KeryxQrPayloadSchema)
NativeKotlin under keryx/vendor/ntfy/android/src/test/kotlin/io/karsmiths/ntfy/; Swift under keryx/vendor/ntfy/ios/ntfy/Tests/ntfyTests/ (policy gate, delivery, FCM wake, poll catch-up, notification open, delivery log, tags)
Serverserver/tests/api/keryx-qr.test.ts, server/tests/keryx_ntfy.test.ts
Web E2Eweb/tests/e2e/mobile-access.spec.ts: operational categories default on, admin features collapsed, v2 QR, enlarge without subscribe_auth in page text
Manualmanual-testing.md §2.7 Mobile Access
Releasebun run release:keryx runs flutter analyze + flutter test; --contracts runs Ariadne bun test:contracts, not a Keryx QR golden
Native harnessbun test:keryx-native (#160 closed). --android is the Linux CI slice
CIUnsigned first slice in .gitlab-ci.yml / ci.md: keryx:dart, keryx:server, keryx:native-android. #112 stays open for signed compile, SBOM, Swift/macOS

Requirement catalog and evidence map ​

Owners are GitLab work items. G3 means the row must have recorded evidence (or an accepted risk in the roadmap) before gate G3. Closing this packet does not complete G3.

Product ​

IDRequirementSourceClassCurrent evidenceGapOwnerG3
P-01Administrator-only companion; no generic end-user productP0-01 / strategyautomated + manual-device + pilotWidget onboarding copy; English app-onboarding-guide.mdRepresentative walk OA-121-app-walk (done 19 September 2026); Czech/live web remain on #121#121 / #122Yes
P-02Friendly alert categories; QR hides server URL, topic IDs, and subscribe_auth from ordinary UIP0-03, P1-02automatedWidget: subscribed cards hide https://notify.example/ and TST000-sos; standard notification detail hides Topic / ntfy message ID / raw JSON. E2E: enlarge QR does not expose subscribe_authWeb E2E does not assert ordinary Mobile Access copy avoids “ntfy” / “topic” jargon#160Yes
P-03Scan-on-own-device QR; no manual ntfy configurationP0-03automated + manual-deviceWidget shows Scan QR code when unconfigured; E2E generates v2 QR from Mobile AccessNo test drives mobile_scanner or injects a scanned payload through the camera callback#160 mocked ingest; physical scan #117 / OA-121-app-walkYes
P-04English + Czech UI parity for Keryx and admin provisioningP0-02 / localizationautomatedCzech ARB key parity; Czech onboarding and stateful-card widgets; web catalogs from #108Czech Mobile Access E2E labels; live Czech HTTPS #107 residual#160 E2E CS; #121 leftover webYes
P-05Pause / resume is local and obviousP1-10 / #121automatedSettings pause switch widget; home shows Notifications are pausedPersistence across process death is #118#118Yes
P-06Reset App clears config and history, not the language preference; does not revoke the ntfy tokenP1-05, #120/#121automated + externalkeryx_config_store_test locale survives clear(); pause resets to falseNo widget for the Reset App confirmation path; revoke remains SafeCall/ntfy host#160; revoke is host-side (#120 handbook)Yes
P-07Debug / test categories stay behind admin warning; ordinary QR has no debugP1-01 A12 / P1-02automatedE2E: admin features collapsed; Test and debug unchecked; warning modal on Test or debugNone for this control#115 inventory (covered)Yes

Security and privacy ​

IDRequirementSourceClassCurrent evidenceGapOwnerG3
S-01HTTPS-only ntfy URL; reject HTTPP1-02automatedDart rejects http server URLs; server keryx_ntfy_https_required testsNone for parser#115 inventory (covered)Yes
S-02QR is safecall.keryx.ntfy.v2 with subscribe_auth; reject v1 and non-Keryx typesP1-02 / versioningautomatedShared golden lib/src/keryx_qr.fixtures.json: Dart fromQrPayload plus Valibot KeryxQrPayloadSchema; POST /keryx/qr key-set must match the golden; E2E asserts payload typeMinted subscribe_auth / token_id / created_at are dynamic (not byte-for-byte). Mocked scan ingest is #160#159 closed 17 September 2026Yes
S-03Subscribe token is read-only, per QR; publish token never in QRP1-02automatedServer mint/revoke tests; E2E enlarge redactionPhysical token ACL on client ntfy#119 (closed dual-run); deny-all OA-119-ntfy-cutover laterYes
S-04Ordinary UI never shows subscribe_auth or raw topic IDsP1-05automatedWidget jargon hiding; delivery-log redaction testCamera-result / paste error paths#160Yes
S-05History cap 200; tombstones suppress cache reintroductionP1-05automatedNotification store cap, archive, deleted-not-reintroduced, recover testsUpgrade of stored JSON shape#118Yes
S-06Full lock-screen title+body is the launch policy (disclose, do not silently redact)P1-05 / P1-07 / P1-08external + soakPolicy packets; first-device swipe-away on 1.2.1 (8)Nutrition label / Data Safety paste; OEM lock-screen variants#125 / #117Yes
S-07No analytics or crash SDK at launchP0-05 / P1-09automated + externaldependency-ownership inventory; pubspec has no analytics SDKMachine SBOM / CI advisory gate#112Yes
S-08Debug metadata only when debug_accessP1-05automatedWidget standard vs debug notification detailNone material#115 inventory (covered)Yes

Platform and delivery ​

IDRequirementSourceClassCurrent evidenceGapOwnerG3
D-01No perpetual Android dataSync FGSP1-03native + manual-deviceArchitecture + code note 13 September 2026; Kotlin tests exist for wake/deliveryNative tests not in flutter test; physical FGS absence#160 harness; #117Yes
D-02Android FCM poll_request / message → authenticated poll → local notificationP1-03native + soakKotlin NtfyFirebaseWakeTest, NtfyDeliveryTest; operator first-device proofHarness + OEM/Doze/killed-state matrix#160; #117 / #124Yes
D-03iOS APNs / poll_request catch-up ingest; generic wake does not spam localsP1-04native + soakSwift NtfyPollCatchUpTests, NtfyRemoteWakeTests, NtfyNotificationOpenTests; Dart open/recover testsHarness + physical iPhone matrix#160; #117 / #124Yes
D-04Delivery is supplemental; Focus/DND/force-stop/OEM battery can suppressP1-10manual-device + soakClaims packet; handbooksRecorded device matrix#117 / #121Yes
D-05Foreground stream while the app is open; pause stops presentationP1-03 / #121automated + manual-devicePause widgets; stream service used in widget harnessLive SSE against ntfy#117Yes
D-06Camera + notification permission denial recovers via OS SettingsP0-04automated + manual-deviceWidget overlays + Settings CTA (accessibility.md)Physical first/permanent deny accepted out of scope 20 September 2026#116 closedNo
D-07iOS 17+ / Android API 31+; phone and tablet orientationsP0-04manual-device + externalPolicy; source floors may still be lowerAlign source + listings; physical orientations#117 / #125Yes
D-08Tier 1 Pixel/AOSP + Samsung; Tier 2 only with evidenceP0-04soakNoneDevice list and results#117Yes

Store, support, and compatibility ​

IDRequirementSourceClassCurrent evidenceGapOwnerG3
C-01WCAG 2.2 AA critical flows; VoiceOver/TalkBack; 200% text; contrast; 44 pt / 48 dpP0-04manual-device + automatedCategory-card semantics; scanner/scan/pause/reset names; 200% widget tests (accessibility.md)AT/WCAG device evidence declined 20 September 2026 (trained hospital/admin operators; no conformance claim)#116 closedNo
C-02Czech Republic only; Business; not a medical deviceP0-02 / P1-10externalListing-copy packetConsole paste#125Yes
C-03SafeCall-channel support; no standalone Keryx SLAP0-05external + pilotHandbooks #120/#121OA-121-app-walk; pilot#121 / #122Yes
C-04Store-like upgrade preserves config and history when QR schema is unchangedversioningmanual-deviceNoneUpgrade / package-replace#118Yes
C-05Reboot, offline, cache expiry, network change, clock skew, rescan, server migrationP4-04manual-deviceRecover helpers unit-testedPhysical and store-track#118 / #124Yes
C-06Sideload downgrade unsupported; stores do not roll backversioningexternalPolicy onlySupport wording at launch#121 / #130No
C-07Reviewer QR + isolated HTTPS ntfyP5-01externalIn-repo packet reviewer-environment.mdLive ACL, {public_base}/keryx/review, durable QR, off-LAN proof#123 residual; OA-123-review-hostGate G4
C-08Client administrators provision without transport helpP4-08pilotHandbook walk OA-120-admin-walk (install+scan)Full pilot scenarios#122Yes

Priority slice (GitLab #115 AC) ​

These rows are the issue’s named priorities. Cadence below must keep them green or explicitly owned.

PriorityIDsAutomated/native todayResidual owner
QR / auth / versioningS-01, S-02, S-03, P-03Shared QR golden + Dart reject/parse; server mint key-set; E2E v2 typeMocked scan ingest #160; physical scan #117
Persistence / migrationS-05, P-06, C-04Store cap, tombstones, locale-survives-resetReset App widget #160; upgrade #118
Lifecycle / tombstonesS-05, D-05Store + alert policy + recover testsSoak #117/#124
Debug gatingP-07, S-08E2E warning; widget debug vs ordinary detailNone material
PermissionsD-06, C-01Denial overlays + Settings CTA + 200% tests (accessibility.md)AT/WCAG device evidence declined (#116 closed 20 September 2026)
NotificationsD-02, D-03, D-04, S-06Dart open/envelope; native tests on diskNative harness #160; #117
Native servicesD-01, D-02, D-03Kotlin + Swift unit tests + bun test:keryx-nativeLinux CI --android; Swift remains local / future macOS job (#112)
ContractsS-02, envelope fixturesShared alert body and QR payload goldens (lib/src/*.fixtures.json); cd keryx && flutter test + cd server && bun testNav test:contracts is out of scope

Cadence and retention ​

Per-commit (local and unsigned GitLab CI) ​

Run when touching keryx/**, vendored ntfy, SafeCall Keryx QR, or alert envelopes:

bash
cd keryx && flutter analyze && flutter test
cd server && bun test

GitLab (keryx:dart, keryx:server, keryx:native-android) also runs bun test:keryx-native --android. Packet: ci.md.

Also run bun test:e2e (or at least mobile-access.spec.ts) when web/** or the Keryx QR HTTP contract changes.

cd keryx && flutter test and cd server && bun test cover S-02 (shared QR golden parse + mint key-set). Do not use bun test:contracts as Keryx evidence. That orchestrator writes SafeCall Nav fixtures and runs ariadne/test/contract_test.dart.

Per-release (bun run release:keryx / bun keryx) ​

  1. Per-commit set above (flutter analyze + flutter test already in the script).
  2. Signed AAB + IPA verify (verify:keryx-aab, verify:keryx-ipa).
  3. Native ntfy unit tests: bun test:keryx-native (Android + Swift locally). Linux CI already ran --android.
  4. Optional --contracts remains Nav-only; record it as Nav, not Keryx QR.

Copy keryx/build/release-evidence/*.txt to NAS evidence/keryx/.

Per-launch / G3 ​

ItemOwner
Accessibility and permission recovery#116 closed 20 September 2026 (no WCAG/AT claim)
Real-device / OEM / delivery matrix#117
Upgrade / reboot / cache / rescan#118
Client pilot#122
Store-track soak and tester approval#124

G4 reviewer environment is #123 (not G3). In-repo packet: reviewer-environment.md. Live host is OA-123-review-host. Console paste is #125.

Retention ​

RecordWhere
Automated logs / checksumsGitLab notes on the owning issue; NAS evidence/keryx/
Device matrix resultsNAS evidence/keryx/ + #117/#118/#124 notes
Release evidence templatetemplates/release-record.md “CI and test evidence” line

Never put QR photographs, tk_… values, keystore passwords, or real client identifiers in git, chat, or tickets.

Follow-up issues ​

Do not file duplicates of #112, #116, #117, #118, #121, #122, #123, or #124.

Filed with this close:

IssueScopeLaunch blocker
#159Done 17 September 2026. Shared lib/src/keryx_qr.fixtures.json: Dart parse + Valibot + minted key-set. Nav test:contracts is not Keryx.Closed — S-02 / contracts slice
#160Done 17 September 2026. Mocked QR ingest, Reset App widget, ordinary Mobile Access jargon E2E, bun test:keryx-native. Linux CI consumes --android (ci.md)Closed — P-02, P-03, P-06, native harness

Physical OEM delivery and upgrade remain on #117/#118.

Regression ownership ​

Change typeWho must keep the map green
Keryx Dart/UIAuthor runs flutter analyze + flutter test; extends widget tests for new ordinary-admin strings
QR schema / /keryx/qrAuthor updates server tests and lib/src/keryx_qr.fixtures.json (Dart parse + Valibot + minted key-set)
Alert envelopeAuthor updates lib/src/ntfy_alert_envelope.fixtures.json plus Dart and TypeScript parse tests
Vendored ntfy nativeAuthor extends Kotlin/Swift tests; runs bun test:keryx-native (or --android / --ios)
Delivery architecture#117 evidence before changing claims or Play/ASC wording
Support copy#120/#121 packets; OA-121-app-walk does not close #121

Thomas Minitsios is Accountable (P0-05). Unsigned CI enforces dart + server + Android native; remaining #112 ACs stay on that issue.

Evidence handoff ​

Remaining workOwner
Protected CI signed compile, SBOM, license/advisory, Swift/macOS#112 residual; unsigned slice is ci.md
Accessibility + permission denial#116 closed 20 September 2026; in-repo slice is accessibility.md. No WCAG claim
Physical OEM / killed-state / soak delivery#117
Upgrade / reboot / cache / reprovision#118
Phone-side guide leftovers (walk, live web, Czech, screenshots)#121
Client pilot#122
Reviewer QR environment#123 residual; in-repo slice is reviewer-environment.md; OA-123-review-host
Store-track soak#124
Privacy-safe monitoring and support intake#132 residual; in-repo slice is monitoring-intake.md; OA-132-intake-drill done
Hotfix, stop-rollout, and rejection#129 residual; in-repo slice is hotfix-runbook.md; OA-129-hotfix-tabletop done

Launch posture ​

Closing #115 approves the map and cadence. It does not claim:

  • #112 signed compile, SBOM, or Swift CI (unsigned YAML is ci.md);
  • VoiceOver, TalkBack, or WCAG pass (in-repo denial UI is accessibility.md; #116 closed 20 September 2026 without that claim);
  • OEM / Doze / killed-state soak;
  • store-track tester approval or a frozen build number;
  • that #160 was still open (closed 17 September 2026). #159 QR goldens are in-repo.