URS-077 · Representative certification gating per manufacturer
Title: Representative certification gating per manufacturer Date: 2026-09-29T02:30:59.420Z Duration: 79.8s Overall Status: ✅ PASS
User Requirement
Section titled “User Requirement”The system shall block representatives who have not completed a manufacturer’s required certification from restricted actions for that manufacturer (bill-only and order-request creation), enforcing the block server-side against crafted submissions, providing help text linking to the certification checklist, lifting the block immediately upon certification completion (timestamped document acknowledgment, fully correct knowledge check with unlimited retries, and signature), keeping other manufacturers unaffected, and exporting completion records that match the stored certification data.
Source: User_Requirement_Specifications_Vantis_DeviceFlow.xlsx — the run below proves the system meets this requirement.
Environment
Section titled “Environment”- Inbox URL: http://localhost:44167
- Database: localhost:36531/cc_repinbox_dev
Status: ✅ PASS
Test Steps
Section titled “Test Steps”Each step below corresponds to one Playwright test that ran sequentially. Screenshots and video recordings provide visual evidence of the UI behaviour.
1. Step 1a: Uncertified rep blocked — ✅ PASS
Section titled “1. Step 1a: Uncertified rep blocked — ✅ PASS”What this step proves:
An approved representative who has not completed the manufacturer’s required certification opens bill-only and order-request creation. The manufacturer is absent from both manufacturer pickers, and a ‘Certification required’ notice links the representative to the certification checklist. A second manufacturer without a certification requirement remains selectable.
Screenshots:


Video recording:
2. Step 1b: Trunk order requests allowed while pending — ✅ PASS
Section titled “2. Step 1b: Trunk order requests allowed while pending — ✅ PASS”Screenshots:

3. Step 2: Direct-post defense — ✅ PASS
Section titled “3. Step 2: Direct-post defense — ✅ PASS”What this step proves:
A crafted submission naming the certification-gated manufacturer is sent directly to the server, bypassing the picker. The server rejects it with a certification-required error and records the gate decision; no order or billing rows are created.
Screenshots:

Video recording:
4. Step 3: Certification completion — ✅ PASS
Section titled “4. Step 3: Certification completion — ✅ PASS”What this step proves:
The representative completes the certification behind the Part 11 signing re-authentication gate: the training flow stays hidden until a one-time signing code — issued on request and read back out of the email the system sent — is verified. The representative then opens and acknowledges the training document (timestamped server-side), fails the knowledge check once (server-side grading records the attempt and allows unlimited retries), passes at 100%, signs, and receives a certificate. The completion consumes the one-time code: replaying the identical completion request — with and without the verified code — is refused with a re-authentication demand and creates no second record. The manufacturer immediately reappears in the pickers.
Audit events generated by this step:
(Evidence scoped to step execution window: 2026-09-29T02:31:35.302Z → 2026-09-29T02:32:01.758Z)
| Time | Type | Action | User | Org | Performed |
|---|---|---|---|---|---|
| 2026-09-29 02:31:37Z | certification | signing_code_issued | marco.silva@corvetasurgical.com | Vantis | — |
| 2026-09-29 02:31:37Z | decision | certifications.signing_challenge.send_sms | marco.silva@corvetasurgical.com | Vantis | no |
| 2026-09-29 02:31:38Z | transactional_email | certification_signing_code | — | Vantis | — |
| 2026-09-29 02:31:40Z | certification | signing_code_verified | marco.silva@corvetasurgical.com | Vantis | — |
| 2026-09-29 02:31:50Z | decision | forms.grade_submission | marco.silva@corvetasurgical.com | Vantis | yes |
| 2026-09-29 02:31:54Z | certification | completed | marco.silva@corvetasurgical.com | Vantis | — |
| 2026-09-29 02:31:54Z | decision | certifications.complete_certification.issue_certificate | marco.silva@corvetasurgical.com | Vantis | yes |
| 2026-09-29 02:31:54Z | decision | certifications.complete_certification.mark_relationship_certified | marco.silva@corvetasurgical.com | Vantis | yes |
| 2026-09-29 02:31:54Z | organization_representation | status_change | marco.silva@corvetasurgical.com | Vantis | — |
| 2026-09-29 02:31:54Z | certification | signing_code_consumed | marco.silva@corvetasurgical.com | Vantis | — |
| 2026-09-29 02:31:54Z | user_log | rep_relationship_certified | marco.silva@corvetasurgical.com | Vantis | — |
| 2026-09-29 02:31:56Z | certification | signing_reauth_failed | marco.silva@corvetasurgical.com | Vantis | — |
| 2026-09-29 02:31:56Z | certification | signing_reauth_failed | marco.silva@corvetasurgical.com | Vantis | — |
| 2026-09-29 02:31:57Z | certification_certificate | issued | marco.silva@corvetasurgical.com | Vantis | — |
| 2026-09-29 02:31:57Z | certification_completion_record | issued | marco.silva@corvetasurgical.com | Vantis | — |
Emails triggered by this step:
(Evidence matched by declared name — step timing not available or no events fell in window)
Email 1: Your signing code for LiraLock Implant System Certification
Template: Your_signing_code_for_LiraLock_Implant_System_Certification

