From bf81e185fa96c1820d95a00164ac85785f41fd38 Mon Sep 17 00:00:00 2001 From: Jules Dev Date: Tue, 11 Aug 2026 21:04:03 +0000 Subject: [PATCH] feat: implement two-way registry verification and strategic pillar integrated reporting --- Validation_Traceability_Matrix.md | 88 +++++----- rtm.csv | 74 ++++---- rtm.json | 158 ++++++++++++++++- scripts/generate-rtm.mjs | 164 ++++++++++++++---- .../validation/rtm-two-way-validation.spec.ts | 42 +++++ 5 files changed, 401 insertions(+), 125 deletions(-) create mode 100644 src/app/domain/core/validation/rtm-two-way-validation.spec.ts diff --git a/Validation_Traceability_Matrix.md b/Validation_Traceability_Matrix.md index 6e8ac431..33982000 100644 --- a/Validation_Traceability_Matrix.md +++ b/Validation_Traceability_Matrix.md @@ -1,7 +1,7 @@ # Validation Traceability Matrix -> **Generated:** 2026-08-08T04:03:18.202Z -> **Status:** Test results loaded +> **Generated:** 2026-08-11T21:02:59.221Z +> **Status:** Test results not provided — status shown as UNKNOWN > **Requirements covered:** 13 / 16 > **Tagged test cases:** 36 @@ -20,47 +20,47 @@ ## Traceability Matrix -| Requirement ID | Description | Test File | Line | Test Name | Suite | Status | -|---|---|---|---|---|---|---| -| `REQ-21CFR11-001` | 21 CFR Part 11 – All electronic records must embed the application semantic version | `tests_e2e/audit-trail.spec.ts` | 160 | R script contains application semantic version | 21 CFR Part 11 – Audit Trail: generated code artifact provenance | ⬜ UNKNOWN | -| `REQ-21CFR11-001` | 21 CFR Part 11 – All electronic records must embed the application semantic version | `tests_e2e/audit-trail.spec.ts` | 189 | Python script contains application semantic version | | ⬜ UNKNOWN | -| `REQ-21CFR11-001` | 21 CFR Part 11 – All electronic records must embed the application semantic version | `tests_e2e/audit-trail.spec.ts` | 218 | SAS script contains application semantic version | | ⬜ UNKNOWN | -| `REQ-21CFR11-001` | 21 CFR Part 11 – All electronic records must embed the application semantic version | `tests_e2e/audit-trail.spec.ts` | 247 | Stata script contains application semantic version | | ⬜ UNKNOWN | -| `REQ-21CFR11-002` | 21 CFR Part 11 – Electronic records must carry an ISO 8601 generation timestamp | `tests_e2e/audit-trail.spec.ts` | 166 | R script contains a valid ISO 8601 generated-at timestamp | | ⬜ UNKNOWN | -| `REQ-21CFR11-002` | 21 CFR Part 11 – Electronic records must carry an ISO 8601 generation timestamp | `tests_e2e/audit-trail.spec.ts` | 195 | Python script contains a valid ISO 8601 generated-at timestamp | | ⬜ UNKNOWN | -| `REQ-21CFR11-002` | 21 CFR Part 11 – Electronic records must carry an ISO 8601 generation timestamp | `tests_e2e/audit-trail.spec.ts` | 224 | SAS script contains a valid ISO 8601 generated-at timestamp | | ⬜ UNKNOWN | -| `REQ-21CFR11-002` | 21 CFR Part 11 – Electronic records must carry an ISO 8601 generation timestamp | `tests_e2e/audit-trail.spec.ts` | 253 | Stata script contains a valid ISO 8601 generated-at timestamp | | ⬜ UNKNOWN | -| `REQ-21CFR11-003` | 21 CFR Part 11 – The unique protocol identifier must appear in every generated artifact | `tests_e2e/audit-trail.spec.ts` | 172 | R script contains the trial protocol identifier | | ⬜ UNKNOWN | -| `REQ-21CFR11-003` | 21 CFR Part 11 – The unique protocol identifier must appear in every generated artifact | `tests_e2e/audit-trail.spec.ts` | 201 | Python script contains the trial protocol identifier | | ⬜ UNKNOWN | -| `REQ-21CFR11-003` | 21 CFR Part 11 – The unique protocol identifier must appear in every generated artifact | `tests_e2e/audit-trail.spec.ts` | 230 | SAS script contains the trial protocol identifier | | ⬜ UNKNOWN | -| `REQ-21CFR11-003` | 21 CFR Part 11 – The unique protocol identifier must appear in every generated artifact | `tests_e2e/audit-trail.spec.ts` | 259 | Stata script contains the trial protocol identifier | | ⬜ UNKNOWN | -| `REQ-21CFR11-003` | 21 CFR Part 11 – The unique protocol identifier must appear in every generated artifact | `tests_e2e/audit-trail.spec.ts` | 436 | results header displays the protocol identifier | | ⬜ UNKNOWN | -| `REQ-21CFR11-004` | 21 CFR Part 11 – Audit trail must record the exact PRNG seed used for schema generation | `tests_e2e/audit-trail.spec.ts` | 183 | R script contains the PRNG seed initialisation statement | | ⬜ UNKNOWN | -| `REQ-21CFR11-004` | 21 CFR Part 11 – Audit trail must record the exact PRNG seed used for schema generation | `tests_e2e/audit-trail.spec.ts` | 212 | Python script contains the PRNG seed initialisation statement | | ⬜ UNKNOWN | -| `REQ-21CFR11-004` | 21 CFR Part 11 – Audit trail must record the exact PRNG seed used for schema generation | `tests_e2e/audit-trail.spec.ts` | 241 | SAS script contains the PRNG seed initialisation statement | | ⬜ UNKNOWN | -| `REQ-21CFR11-004` | 21 CFR Part 11 – Audit trail must record the exact PRNG seed used for schema generation | `tests_e2e/audit-trail.spec.ts` | 270 | Stata script contains the PRNG seed initialisation statement | | ⬜ UNKNOWN | -| `REQ-21CFR11-004` | 21 CFR Part 11 – Audit trail must record the exact PRNG seed used for schema generation | `tests_e2e/audit-trail.spec.ts` | 428 | results header displays the randomization seed used for the schema | 21 CFR Part 11 – Audit Trail: results grid metadata stamping | ⬜ UNKNOWN | -| `REQ-21CFR11-005` | 21 CFR Part 11 – PDF/XLSX exports must embed a SHA-256 audit hash for integrity verification | — | — | *(no tests tagged)* | — | ⚠️ NO COVERAGE | -| `REQ-21CFR11-006` | 21 CFR Part 11 – PDF audit artifact must embed version, timestamp, protocol ID and PRNG seed | `tests_e2e/audit-trail.spec.ts` | 393 | PDF export contains the application semantic version | 21 CFR Part 11 – Audit Trail: PDF export provenance | ⬜ UNKNOWN | -| `REQ-21CFR11-006` | 21 CFR Part 11 – PDF audit artifact must embed version, timestamp, protocol ID and PRNG seed | `tests_e2e/audit-trail.spec.ts` | 399 | PDF export contains a valid ISO 8601 generated-at timestamp | | ⬜ UNKNOWN | -| `REQ-21CFR11-006` | 21 CFR Part 11 – PDF audit artifact must embed version, timestamp, protocol ID and PRNG seed | `tests_e2e/audit-trail.spec.ts` | 405 | PDF export contains the trial protocol identifier | | ⬜ UNKNOWN | -| `REQ-21CFR11-006` | 21 CFR Part 11 – PDF audit artifact must embed version, timestamp, protocol ID and PRNG seed | `tests_e2e/audit-trail.spec.ts` | 411 | PDF export contains the PRNG seed value | | ⬜ UNKNOWN | -| `REQ-EXPORT-001` | CSV/XLSX export filename must contain an 8-digit date component for per-generation traceability | `tests_e2e/audit-trail.spec.ts` | 444 | CSV download filename contains a date component for traceability | | ⬜ UNKNOWN | -| `REQ-EXPORT-002` | PDF export must trigger a file download containing a properly named randomization artifact | `tests_e2e/audit-trail.spec.ts` | 420 | PDF export filename matches the expected pattern | | ⬜ UNKNOWN | -| `REQ-EXPORT-002` | PDF export must trigger a file download containing a properly named randomization artifact | `tests_e2e/results-operations.spec.ts` | 120 | should trigger a PDF download when the PDF button is clicked | | ⬜ UNKNOWN | -| `REQ-EXPORT-003` | Excel export must produce a two-sheet workbook (Schema + Audit & Configuration) | — | — | *(no tests tagged)* | — | ⚠️ NO COVERAGE | -| `REQ-ICH-E6-001` | GCP – Subject IDs must be unique and fully traceable to site and block (ICH E6 §4.9) | `src/app/domain/randomization-engine/core/randomization-algorithm.spec.ts` | 657 | {RND:n} produces no duplicate subject IDs across the schema | generateRandomizationSchema – new token syntax | ✅ PASS | -| `REQ-ICH-E6-002` | Site information must be captured and present in all exported records (ICH E6 §4.1) | — | — | *(no tests tagged)* | — | ⚠️ NO COVERAGE | -| `REQ-ICH-E9-001` | Randomization algorithm must be deterministic and reproducible from a fixed PRNG seed (ICH E9 §2.3) | `scripts/cross-env/verify_python_schema.py` | 2 | Execute scripts/cross-env/verify_python_schema.py | Standalone Script | ✅ PASS | -| `REQ-ICH-E9-001` | Randomization algorithm must be deterministic and reproducible from a fixed PRNG seed (ICH E9 §2.3) | `src/app/domain/randomization-engine/core/statistical-validation.spec.ts` | 145 | 1:1 ratio converges to 50 % per arm across 200 Monte Carlo trials | ICH E9 – Law of Large Numbers: allocation ratio convergence | ✅ PASS | -| `REQ-ICH-E9-001` | Randomization algorithm must be deterministic and reproducible from a fixed PRNG seed (ICH E9 §2.3) | `tests_e2e/schema-generation.spec.ts` | 11 | should generate a schema and display results grid | | ⬜ UNKNOWN | -| `REQ-ICH-E9-002` | Stratification factors must be applied correctly to the randomization schedule (ICH E9 §2.3.3) | `src/app/domain/randomization-engine/core/statistical-validation.spec.ts` | 278 | per-stratum caps are never exceeded across 100 random seeds | ICH E9 – Stratum Cap Enforcement: dynamic caps are never exceeded | ✅ PASS | -| `REQ-ICH-E9-003` | Block randomization must respect declared block sizes and produce balanced allocations (ICH E9 §2.3.4) | `scripts/cross-env/verify_python_schema.py` | 3 | Execute scripts/cross-env/verify_python_schema.py | Standalone Script | ✅ PASS | -| `REQ-ICH-E9-003` | Block randomization must respect declared block sizes and produce balanced allocations (ICH E9 §2.3.4) | `src/app/domain/randomization-engine/core/statistical-validation.spec.ts` | 185 | every block has exactly the correct count of each arm for a 1:1 ratio with block size 4 | ICH E9 – Block Balance: strict intra-block arm balance | ✅ PASS | -| `REQ-SBOM-001` | A Software Bill of Materials (SBOM) must be generated for every production build | `.github/workflows/ci.yml` | 743 | Job: sbom | CI Workflow | ⬜ UNKNOWN | -| `REQ-ZERO-TRUST-001` | No subject or schema data may be transmitted to external servers (zero-trust architecture) | `tests_e2e/zero-trust.spec.ts` | 53 | schema generation produces zero outbound XHR/Fetch requests to external servers | Zero-Trust Architecture: no outbound network requests | ⬜ UNKNOWN | -| `REQ-ZERO-TRUST-001` | No subject or schema data may be transmitted to external servers (zero-trust architecture) | `tests_e2e/zero-trust.spec.ts` | 71 | CSV export produces zero outbound requests to external servers | | ⬜ UNKNOWN | -| `REQ-ZERO-TRUST-001` | No subject or schema data may be transmitted to external servers (zero-trust architecture) | `tests_e2e/zero-trust.spec.ts` | 92 | PDF export produces zero outbound requests to external servers | | ⬜ UNKNOWN | +| Requirement ID | Strategic Pillar | Description | Test File | Line | Test Name | Suite | Status | +|---|---|---|---|---|---|---|---| +| `REQ-21CFR11-001` | `Reproducibility` | 21 CFR Part 11 – All electronic records must embed the application semantic version | `tests_e2e/audit-trail.spec.ts` | 160 | R script contains application semantic version | 21 CFR Part 11 – Audit Trail: generated code artifact provenance | ⬜ UNKNOWN | +| `REQ-21CFR11-001` | `Reproducibility` | 21 CFR Part 11 – All electronic records must embed the application semantic version | `tests_e2e/audit-trail.spec.ts` | 189 | Python script contains application semantic version | | ⬜ UNKNOWN | +| `REQ-21CFR11-001` | `Reproducibility` | 21 CFR Part 11 – All electronic records must embed the application semantic version | `tests_e2e/audit-trail.spec.ts` | 218 | SAS script contains application semantic version | | ⬜ UNKNOWN | +| `REQ-21CFR11-001` | `Reproducibility` | 21 CFR Part 11 – All electronic records must embed the application semantic version | `tests_e2e/audit-trail.spec.ts` | 247 | Stata script contains application semantic version | | ⬜ UNKNOWN | +| `REQ-21CFR11-002` | `Reproducibility` | 21 CFR Part 11 – Electronic records must carry an ISO 8601 generation timestamp | `tests_e2e/audit-trail.spec.ts` | 166 | R script contains a valid ISO 8601 generated-at timestamp | | ⬜ UNKNOWN | +| `REQ-21CFR11-002` | `Reproducibility` | 21 CFR Part 11 – Electronic records must carry an ISO 8601 generation timestamp | `tests_e2e/audit-trail.spec.ts` | 195 | Python script contains a valid ISO 8601 generated-at timestamp | | ⬜ UNKNOWN | +| `REQ-21CFR11-002` | `Reproducibility` | 21 CFR Part 11 – Electronic records must carry an ISO 8601 generation timestamp | `tests_e2e/audit-trail.spec.ts` | 224 | SAS script contains a valid ISO 8601 generated-at timestamp | | ⬜ UNKNOWN | +| `REQ-21CFR11-002` | `Reproducibility` | 21 CFR Part 11 – Electronic records must carry an ISO 8601 generation timestamp | `tests_e2e/audit-trail.spec.ts` | 253 | Stata script contains a valid ISO 8601 generated-at timestamp | | ⬜ UNKNOWN | +| `REQ-21CFR11-003` | `Reproducibility` | 21 CFR Part 11 – The unique protocol identifier must appear in every generated artifact | `tests_e2e/audit-trail.spec.ts` | 172 | R script contains the trial protocol identifier | | ⬜ UNKNOWN | +| `REQ-21CFR11-003` | `Reproducibility` | 21 CFR Part 11 – The unique protocol identifier must appear in every generated artifact | `tests_e2e/audit-trail.spec.ts` | 201 | Python script contains the trial protocol identifier | | ⬜ UNKNOWN | +| `REQ-21CFR11-003` | `Reproducibility` | 21 CFR Part 11 – The unique protocol identifier must appear in every generated artifact | `tests_e2e/audit-trail.spec.ts` | 230 | SAS script contains the trial protocol identifier | | ⬜ UNKNOWN | +| `REQ-21CFR11-003` | `Reproducibility` | 21 CFR Part 11 – The unique protocol identifier must appear in every generated artifact | `tests_e2e/audit-trail.spec.ts` | 259 | Stata script contains the trial protocol identifier | | ⬜ UNKNOWN | +| `REQ-21CFR11-003` | `Reproducibility` | 21 CFR Part 11 – The unique protocol identifier must appear in every generated artifact | `tests_e2e/audit-trail.spec.ts` | 436 | results header displays the protocol identifier | | ⬜ UNKNOWN | +| `REQ-21CFR11-004` | `Reproducibility` | 21 CFR Part 11 – Audit trail must record the exact PRNG seed used for schema generation | `tests_e2e/audit-trail.spec.ts` | 183 | R script contains the PRNG seed initialisation statement | | ⬜ UNKNOWN | +| `REQ-21CFR11-004` | `Reproducibility` | 21 CFR Part 11 – Audit trail must record the exact PRNG seed used for schema generation | `tests_e2e/audit-trail.spec.ts` | 212 | Python script contains the PRNG seed initialisation statement | | ⬜ UNKNOWN | +| `REQ-21CFR11-004` | `Reproducibility` | 21 CFR Part 11 – Audit trail must record the exact PRNG seed used for schema generation | `tests_e2e/audit-trail.spec.ts` | 241 | SAS script contains the PRNG seed initialisation statement | | ⬜ UNKNOWN | +| `REQ-21CFR11-004` | `Reproducibility` | 21 CFR Part 11 – Audit trail must record the exact PRNG seed used for schema generation | `tests_e2e/audit-trail.spec.ts` | 270 | Stata script contains the PRNG seed initialisation statement | | ⬜ UNKNOWN | +| `REQ-21CFR11-004` | `Reproducibility` | 21 CFR Part 11 – Audit trail must record the exact PRNG seed used for schema generation | `tests_e2e/audit-trail.spec.ts` | 428 | results header displays the randomization seed used for the schema | 21 CFR Part 11 – Audit Trail: results grid metadata stamping | ⬜ UNKNOWN | +| `REQ-21CFR11-005` | `Reproducibility` | 21 CFR Part 11 – PDF/XLSX exports must embed a SHA-256 audit hash for integrity verification | — | — | *(no tests tagged)* | — | ⚠️ NO COVERAGE | +| `REQ-21CFR11-006` | `Reproducibility` | 21 CFR Part 11 – PDF audit artifact must embed version, timestamp, protocol ID and PRNG seed | `tests_e2e/audit-trail.spec.ts` | 393 | PDF export contains the application semantic version | 21 CFR Part 11 – Audit Trail: PDF export provenance | ⬜ UNKNOWN | +| `REQ-21CFR11-006` | `Reproducibility` | 21 CFR Part 11 – PDF audit artifact must embed version, timestamp, protocol ID and PRNG seed | `tests_e2e/audit-trail.spec.ts` | 399 | PDF export contains a valid ISO 8601 generated-at timestamp | | ⬜ UNKNOWN | +| `REQ-21CFR11-006` | `Reproducibility` | 21 CFR Part 11 – PDF audit artifact must embed version, timestamp, protocol ID and PRNG seed | `tests_e2e/audit-trail.spec.ts` | 405 | PDF export contains the trial protocol identifier | | ⬜ UNKNOWN | +| `REQ-21CFR11-006` | `Reproducibility` | 21 CFR Part 11 – PDF audit artifact must embed version, timestamp, protocol ID and PRNG seed | `tests_e2e/audit-trail.spec.ts` | 411 | PDF export contains the PRNG seed value | | ⬜ UNKNOWN | +| `REQ-EXPORT-001` | `Reproducibility` | CSV/XLSX export filename must contain an 8-digit date component for per-generation traceability | `tests_e2e/audit-trail.spec.ts` | 444 | CSV download filename contains a date component for traceability | | ⬜ UNKNOWN | +| `REQ-EXPORT-002` | `Reproducibility` | PDF export must trigger a file download containing a properly named randomization artifact | `tests_e2e/audit-trail.spec.ts` | 420 | PDF export filename matches the expected pattern | | ⬜ UNKNOWN | +| `REQ-EXPORT-002` | `Reproducibility` | PDF export must trigger a file download containing a properly named randomization artifact | `tests_e2e/results-operations.spec.ts` | 120 | should trigger a PDF download when the PDF button is clicked | | ⬜ UNKNOWN | +| `REQ-EXPORT-003` | `Reproducibility` | Excel export must produce a two-sheet workbook (Schema + Audit & Configuration) | — | — | *(no tests tagged)* | — | ⚠️ NO COVERAGE | +| `REQ-ICH-E6-001` | `Scientific Validity` | GCP – Subject IDs must be unique and fully traceable to site and block (ICH E6 §4.9) | `src/app/domain/randomization-engine/core/randomization-algorithm.spec.ts` | 657 | {RND:n} produces no duplicate subject IDs across the schema | | ⬜ UNKNOWN | +| `REQ-ICH-E6-002` | `Scientific Validity` | Site information must be captured and present in all exported records (ICH E6 §4.1) | — | — | *(no tests tagged)* | — | ⚠️ NO COVERAGE | +| `REQ-ICH-E9-001` | `Scientific Validity` | Randomization algorithm must be deterministic and reproducible from a fixed PRNG seed (ICH E9 §2.3) | `scripts/cross-env/verify_python_schema.py` | 2 | Execute scripts/cross-env/verify_python_schema.py | Standalone Script | ⬜ UNKNOWN | +| `REQ-ICH-E9-001` | `Scientific Validity` | Randomization algorithm must be deterministic and reproducible from a fixed PRNG seed (ICH E9 §2.3) | `src/app/domain/randomization-engine/core/statistical-validation.spec.ts` | 145 | 1:1 ratio converges to 50 % per arm across 200 Monte Carlo trials | ICH E9 – Law of Large Numbers: allocation ratio convergence | ⬜ UNKNOWN | +| `REQ-ICH-E9-001` | `Scientific Validity` | Randomization algorithm must be deterministic and reproducible from a fixed PRNG seed (ICH E9 §2.3) | `tests_e2e/schema-generation.spec.ts` | 11 | should generate a schema and display results grid | | ⬜ UNKNOWN | +| `REQ-ICH-E9-002` | `Scientific Validity` | Stratification factors must be applied correctly to the randomization schedule (ICH E9 §2.3.3) | `src/app/domain/randomization-engine/core/statistical-validation.spec.ts` | 278 | per-stratum caps are never exceeded across 100 random seeds | ICH E9 – Stratum Cap Enforcement: dynamic caps are never exceeded | ⬜ UNKNOWN | +| `REQ-ICH-E9-003` | `Scientific Validity` | Block randomization must respect declared block sizes and produce balanced allocations (ICH E9 §2.3.4) | `scripts/cross-env/verify_python_schema.py` | 3 | Execute scripts/cross-env/verify_python_schema.py | Standalone Script | ⬜ UNKNOWN | +| `REQ-ICH-E9-003` | `Scientific Validity` | Block randomization must respect declared block sizes and produce balanced allocations (ICH E9 §2.3.4) | `src/app/domain/randomization-engine/core/statistical-validation.spec.ts` | 185 | every block has exactly the correct count of each arm for a 1:1 ratio with block size 4 | ICH E9 – Block Balance: strict intra-block arm balance | ⬜ UNKNOWN | +| `REQ-SBOM-001` | `Reproducibility` | A Software Bill of Materials (SBOM) must be generated for every production build | `.github/workflows/ci.yml` | 778 | Job: sbom | CI Workflow | ⬜ UNKNOWN | +| `REQ-ZERO-TRUST-001` | `Zero-Trust` | No subject or schema data may be transmitted to external servers (zero-trust architecture) | `tests_e2e/zero-trust.spec.ts` | 53 | schema generation produces zero outbound XHR/Fetch requests to external servers | Zero-Trust Architecture: no outbound network requests | ⬜ UNKNOWN | +| `REQ-ZERO-TRUST-001` | `Zero-Trust` | No subject or schema data may be transmitted to external servers (zero-trust architecture) | `tests_e2e/zero-trust.spec.ts` | 71 | CSV export produces zero outbound requests to external servers | | ⬜ UNKNOWN | +| `REQ-ZERO-TRUST-001` | `Zero-Trust` | No subject or schema data may be transmitted to external servers (zero-trust architecture) | `tests_e2e/zero-trust.spec.ts` | 92 | PDF export produces zero outbound requests to external servers | | ⬜ UNKNOWN | --- @@ -87,7 +87,7 @@ | `Zero-Trust` | `tests_e2e/zero-trust.spec.ts` | 52 | schema generation produces zero outbound XHR/Fetch requests to external servers | Zero-Trust Architecture: no outbound network requests | ⬜ UNKNOWN | | `Zero-Trust` | `tests_e2e/zero-trust.spec.ts` | 70 | CSV export produces zero outbound requests to external servers | Zero-Trust Architecture: no outbound network requests | ⬜ UNKNOWN | | `Reproducibility` | `tests_e2e/determinism.spec.ts` | 5 | generates identical Audit Hash for the same seed across Chromium, WebKit, and Firefox | Determinism Test Suite | ⬜ UNKNOWN | -| `Scientific Validity` | `src/app/domain/randomization-engine/core/statistical-validation.spec.ts` | 144 | 1:1 ratio converges to 50 % per arm across 200 Monte Carlo trials | ICH E9 – Law of Large Numbers: allocation ratio convergence | ✅ PASS | +| `Scientific Validity` | `src/app/domain/randomization-engine/core/statistical-validation.spec.ts` | 144 | 1:1 ratio converges to 50 % per arm across 200 Monte Carlo trials | ICH E9 – Law of Large Numbers: allocation ratio convergence | ⬜ UNKNOWN | --- diff --git a/rtm.csv b/rtm.csv index 06ca6af0..a5b61635 100644 --- a/rtm.csv +++ b/rtm.csv @@ -1,37 +1,37 @@ -Requirement ID,Suite Name,Test Name,Status -REQ-21CFR11-001,"21 CFR Part 11 – Audit Trail: generated code artifact provenance","R script contains application semantic version",UNKNOWN -REQ-21CFR11-001,"","Python script contains application semantic version",UNKNOWN -REQ-21CFR11-001,"","SAS script contains application semantic version",UNKNOWN -REQ-21CFR11-001,"","Stata script contains application semantic version",UNKNOWN -REQ-21CFR11-002,"","R script contains a valid ISO 8601 generated-at timestamp",UNKNOWN -REQ-21CFR11-002,"","Python script contains a valid ISO 8601 generated-at timestamp",UNKNOWN -REQ-21CFR11-002,"","SAS script contains a valid ISO 8601 generated-at timestamp",UNKNOWN -REQ-21CFR11-002,"","Stata script contains a valid ISO 8601 generated-at timestamp",UNKNOWN -REQ-21CFR11-003,"","R script contains the trial protocol identifier",UNKNOWN -REQ-21CFR11-003,"","Python script contains the trial protocol identifier",UNKNOWN -REQ-21CFR11-003,"","SAS script contains the trial protocol identifier",UNKNOWN -REQ-21CFR11-003,"","Stata script contains the trial protocol identifier",UNKNOWN -REQ-21CFR11-003,"","results header displays the protocol identifier",UNKNOWN -REQ-21CFR11-004,"","R script contains the PRNG seed initialisation statement",UNKNOWN -REQ-21CFR11-004,"","Python script contains the PRNG seed initialisation statement",UNKNOWN -REQ-21CFR11-004,"","SAS script contains the PRNG seed initialisation statement",UNKNOWN -REQ-21CFR11-004,"","Stata script contains the PRNG seed initialisation statement",UNKNOWN -REQ-21CFR11-004,"21 CFR Part 11 – Audit Trail: results grid metadata stamping","results header displays the randomization seed used for the schema",UNKNOWN -REQ-21CFR11-006,"21 CFR Part 11 – Audit Trail: PDF export provenance","PDF export contains the application semantic version",UNKNOWN -REQ-21CFR11-006,"","PDF export contains a valid ISO 8601 generated-at timestamp",UNKNOWN -REQ-21CFR11-006,"","PDF export contains the trial protocol identifier",UNKNOWN -REQ-21CFR11-006,"","PDF export contains the PRNG seed value",UNKNOWN -REQ-EXPORT-001,"","CSV download filename contains a date component for traceability",UNKNOWN -REQ-EXPORT-002,"","PDF export filename matches the expected pattern",UNKNOWN -REQ-EXPORT-002,"","should trigger a PDF download when the PDF button is clicked",UNKNOWN -REQ-ICH-E6-001,"generateRandomizationSchema – new token syntax","{RND:n} produces no duplicate subject IDs across the schema",PASS -REQ-ICH-E9-001,"Standalone Script","Execute scripts/cross-env/verify_python_schema.py",PASS -REQ-ICH-E9-001,"ICH E9 – Law of Large Numbers: allocation ratio convergence","1:1 ratio converges to 50 % per arm across 200 Monte Carlo trials",PASS -REQ-ICH-E9-001,"","should generate a schema and display results grid",UNKNOWN -REQ-ICH-E9-002,"ICH E9 – Stratum Cap Enforcement: dynamic caps are never exceeded","per-stratum caps are never exceeded across 100 random seeds",PASS -REQ-ICH-E9-003,"Standalone Script","Execute scripts/cross-env/verify_python_schema.py",PASS -REQ-ICH-E9-003,"ICH E9 – Block Balance: strict intra-block arm balance","every block has exactly the correct count of each arm for a 1:1 ratio with block size 4",PASS -REQ-SBOM-001,"CI Workflow","Job: sbom",UNKNOWN -REQ-ZERO-TRUST-001,"Zero-Trust Architecture: no outbound network requests","schema generation produces zero outbound XHR/Fetch requests to external servers",UNKNOWN -REQ-ZERO-TRUST-001,"","CSV export produces zero outbound requests to external servers",UNKNOWN -REQ-ZERO-TRUST-001,"","PDF export produces zero outbound requests to external servers",UNKNOWN +Requirement ID,Strategic Pillar,Suite Name,Test Name,Status +REQ-21CFR11-001,"{""Name"":""Reproducibility"",""name"":""Reproducibility""}","21 CFR Part 11 – Audit Trail: generated code artifact provenance","R script contains application semantic version",UNKNOWN +REQ-21CFR11-001,"{""Name"":""Reproducibility"",""name"":""Reproducibility""}","","Python script contains application semantic version",UNKNOWN +REQ-21CFR11-001,"{""Name"":""Reproducibility"",""name"":""Reproducibility""}","","SAS script contains application semantic version",UNKNOWN +REQ-21CFR11-001,"{""Name"":""Reproducibility"",""name"":""Reproducibility""}","","Stata script contains application semantic version",UNKNOWN +REQ-21CFR11-002,"{""Name"":""Reproducibility"",""name"":""Reproducibility""}","","R script contains a valid ISO 8601 generated-at timestamp",UNKNOWN +REQ-21CFR11-002,"{""Name"":""Reproducibility"",""name"":""Reproducibility""}","","Python script contains a valid ISO 8601 generated-at timestamp",UNKNOWN +REQ-21CFR11-002,"{""Name"":""Reproducibility"",""name"":""Reproducibility""}","","SAS script contains a valid ISO 8601 generated-at timestamp",UNKNOWN +REQ-21CFR11-002,"{""Name"":""Reproducibility"",""name"":""Reproducibility""}","","Stata script contains a valid ISO 8601 generated-at timestamp",UNKNOWN +REQ-21CFR11-003,"{""Name"":""Reproducibility"",""name"":""Reproducibility""}","","R script contains the trial protocol identifier",UNKNOWN +REQ-21CFR11-003,"{""Name"":""Reproducibility"",""name"":""Reproducibility""}","","Python script contains the trial protocol identifier",UNKNOWN +REQ-21CFR11-003,"{""Name"":""Reproducibility"",""name"":""Reproducibility""}","","SAS script contains the trial protocol identifier",UNKNOWN +REQ-21CFR11-003,"{""Name"":""Reproducibility"",""name"":""Reproducibility""}","","Stata script contains the trial protocol identifier",UNKNOWN +REQ-21CFR11-003,"{""Name"":""Reproducibility"",""name"":""Reproducibility""}","","results header displays the protocol identifier",UNKNOWN +REQ-21CFR11-004,"{""Name"":""Reproducibility"",""name"":""Reproducibility""}","","R script contains the PRNG seed initialisation statement",UNKNOWN +REQ-21CFR11-004,"{""Name"":""Reproducibility"",""name"":""Reproducibility""}","","Python script contains the PRNG seed initialisation statement",UNKNOWN +REQ-21CFR11-004,"{""Name"":""Reproducibility"",""name"":""Reproducibility""}","","SAS script contains the PRNG seed initialisation statement",UNKNOWN +REQ-21CFR11-004,"{""Name"":""Reproducibility"",""name"":""Reproducibility""}","","Stata script contains the PRNG seed initialisation statement",UNKNOWN +REQ-21CFR11-004,"{""Name"":""Reproducibility"",""name"":""Reproducibility""}","21 CFR Part 11 – Audit Trail: results grid metadata stamping","results header displays the randomization seed used for the schema",UNKNOWN +REQ-21CFR11-006,"{""Name"":""Reproducibility"",""name"":""Reproducibility""}","21 CFR Part 11 – Audit Trail: PDF export provenance","PDF export contains the application semantic version",UNKNOWN +REQ-21CFR11-006,"{""Name"":""Reproducibility"",""name"":""Reproducibility""}","","PDF export contains a valid ISO 8601 generated-at timestamp",UNKNOWN +REQ-21CFR11-006,"{""Name"":""Reproducibility"",""name"":""Reproducibility""}","","PDF export contains the trial protocol identifier",UNKNOWN +REQ-21CFR11-006,"{""Name"":""Reproducibility"",""name"":""Reproducibility""}","","PDF export contains the PRNG seed value",UNKNOWN +REQ-EXPORT-001,"{""Name"":""Reproducibility"",""name"":""Reproducibility""}","","CSV download filename contains a date component for traceability",UNKNOWN +REQ-EXPORT-002,"{""Name"":""Reproducibility"",""name"":""Reproducibility""}","","PDF export filename matches the expected pattern",UNKNOWN +REQ-EXPORT-002,"{""Name"":""Reproducibility"",""name"":""Reproducibility""}","","should trigger a PDF download when the PDF button is clicked",UNKNOWN +REQ-ICH-E6-001,"{""Name"":""Scientific Validity"",""name"":""Scientific Validity""}","","{RND:n} produces no duplicate subject IDs across the schema",UNKNOWN +REQ-ICH-E9-001,"{""Name"":""Scientific Validity"",""name"":""Scientific Validity""}","Standalone Script","Execute scripts/cross-env/verify_python_schema.py",UNKNOWN +REQ-ICH-E9-001,"{""Name"":""Scientific Validity"",""name"":""Scientific Validity""}","ICH E9 – Law of Large Numbers: allocation ratio convergence","1:1 ratio converges to 50 % per arm across 200 Monte Carlo trials",UNKNOWN +REQ-ICH-E9-001,"{""Name"":""Scientific Validity"",""name"":""Scientific Validity""}","","should generate a schema and display results grid",UNKNOWN +REQ-ICH-E9-002,"{""Name"":""Scientific Validity"",""name"":""Scientific Validity""}","ICH E9 – Stratum Cap Enforcement: dynamic caps are never exceeded","per-stratum caps are never exceeded across 100 random seeds",UNKNOWN +REQ-ICH-E9-003,"{""Name"":""Scientific Validity"",""name"":""Scientific Validity""}","Standalone Script","Execute scripts/cross-env/verify_python_schema.py",UNKNOWN +REQ-ICH-E9-003,"{""Name"":""Scientific Validity"",""name"":""Scientific Validity""}","ICH E9 – Block Balance: strict intra-block arm balance","every block has exactly the correct count of each arm for a 1:1 ratio with block size 4",UNKNOWN +REQ-SBOM-001,"{""Name"":""Reproducibility"",""name"":""Reproducibility""}","CI Workflow","Job: sbom",UNKNOWN +REQ-ZERO-TRUST-001,"{""Name"":""Zero-Trust"",""name"":""Zero-Trust""}","Zero-Trust Architecture: no outbound network requests","schema generation produces zero outbound XHR/Fetch requests to external servers",UNKNOWN +REQ-ZERO-TRUST-001,"{""Name"":""Zero-Trust"",""name"":""Zero-Trust""}","","CSV export produces zero outbound requests to external servers",UNKNOWN +REQ-ZERO-TRUST-001,"{""Name"":""Zero-Trust"",""name"":""Zero-Trust""}","","PDF export produces zero outbound requests to external servers",UNKNOWN diff --git a/rtm.json b/rtm.json index 035c6fab..fa5187ad 100644 --- a/rtm.json +++ b/rtm.json @@ -1,216 +1,360 @@ [ { "Requirement ID": "REQ-21CFR11-001", + "Strategic Pillar": { + "Name": "Reproducibility", + "name": "Reproducibility" + }, "Suite Name": "21 CFR Part 11 – Audit Trail: generated code artifact provenance", "Test Name": "R script contains application semantic version", "Status": "UNKNOWN" }, { "Requirement ID": "REQ-21CFR11-001", + "Strategic Pillar": { + "Name": "Reproducibility", + "name": "Reproducibility" + }, "Suite Name": "", "Test Name": "Python script contains application semantic version", "Status": "UNKNOWN" }, { "Requirement ID": "REQ-21CFR11-001", + "Strategic Pillar": { + "Name": "Reproducibility", + "name": "Reproducibility" + }, "Suite Name": "", "Test Name": "SAS script contains application semantic version", "Status": "UNKNOWN" }, { "Requirement ID": "REQ-21CFR11-001", + "Strategic Pillar": { + "Name": "Reproducibility", + "name": "Reproducibility" + }, "Suite Name": "", "Test Name": "Stata script contains application semantic version", "Status": "UNKNOWN" }, { "Requirement ID": "REQ-21CFR11-002", + "Strategic Pillar": { + "Name": "Reproducibility", + "name": "Reproducibility" + }, "Suite Name": "", "Test Name": "R script contains a valid ISO 8601 generated-at timestamp", "Status": "UNKNOWN" }, { "Requirement ID": "REQ-21CFR11-002", + "Strategic Pillar": { + "Name": "Reproducibility", + "name": "Reproducibility" + }, "Suite Name": "", "Test Name": "Python script contains a valid ISO 8601 generated-at timestamp", "Status": "UNKNOWN" }, { "Requirement ID": "REQ-21CFR11-002", + "Strategic Pillar": { + "Name": "Reproducibility", + "name": "Reproducibility" + }, "Suite Name": "", "Test Name": "SAS script contains a valid ISO 8601 generated-at timestamp", "Status": "UNKNOWN" }, { "Requirement ID": "REQ-21CFR11-002", + "Strategic Pillar": { + "Name": "Reproducibility", + "name": "Reproducibility" + }, "Suite Name": "", "Test Name": "Stata script contains a valid ISO 8601 generated-at timestamp", "Status": "UNKNOWN" }, { "Requirement ID": "REQ-21CFR11-003", + "Strategic Pillar": { + "Name": "Reproducibility", + "name": "Reproducibility" + }, "Suite Name": "", "Test Name": "R script contains the trial protocol identifier", "Status": "UNKNOWN" }, { "Requirement ID": "REQ-21CFR11-003", + "Strategic Pillar": { + "Name": "Reproducibility", + "name": "Reproducibility" + }, "Suite Name": "", "Test Name": "Python script contains the trial protocol identifier", "Status": "UNKNOWN" }, { "Requirement ID": "REQ-21CFR11-003", + "Strategic Pillar": { + "Name": "Reproducibility", + "name": "Reproducibility" + }, "Suite Name": "", "Test Name": "SAS script contains the trial protocol identifier", "Status": "UNKNOWN" }, { "Requirement ID": "REQ-21CFR11-003", + "Strategic Pillar": { + "Name": "Reproducibility", + "name": "Reproducibility" + }, "Suite Name": "", "Test Name": "Stata script contains the trial protocol identifier", "Status": "UNKNOWN" }, { "Requirement ID": "REQ-21CFR11-003", + "Strategic Pillar": { + "Name": "Reproducibility", + "name": "Reproducibility" + }, "Suite Name": "", "Test Name": "results header displays the protocol identifier", "Status": "UNKNOWN" }, { "Requirement ID": "REQ-21CFR11-004", + "Strategic Pillar": { + "Name": "Reproducibility", + "name": "Reproducibility" + }, "Suite Name": "", "Test Name": "R script contains the PRNG seed initialisation statement", "Status": "UNKNOWN" }, { "Requirement ID": "REQ-21CFR11-004", + "Strategic Pillar": { + "Name": "Reproducibility", + "name": "Reproducibility" + }, "Suite Name": "", "Test Name": "Python script contains the PRNG seed initialisation statement", "Status": "UNKNOWN" }, { "Requirement ID": "REQ-21CFR11-004", + "Strategic Pillar": { + "Name": "Reproducibility", + "name": "Reproducibility" + }, "Suite Name": "", "Test Name": "SAS script contains the PRNG seed initialisation statement", "Status": "UNKNOWN" }, { "Requirement ID": "REQ-21CFR11-004", + "Strategic Pillar": { + "Name": "Reproducibility", + "name": "Reproducibility" + }, "Suite Name": "", "Test Name": "Stata script contains the PRNG seed initialisation statement", "Status": "UNKNOWN" }, { "Requirement ID": "REQ-21CFR11-004", + "Strategic Pillar": { + "Name": "Reproducibility", + "name": "Reproducibility" + }, "Suite Name": "21 CFR Part 11 – Audit Trail: results grid metadata stamping", "Test Name": "results header displays the randomization seed used for the schema", "Status": "UNKNOWN" }, { "Requirement ID": "REQ-21CFR11-006", + "Strategic Pillar": { + "Name": "Reproducibility", + "name": "Reproducibility" + }, "Suite Name": "21 CFR Part 11 – Audit Trail: PDF export provenance", "Test Name": "PDF export contains the application semantic version", "Status": "UNKNOWN" }, { "Requirement ID": "REQ-21CFR11-006", + "Strategic Pillar": { + "Name": "Reproducibility", + "name": "Reproducibility" + }, "Suite Name": "", "Test Name": "PDF export contains a valid ISO 8601 generated-at timestamp", "Status": "UNKNOWN" }, { "Requirement ID": "REQ-21CFR11-006", + "Strategic Pillar": { + "Name": "Reproducibility", + "name": "Reproducibility" + }, "Suite Name": "", "Test Name": "PDF export contains the trial protocol identifier", "Status": "UNKNOWN" }, { "Requirement ID": "REQ-21CFR11-006", + "Strategic Pillar": { + "Name": "Reproducibility", + "name": "Reproducibility" + }, "Suite Name": "", "Test Name": "PDF export contains the PRNG seed value", "Status": "UNKNOWN" }, { "Requirement ID": "REQ-EXPORT-001", + "Strategic Pillar": { + "Name": "Reproducibility", + "name": "Reproducibility" + }, "Suite Name": "", "Test Name": "CSV download filename contains a date component for traceability", "Status": "UNKNOWN" }, { "Requirement ID": "REQ-EXPORT-002", + "Strategic Pillar": { + "Name": "Reproducibility", + "name": "Reproducibility" + }, "Suite Name": "", "Test Name": "PDF export filename matches the expected pattern", "Status": "UNKNOWN" }, { "Requirement ID": "REQ-EXPORT-002", + "Strategic Pillar": { + "Name": "Reproducibility", + "name": "Reproducibility" + }, "Suite Name": "", "Test Name": "should trigger a PDF download when the PDF button is clicked", "Status": "UNKNOWN" }, { "Requirement ID": "REQ-ICH-E6-001", - "Suite Name": "generateRandomizationSchema – new token syntax", + "Strategic Pillar": { + "Name": "Scientific Validity", + "name": "Scientific Validity" + }, + "Suite Name": "", "Test Name": "{RND:n} produces no duplicate subject IDs across the schema", - "Status": "PASS" + "Status": "UNKNOWN" }, { "Requirement ID": "REQ-ICH-E9-001", + "Strategic Pillar": { + "Name": "Scientific Validity", + "name": "Scientific Validity" + }, "Suite Name": "Standalone Script", "Test Name": "Execute scripts/cross-env/verify_python_schema.py", - "Status": "PASS" + "Status": "UNKNOWN" }, { "Requirement ID": "REQ-ICH-E9-001", + "Strategic Pillar": { + "Name": "Scientific Validity", + "name": "Scientific Validity" + }, "Suite Name": "ICH E9 – Law of Large Numbers: allocation ratio convergence", "Test Name": "1:1 ratio converges to 50 % per arm across 200 Monte Carlo trials", - "Status": "PASS" + "Status": "UNKNOWN" }, { "Requirement ID": "REQ-ICH-E9-001", + "Strategic Pillar": { + "Name": "Scientific Validity", + "name": "Scientific Validity" + }, "Suite Name": "", "Test Name": "should generate a schema and display results grid", "Status": "UNKNOWN" }, { "Requirement ID": "REQ-ICH-E9-002", + "Strategic Pillar": { + "Name": "Scientific Validity", + "name": "Scientific Validity" + }, "Suite Name": "ICH E9 – Stratum Cap Enforcement: dynamic caps are never exceeded", "Test Name": "per-stratum caps are never exceeded across 100 random seeds", - "Status": "PASS" + "Status": "UNKNOWN" }, { "Requirement ID": "REQ-ICH-E9-003", + "Strategic Pillar": { + "Name": "Scientific Validity", + "name": "Scientific Validity" + }, "Suite Name": "Standalone Script", "Test Name": "Execute scripts/cross-env/verify_python_schema.py", - "Status": "PASS" + "Status": "UNKNOWN" }, { "Requirement ID": "REQ-ICH-E9-003", + "Strategic Pillar": { + "Name": "Scientific Validity", + "name": "Scientific Validity" + }, "Suite Name": "ICH E9 – Block Balance: strict intra-block arm balance", "Test Name": "every block has exactly the correct count of each arm for a 1:1 ratio with block size 4", - "Status": "PASS" + "Status": "UNKNOWN" }, { "Requirement ID": "REQ-SBOM-001", + "Strategic Pillar": { + "Name": "Reproducibility", + "name": "Reproducibility" + }, "Suite Name": "CI Workflow", "Test Name": "Job: sbom", "Status": "UNKNOWN" }, { "Requirement ID": "REQ-ZERO-TRUST-001", + "Strategic Pillar": { + "Name": "Zero-Trust", + "name": "Zero-Trust" + }, "Suite Name": "Zero-Trust Architecture: no outbound network requests", "Test Name": "schema generation produces zero outbound XHR/Fetch requests to external servers", "Status": "UNKNOWN" }, { "Requirement ID": "REQ-ZERO-TRUST-001", + "Strategic Pillar": { + "Name": "Zero-Trust", + "name": "Zero-Trust" + }, "Suite Name": "", "Test Name": "CSV export produces zero outbound requests to external servers", "Status": "UNKNOWN" }, { "Requirement ID": "REQ-ZERO-TRUST-001", + "Strategic Pillar": { + "Name": "Zero-Trust", + "name": "Zero-Trust" + }, "Suite Name": "", "Test Name": "PDF export produces zero outbound requests to external servers", "Status": "UNKNOWN" diff --git a/scripts/generate-rtm.mjs b/scripts/generate-rtm.mjs index 2fd4692c..4d43fcc3 100644 --- a/scripts/generate-rtm.mjs +++ b/scripts/generate-rtm.mjs @@ -45,27 +45,105 @@ const outputPath = getArg('--out') ?? 'Validation_Tracea const __filename = fileURLToPath(import.meta.url); const repoRoot = join(dirname(__filename), '..'); +// ── Strategic Pillars ────────────────────────────────────────────────────────── + +const VALID_PILLARS = [ + 'Zero-Trust', + 'Reproducibility', + 'Scientific Validity' +]; + +const PILLAR_MAPPING = { + 'Zero-Trust': 'Zero-Trust', + 'ZeroTrust': 'Zero-Trust', + 'Reproducibility': 'Reproducibility', + 'Scientific Validity': 'Scientific Validity', + 'ScientificValidity': 'Scientific Validity' +}; + // ── Regulatory requirements catalogue ───────────────────────────────────────── const REQUIREMENTS = { - 'REQ-ICH-E9-001': 'Randomization algorithm must be deterministic and reproducible from a fixed PRNG seed (ICH E9 §2.3)', - 'REQ-ICH-E9-002': 'Stratification factors must be applied correctly to the randomization schedule (ICH E9 §2.3.3)', - 'REQ-ICH-E9-003': 'Block randomization must respect declared block sizes and produce balanced allocations (ICH E9 §2.3.4)', - 'REQ-ICH-E6-001': 'GCP – Subject IDs must be unique and fully traceable to site and block (ICH E6 §4.9)', - 'REQ-ICH-E6-002': 'Site information must be captured and present in all exported records (ICH E6 §4.1)', - 'REQ-21CFR11-001': '21 CFR Part 11 – All electronic records must embed the application semantic version', - 'REQ-21CFR11-002': '21 CFR Part 11 – Electronic records must carry an ISO 8601 generation timestamp', - 'REQ-21CFR11-003': '21 CFR Part 11 – The unique protocol identifier must appear in every generated artifact', - 'REQ-21CFR11-004': '21 CFR Part 11 – Audit trail must record the exact PRNG seed used for schema generation', - 'REQ-21CFR11-005': '21 CFR Part 11 – PDF/XLSX exports must embed a SHA-256 audit hash for integrity verification', - 'REQ-21CFR11-006': '21 CFR Part 11 – PDF audit artifact must embed version, timestamp, protocol ID and PRNG seed', - 'REQ-ZERO-TRUST-001': 'No subject or schema data may be transmitted to external servers (zero-trust architecture)', - 'REQ-SBOM-001': 'A Software Bill of Materials (SBOM) must be generated for every production build', - 'REQ-EXPORT-001': 'CSV/XLSX export filename must contain an 8-digit date component for per-generation traceability', - 'REQ-EXPORT-002': 'PDF export must trigger a file download containing a properly named randomization artifact', - 'REQ-EXPORT-003': 'Excel export must produce a two-sheet workbook (Schema + Audit & Configuration)', + 'REQ-ICH-E9-001': { + description: 'Randomization algorithm must be deterministic and reproducible from a fixed PRNG seed (ICH E9 §2.3)', + pillar: 'Scientific Validity' + }, + 'REQ-ICH-E9-002': { + description: 'Stratification factors must be applied correctly to the randomization schedule (ICH E9 §2.3.3)', + pillar: 'Scientific Validity' + }, + 'REQ-ICH-E9-003': { + description: 'Block randomization must respect declared block sizes and produce balanced allocations (ICH E9 §2.3.4)', + pillar: 'Scientific Validity' + }, + 'REQ-ICH-E6-001': { + description: 'GCP – Subject IDs must be unique and fully traceable to site and block (ICH E6 §4.9)', + pillar: 'Scientific Validity' + }, + 'REQ-ICH-E6-002': { + description: 'Site information must be captured and present in all exported records (ICH E6 §4.1)', + pillar: 'Scientific Validity' + }, + 'REQ-21CFR11-001': { + description: '21 CFR Part 11 – All electronic records must embed the application semantic version', + pillar: 'Reproducibility' + }, + 'REQ-21CFR11-002': { + description: '21 CFR Part 11 – Electronic records must carry an ISO 8601 generation timestamp', + pillar: 'Reproducibility' + }, + 'REQ-21CFR11-003': { + description: '21 CFR Part 11 – The unique protocol identifier must appear in every generated artifact', + pillar: 'Reproducibility' + }, + 'REQ-21CFR11-004': { + description: '21 CFR Part 11 – Audit trail must record the exact PRNG seed used for schema generation', + pillar: 'Reproducibility' + }, + 'REQ-21CFR11-005': { + description: '21 CFR Part 11 – PDF/XLSX exports must embed a SHA-256 audit hash for integrity verification', + pillar: 'Reproducibility' + }, + 'REQ-21CFR11-006': { + description: '21 CFR Part 11 – PDF audit artifact must embed version, timestamp, protocol ID and PRNG seed', + pillar: 'Reproducibility' + }, + 'REQ-ZERO-TRUST-001': { + description: 'No subject or schema data may be transmitted to external servers (zero-trust architecture)', + pillar: 'Zero-Trust' + }, + 'REQ-SBOM-001': { + description: 'A Software Bill of Materials (SBOM) must be generated for every production build', + pillar: 'Reproducibility' + }, + 'REQ-EXPORT-001': { + description: 'CSV/XLSX export filename must contain an 8-digit date component for per-generation traceability', + pillar: 'Reproducibility' + }, + 'REQ-EXPORT-002': { + description: 'PDF export must trigger a file download containing a properly named randomization artifact', + pillar: 'Reproducibility' + }, + 'REQ-EXPORT-003': { + description: 'Excel export must produce a two-sheet workbook (Schema + Audit & Configuration)', + pillar: 'Reproducibility' + } }; +// Validate requirements registry has a direct, linked strategic pillar +for (const [reqId, reqData] of Object.entries(REQUIREMENTS)) { + if (!reqData || typeof reqData !== 'object' || !reqData.pillar) { + console.error(`[generate-rtm] ERROR: Registered requirement '${reqId}' does not have a linked strategic pillar.`); + process.exit(1); + } + const canonical = PILLAR_MAPPING[reqData.pillar]; + if (!canonical || !VALID_PILLARS.includes(canonical)) { + console.error(`[generate-rtm] ERROR: Registered requirement '${reqId}' has an unrecognized linked strategic pillar '${reqData.pillar}'.`); + process.exit(1); + } +} + + // ── File discovery ───────────────────────────────────────────────────────────── function findSpecFiles(dir) { @@ -394,31 +472,37 @@ lines.push(`| Requirements with no test coverage | ${totalReqs - coveredReqs} |` lines.push(`| Total tagged test cases | ${totalTests} |\n`); lines.push('---'); lines.push('\n## Traceability Matrix\n'); -lines.push(`| Requirement ID | Description | Test File | Line | Test Name | Suite | Status |`); -lines.push(`|---|---|---|---|---|---|---|`); +lines.push(`| Requirement ID | Strategic Pillar | Description | Test File | Line | Test Name | Suite | Status |`); +lines.push(`|---|---|---|---|---|---|---|---|`); const csvRows = []; -csvRows.push(`Requirement ID,Suite Name,Test Name,Status`); +csvRows.push(`Requirement ID,Strategic Pillar,Suite Name,Test Name,Status`); const jsonExport = []; for (const reqId of sortedReqIds) { const entries = byReq.get(reqId) ?? []; - const desc = REQUIREMENTS[reqId] ?? '*(undocumented requirement)*'; + const desc = REQUIREMENTS[reqId]?.description ?? '*(undocumented requirement)*'; + const pillar = REQUIREMENTS[reqId]?.pillar ?? '—'; if (entries.length === 0) { - lines.push(`| \`${reqId}\` | ${desc} | — | — | *(no tests tagged)* | — | ⚠️ NO COVERAGE |`); + lines.push(`| \`${reqId}\` | \`${pillar}\` | ${desc} | — | — | *(no tests tagged)* | — | ⚠️ NO COVERAGE |`); } else { for (const entry of entries) { const statusIcon = entry.status === 'PASS' ? '✅ PASS' : entry.status === 'SKIP' ? '⏭️ SKIP' : entry.status === 'UNKNOWN' ? '⬜ UNKNOWN' : '❌ FAIL'; const safeTest = entry.testName.replace(/\\/g, '\\\\').replace(/\|/g, '\\|'); const safeSuite = entry.suiteName.replace(/\\/g, '\\\\').replace(/\|/g, '\\|'); - lines.push(`| \`${reqId}\` | ${desc} | \`${entry.file}\` | ${entry.line} | ${safeTest} | ${safeSuite} | ${statusIcon} |`); + lines.push(`| \`${reqId}\` | \`${pillar}\` | ${desc} | \`${entry.file}\` | ${entry.line} | ${safeTest} | ${safeSuite} | ${statusIcon} |`); const escapeCsv = (str) => `"${str.replace(/"/g, '""')}"`; - csvRows.push(`${reqId},${escapeCsv(entry.suiteName)},${escapeCsv(entry.testName)},${entry.status}`); + const pillarObjString = JSON.stringify({ "Name": pillar, "name": pillar }); + csvRows.push(`${reqId},${escapeCsv(pillarObjString)},${escapeCsv(entry.suiteName)},${escapeCsv(entry.testName)},${entry.status}`); jsonExport.push({ "Requirement ID": reqId, + "Strategic Pillar": { + "Name": pillar, + "name": pillar + }, "Suite Name": entry.suiteName, "Test Name": entry.testName, "Status": entry.status @@ -502,19 +586,6 @@ for (const [personaName, occurrences] of foundPersonas.entries()) { } // ── Strategic Pillar Scanning & Alignment Verification ───────────────────────── -const VALID_PILLARS = [ - 'Zero-Trust', - 'Reproducibility', - 'Scientific Validity' -]; - -const PILLAR_MAPPING = { - 'Zero-Trust': 'Zero-Trust', - 'ZeroTrust': 'Zero-Trust', - 'Reproducibility': 'Reproducibility', - 'Scientific Validity': 'Scientific Validity', - 'ScientificValidity': 'Scientific Validity' -}; const foundPillars = new Map(); for (const p of VALID_PILLARS) { @@ -560,6 +631,9 @@ function findPillarTagsInLine(line) { for (const file of allSpecFiles) { const content = readFileSync(file, 'utf-8'); const fileLines = content.split('\n'); + const relFile = relative(repoRoot, file).replace(/\\/g, '/'); + const blocks = fileReqBlocks.get(relFile); + for (let i = 0; i < fileLines.length; i++) { const tags = findPillarTagsInLine(fileLines[i]); for (const tag of tags) { @@ -569,7 +643,23 @@ for (const file of allSpecFiles) { process.exit(1); } - const relFile = relative(repoRoot, file).replace(/\\/g, '/'); + // Two-Way Tag Alignment Verification + const pillarLine = i + 1; + const associatedBlock = blocks && blocks.find(b => + (pillarLine >= b.startLine - 5 && pillarLine <= b.endLine + 5) || + Math.abs(pillarLine - b.startLine) <= 5 || + Math.abs(pillarLine - b.endLine) <= 5 + ); + if (associatedBlock) { + const registryPillar = REQUIREMENTS[associatedBlock.reqId]?.pillar; + if (registryPillar) { + const canonicalRegistryPillar = PILLAR_MAPPING[registryPillar]; + if (canonicalPillar !== canonicalRegistryPillar) { + console.error(`[generate-rtm] ERROR: Mismatched tags in file '${relFile}'. Test-level requirement '${associatedBlock.reqId}' maps to registry pillar '${registryPillar}' ('${canonicalRegistryPillar}'), but the test file declares pillar '${tag}' ('${canonicalPillar}').`); + process.exit(1); + } + } + } let testName = "Associated Unit Test"; let suiteName = "Pillar Verification"; diff --git a/src/app/domain/core/validation/rtm-two-way-validation.spec.ts b/src/app/domain/core/validation/rtm-two-way-validation.spec.ts new file mode 100644 index 00000000..7a814eb9 --- /dev/null +++ b/src/app/domain/core/validation/rtm-two-way-validation.spec.ts @@ -0,0 +1,42 @@ +import { describe, it, expect } from 'vitest'; +import { spawnSync } from 'child_process'; +import { writeFileSync, existsSync, unlinkSync } from 'fs'; +import { join } from 'path'; + +describe('Two-Way Registry Verification and Alignment', () => { + const repoRoot = join(__dirname, '../../../../..'); + const tempSpecPath = join(repoRoot, 'src/app/domain/core/validation/temp-test-mismatch.spec.ts'); + + it('should pass validation when the codebase is fully aligned', () => { + const res = spawnSync('node', ['scripts/generate-rtm.mjs'], { cwd: repoRoot, encoding: 'utf-8' }); + expect(res.status).toBe(0); + }); + + it('should fail immediately with an exit error if a test-level requirement tag maps to a different strategic pillar', () => { + // 1. Write a temporary file with a mismatched pillar tag (obfuscate tags so the scanner doesn't scan this test file) + const reqPart = 'REQ-' + 'ZERO-TRUST-001'; + const pillarPart = '@' + 'pillar:Reproducibility'; + writeFileSync( + tempSpecPath, + `// [${reqPart}]\n// ${pillarPart}\ntest('mismatched zero-trust test', () => {});\n`, + 'utf-8' + ); + + try { + // 2. Run the validation engine / generator script + const res = spawnSync('node', ['scripts/generate-rtm.mjs'], { cwd: repoRoot, encoding: 'utf-8' }); + + // 3. Verify it terminates immediately with exit code 1 and outputs error + expect(res.status).toBe(1); + expect(res.stderr).toContain('Mismatched tags'); + expect(res.stderr).toContain(reqPart); + expect(res.stderr).toContain('Zero-Trust'); + expect(res.stderr).toContain('Reproducibility'); + } finally { + // 4. Clean up temporary file + if (existsSync(tempSpecPath)) { + unlinkSync(tempSpecPath); + } + } + }); +});