Screenshots:











Video recording:
5. Step 4: Per-manufacturer isolation — ✅ PASS
Section titled “5. Step 4: Per-manufacturer isolation — ✅ PASS”What this step proves:
The certification gate is scoped to the requiring manufacturer. The same representative could act for the second manufacturer before, during, and after certification; both manufacturers are selectable at the end of the run.
Screenshots:

Video recording:
6. Step 5: Completion-record export — ✅ PASS
Section titled “6. Step 5: Completion-record export — ✅ PASS”What this step proves:
The manufacturer exports certification completion records. The CSV download contains one row per completion record — including the newly certified representative — with version, completion date, and signature integrity hash, matching the database. A background PDF bundle job is requested and completes with a downloadable ZIP.
Audit events generated by this step:
(Evidence scoped to step execution window: 2026-09-29T02:32:14.013Z → 2026-09-29T02:32:16.870Z)
| Time | Type | Action | User | Org | Performed |
|---|---|---|---|---|---|
| 2026-09-29 02:32:14Z | export | export:certification-completion-records | mark.manufacturer@vantismedical.com | Vantis | — |
| 2026-09-29 02:32:16Z | bulk_export | requested | mark.manufacturer@vantismedical.com | Vantis | — |
Screenshots:



Video recording:
Database Validations
Section titled “Database Validations”The following SQL queries ran against the application database after the Playwright scenarios completed. Each query asserts a specific condition that proves the feature under test persisted its data correctly.
Certification block existed before it was lifted this run — ✅ PASS
Section titled “Certification block existed before it was lifted this run — ✅ PASS”Assertion: A pending_certification hold is recorded earlier in the run than the active completion transition
SELECT to_status, reason_code, created_at FROM organization_representation_request_status_changes WHERE relationship_id = $1 AND created_at > NOW() - INTERVAL '2 hours' ORDER BY created_at| to_status | reason_code | created_at |
|---|---|---|
| active | certification_completed | 2026-09-29T02:31:54.918Z |
Gate actively evaluated the representative during the run — ✅ PASS
Section titled “Gate actively evaluated the representative during the run — ✅ PASS”Assertion: The per-manufacturer action gate recorded decisions for the representative while ordering flows were exercised
SELECT action, payload->>'reason' AS reason FROM audit_events WHERE action = 'representatives.gate_rep_action' AND user_id = $1 AND created_at > NOW() - INTERVAL '2 hours' ORDER BY created_at DESC LIMIT 10| action | reason |
|---|---|
| representatives.gate_rep_action | no_blocking_relationship |
No order or billing rows were created while the representative was gated — ✅ PASS
Section titled “No order or billing rows were created while the representative was gated — ✅ PASS”Assertion: Zero order requests and zero billing orders exist for the gated representative
SELECT (SELECT COUNT(*) FROM order_requests WHERE requested_by_user_id = $1 AND created_at > NOW() - INTERVAL '2 hours') AS order_count, (SELECT COUNT(*) FROM billing_orders WHERE created_by_user_id = $1 AND created_at > NOW() - INTERVAL '2 hours') AS billing_count| order_count | billing_count |
|---|---|
| 0 | 0 |
Immutable certification record created on completion — ✅ PASS
Section titled “Immutable certification record created on completion — ✅ PASS”Assertion: Exactly one record exists for the rep, on version 2, with a completion timestamp, signature reference, and form submission
SELECT r.rep_user_id, r.certification_id, r.certification_version_id, r.completed_at, r.expires_at, r.signature_ref, r.form_submission_id FROM certification_records r WHERE r.rep_user_id = $1 AND r.certification_id = $2| rep_user_id | certification_id | certification_version_id | completed_at | expires_at | signature_ref | form_submission_id |
|---|---|---|---|---|---|---|
| ce000001-0000-4000-8000-000000000002 | ce000100-0000-4000-8000-000000000001 | ce000200-0000-4000-8000-000000000002 | 2026-09-29T02:31:54.929Z | 2028-09-29T00:00:00.000Z | 01a0eb01-2132-7b31-877d-f31ae2c707f8 | 01a0eb01-1109-70a8-a813-f763c4b01bfa |
Status change recorded: pending_certification to active on completion — ✅ PASS
Section titled “Status change recorded: pending_certification to active on completion — ✅ PASS”Assertion: A status-change row with reason_code certification_completed moved the relationship to active
SELECT from_status, to_status, reason_code FROM organization_representation_request_status_changes WHERE relationship_id = $1 AND to_status = 'active' AND reason_code = 'certification_completed' AND created_at > NOW() - INTERVAL '2 hours'| from_status | to_status | reason_code |
|---|---|---|
| pending_certification | active | certification_completed |
Document acknowledgment timestamps recorded with the document ID — ✅ PASS
Section titled “Document acknowledgment timestamps recorded with the document ID — ✅ PASS”Assertion: A document view row exists for the acknowledged training document with a first-seen timestamp
SELECT upload_id, first_viewed_at, last_viewed_at FROM form_document_views WHERE user_id = $1 AND form_definition_id = $2| upload_id | first_viewed_at | last_viewed_at |
|---|---|---|
| ce000400-0000-4000-8000-000000000002 | 2026-09-29T02:31:41.593Z | 2026-09-29T02:31:41.722Z |
Quiz graded server-side: one failed attempt then a passing attempt — ✅ PASS
Section titled “Quiz graded server-side: one failed attempt then a passing attempt — ✅ PASS”Assertion: At least one failed attempt (incorrect answers, no submission) precedes a passing attempt linked to the stored submission
SELECT passed, incorrect_count, form_submission_id FROM form_quiz_attempts WHERE user_id = $1 AND form_definition_id = $2 ORDER BY created_at| passed | incorrect_count | form_submission_id |
|---|---|---|
| false | 1 | 01a0eb01-1109-70a8-a813-f763c4b01bfa |
| true | 0 | 01a0eb01-1109-70a8-a813-f763c4b01bfa |
| true | 0 | 01a0eb01-1109-70a8-a813-f763c4b01bfa |
Part 11 signing challenge verified, consumed, and linked to the signature — ✅ PASS
Section titled “Part 11 signing challenge verified, consumed, and linked to the signature — ✅ PASS”Assertion: Exactly one consumed signing challenge exists: emailed, verified (window opened), consumed by the completion, linked to the completion signature, and storing only a sha256 hash — never the raw code
SELECT ch.code_hash, ch.email_sent_at, ch.verified_at, ch.signing_window_expires_at, ch.consumed_at, ch.consumed_signature_id, r.signature_ref FROM certification_signing_challenges ch JOIN certification_records r ON r.rep_user_id = ch.user_id AND r.certification_id = ch.certification_id WHERE ch.user_id = $1 AND ch.certification_id = $2 AND ch.consumed_at IS NOT NULL| code_hash | email_sent_at | verified_at | signing_window_expires_at | consumed_at | consumed_signature_id | signature_ref |
|---|---|---|---|---|---|---|
| 5f882cd4b24abd65c4d931c40b216400a8527c425bc0eb3471097bc30a457f74 | 2026-09-29T02:31:37.429Z | 2026-09-29T02:31:40.853Z | 2026-09-29T06:31:40.853Z | 2026-09-29T02:31:54.932Z | 01a0eb01-2132-7b31-877d-f31ae2c707f8 | 01a0eb01-2132-7b31-877d-f31ae2c707f8 |
Signature captured with integrity hash — ✅ PASS
Section titled “Signature captured with integrity hash — ✅ PASS”Assertion: The completion signature has meaning ‘Certification completion’, a signer name, an execution timestamp, and a SHA-256 hash
SELECT s.meaning, s.signer_name, s.sha256_hash, s.executed_at FROM signatures s JOIN certification_records r ON r.signature_ref = s.id::text WHERE r.rep_user_id = $1 AND r.certification_id = $2| meaning | signer_name | sha256_hash | executed_at |
|---|---|---|---|
| Certification completion | Marco Silva | 9b34dc26b713e354e6833fbd8da8717422eb648b95c34c07f4308755197cdbf2 | 2026-09-29T02:31:54.929Z |
Signed-payload hash recomputes from the stored completion record and signature — ✅ PASS
Section titled “Signed-payload hash recomputes from the stored completion record and signature — ✅ PASS”Assertion: signatures.signed_payload_sha256 equals the sha256 of the canonical signed payload (record id, certification, version, submission, signer, meaning, execution time, SVG hash) rebuilt from the stored rows, and the record completed_at equals the signature executed_at the hash binds (11.70)
SELECT r.id AS record_id, r.certification_id, r.certification_version_id, r.form_submission_id, r.rep_user_id, r.completed_at, s.meaning, s.executed_at, s.sha256_hash, s.signed_payload_sha256 FROM certification_records r JOIN signatures s ON r.signature_ref = s.id::text WHERE r.rep_user_id = $1 AND r.certification_id = $2| record_id | certification_id | certification_version_id | form_submission_id | rep_user_id | completed_at | meaning | executed_at | sha256_hash | signed_payload_sha256 |
|---|---|---|---|---|---|---|---|---|---|
| 01a0eb01-2131-737b-95f7-636028f0aaad | ce000100-0000-4000-8000-000000000001 | ce000200-0000-4000-8000-000000000002 | 01a0eb01-1109-70a8-a813-f763c4b01bfa | ce000001-0000-4000-8000-000000000002 | 2026-09-29T02:31:54.929Z | Certification completion | 2026-09-29T02:31:54.929Z | 9b34dc26b713e354e6833fbd8da8717422eb648b95c34c07f4308755197cdbf2 | 8db42d4e7b2c524d03ddac135cf729b83211019ad0184ca4b76cbaf76e87d87e |
Refused signing re-authentication attempts audited with machine-readable reasons — ✅ PASS
Section titled “Refused signing re-authentication attempts audited with machine-readable reasons — ✅ PASS”Assertion: The suite’s two refused completion replays are audited (11.300(d)): the replay without the signing cookie as ‘missing_code_cookie’, and the tampered replay with the resurrected (already-consumed) code as ‘no_challenge’
SELECT payload->>'reason' AS reason, created_at FROM audit_events WHERE event_type = 'certification' AND action = 'signing_reauth_failed' AND user_id = $1 AND object_id = $2 AND created_at > NOW() - INTERVAL '2 hours' ORDER BY created_at| reason | created_at |
|---|---|
| missing_code_cookie | 2026-09-29T02:31:56.327Z |
| no_challenge | 2026-09-29T02:31:56.360Z |
Certificate and completion-record documents issued — ✅ PASS
Section titled “Certificate and completion-record documents issued — ✅ PASS”Assertion: Both a certificate and a completion_record document were generated
SELECT document_type, status FROM certification_documents WHERE user_id = $1 AND organization_id = $2 AND created_at > NOW() - INTERVAL '2 hours'| document_type | status |
|---|---|
| certificate | created |
| completion_record | created |
Second-manufacturer relationship untouched throughout — ✅ PASS
Section titled “Second-manufacturer relationship untouched throughout — ✅ PASS”Assertion: The relationship with the second manufacturer remained active for the whole run
SELECT status, active FROM organization_representation_relationships WHERE id = $1| status | active |
|---|---|
| active | true |
Completion-record export audited and a background bundle job was created — ✅ PASS
Section titled “Completion-record export audited and a background bundle job was created — ✅ PASS”Assertion: The CSV export was audited and a certification-completion-records bulk export job was created
SELECT (SELECT COUNT(*) FROM audit_events WHERE event_type = 'export' AND action = 'export:certification-completion-records' AND organization_id = $1 AND created_at > NOW() - INTERVAL '2 hours') AS csv_exports, (SELECT COUNT(*) FROM bulk_exports WHERE organization_id = $1 AND export_type = 'certification_completion_records' AND created_at > NOW() - INTERVAL '2 hours') AS bundle_jobs| csv_exports | bundle_jobs |
|---|---|
| 1 | 1 |
Export rows match certification records — ✅ PASS
Section titled “Export rows match certification records — ✅ PASS”Assertion: The certification records the CSV was compared against (rep, version, completion date) are present
SELECT r.id, u.name AS rep_name, v.version_number, r.completed_at FROM certification_records r JOIN users u ON u.id = r.rep_user_id JOIN certification_versions v ON v.id = r.certification_version_id WHERE r.certification_id = $1 ORDER BY r.completed_at| id | rep_name | version_number | completed_at |
|---|---|---|---|
| ce000700-0000-4000-8000-000000000004 | Theo Larsen | 1 | 2025-12-03T02:23:22.269Z |
| ce000700-0000-4000-8000-000000000005 | Dana Whitfield | 2 | 2026-08-15T02:23:22.269Z |
| ce000700-0000-4000-8000-000000000003 | Elena Novak | 2 | 2026-08-30T02:23:22.269Z |
| 01a0eb01-2131-737b-95f7-636028f0aaad | Marco Silva | 2 | 2026-09-29T02:31:54.929Z |
Audit & Email Assertion Ledger
Section titled “Audit & Email Assertion Ledger”Per-declaration outcome of every expectedAuditActions and expectedEmailTemplates entry written into the orchestrator. Missing evidence here is a real test failure, not a soft warning.
Audit Action Assertions
Section titled “Audit Action Assertions”Each row asserts that a declared expectedAuditActions entry produced a matching row in audit_events. A ❌ flips overall status to FAIL — the declaration is real proof, not just an annotation.
| Step | Expected Audit Action | Found |
|---|---|---|
| Step 3: Certification completion | certification:signing_code_issued | ✅ |
| Step 3: Certification completion | certification:signing_code_verified | ✅ |
| Step 3: Certification completion | certification:signing_code_consumed | ✅ |
| Step 3: Certification completion | certification:completed | ✅ |
| Step 3: Certification completion | certification:signing_reauth_failed | ✅ |
| Step 5: Completion-record export | export:export:certification-completion-records | ✅ |
Email Template Assertions
Section titled “Email Template Assertions”Each row asserts that a declared expectedEmailTemplates entry was matched (case-insensitive substring) by a captured email subject or template. A ❌ flips overall status to FAIL.
| Step | Expected Template | Found |
|---|---|---|
| Step 3: Certification completion | Your signing code | ✅ |
Audit Log Events
Section titled “Audit Log Events”Every row written to audit_events while this test was running (scoped to the demo organizations). Provides compliance evidence that user actions are traced end-to-end (URS-003).
Capture window start: 2026-09-29T02:30:57.495Z
SELECT ae.created_at, ae.event_type, ae.action, ae.user_id, u.email AS user_email, ae.organization_id, o.name AS organization_name, ae.object_id, ae.secondary_object_id, ae.payload, ae.route, ae.trace_id FROM audit_events ae LEFT JOIN users u ON u.id = ae.user_id LEFT JOIN organizations o ON o.id = ae.organization_id WHERE ae.created_at >= $1 AND ae.organization_id = ANY($2::uuid[]) ORDER BY ae.created_at ASC24 event(s) captured:
| Time | Type | Action | User | Org | Object ID | Performed | Reason |
|---|---|---|---|---|---|---|---|
| 2026-09-29 02:31:04Z | user_log | user:login | marco.silva@corvetasurgical.com | Corveta Surgical Group | — | — | |
| 2026-09-29 02:31:21Z | user_log | user:login | marco.silva@corvetasurgical.com | Corveta Surgical Group | — | — | |
| 2026-09-29 02:31:31Z | user_log | user:login | marco.silva@corvetasurgical.com | Corveta Surgical Group | — | — | |
| 2026-09-29 02:31:37Z | certification | signing_code_issued | marco.silva@corvetasurgical.com | Vantis | ce000100-0000-4000-8000-000000000001 | — | |
| 2026-09-29 02:31:37Z | decision | certifications.signing_challenge.send_sms | marco.silva@corvetasurgical.com | Vantis | 01a0eb00-dca4-7981-9ba0-4fb9bbb1917b | no | no_phone_channel |
| 2026-09-29 02:31:38Z | transactional_email | certification_signing_code | — | Vantis | 01a0eb00-dca4-7981-9ba0-4fb9bbb1917b | — | |
| 2026-09-29 02:31:40Z | certification | signing_code_verified | marco.silva@corvetasurgical.com | Vantis | ce000100-0000-4000-8000-000000000001 | — | |
| 2026-09-29 02:31:50Z | decision | forms.grade_submission | marco.silva@corvetasurgical.com | Vantis | ce000300-0000-4000-8000-000000000002 | yes | all_answers_correct |
| 2026-09-29 02:31:54Z | certification | completed | marco.silva@corvetasurgical.com | Vantis | 01a0eb01-2131-737b-95f7-636028f0aaad | — | |
| 2026-09-29 02:31:54Z | decision | certifications.complete_certification.issue_certificate | marco.silva@corvetasurgical.com | Vantis | 01a0eb01-2131-737b-95f7-636028f0aaad | yes | quiz_backed_completion |
| 2026-09-29 02:31:54Z | decision | certifications.complete_certification.mark_relationship_certified | marco.silva@corvetasurgical.com | Vantis | ce000002-0000-4000-8000-000000000002 | yes | relationship_pending_certification |
| 2026-09-29 02:31:54Z | organization_representation | status_change | marco.silva@corvetasurgical.com | Vantis | ce000002-0000-4000-8000-000000000002 | — | Certification completed |
| 2026-09-29 02:31:54Z | certification | signing_code_consumed | marco.silva@corvetasurgical.com | Vantis | ce000100-0000-4000-8000-000000000001 | — | |
| 2026-09-29 02:31:54Z | user_log | rep_relationship_certified | marco.silva@corvetasurgical.com | Vantis | — | — | Certification completed |
| 2026-09-29 02:31:56Z | certification | signing_reauth_failed | marco.silva@corvetasurgical.com | Vantis | ce000100-0000-4000-8000-000000000001 | — | missing_code_cookie |
| 2026-09-29 02:31:56Z | certification | signing_reauth_failed | marco.silva@corvetasurgical.com | Vantis | ce000100-0000-4000-8000-000000000001 | — | no_challenge |
| 2026-09-29 02:31:57Z | certification_certificate | issued | marco.silva@corvetasurgical.com | Vantis | 01a0eb01-2158-7e51-95b1-e785dfaf8208 | — | |
| 2026-09-29 02:31:57Z | certification_completion_record | issued | marco.silva@corvetasurgical.com | Vantis | 01a0eb01-215d-7731-b3c3-e203ac055bfb | — | |
| 2026-09-29 02:32:03Z | user_log | user:login | marco.silva@corvetasurgical.com | Corveta Surgical Group | — | — | |
| 2026-09-29 02:32:09Z | user_log | user:login | mark.manufacturer@vantismedical.com | Vantis | — | — | |
| 2026-09-29 02:32:14Z | export | export:certification-completion-records | mark.manufacturer@vantismedical.com | Vantis | ce000100-0000-4000-8000-000000000001 | — | |
| 2026-09-29 02:32:16Z | bulk_export | requested | mark.manufacturer@vantismedical.com | Vantis | 01a0eb01-7581-7128-b7ac-98705c5f998e | — | |
| 2026-09-29 02:32:17Z | bulk_export | started | mark.manufacturer@vantismedical.com | Vantis | 01a0eb01-7581-7128-b7ac-98705c5f998e | — | |
| 2026-09-29 02:32:17Z | decision | bulk_exports.run_bulk_export.claim_job | — | Vantis | 01a0eb01-7581-7128-b7ac-98705c5f998e | yes | claimed_requested_job |
Email Evidence
Section titled “Email Evidence”1 notification email(s) were captured during this test run. Each email is rendered as a screenshot for compliance review.
1. Your signing code for LiraLock Implant System Certification
Section titled “1. Your signing code for LiraLock Implant System Certification”Template: Your_signing_code_for_LiraLock_Implant_System_Certification

Downloads
Section titled “Downloads”- audit-events.json
- completion-records.csv
- report.md
- result.json
- screenshots-index.json
- step-timings.json