Quality / tfl-engine
Engine
TFL Engine — evidence
The R package that turns ADaM into ARDs and ARDs into displays: data-prep, the analysis.yaml interpreter, the owned ard.json serialization, and the gt/tfrmt/r2rtf renderers.
62reviewed requirementstfl-engine.md
74test recordsr-unit
0failing74 passing
94%requirement coverage58/62 evidenced
Requirements
| ID | Area | Requirement | Evidence type | Result |
|---|---|---|---|---|
| TFL-PREP-001 | prepare_data() excludes all screen-failure subjects (ARM == "Screen Failure") from ADSL and restricts every other prepared dataset to the surviving subjects. | 1 passing | ||
| TFL-PREP-002 | ITTFL is derived from randomisation (!is.na(RANDDT)); SAFFL is used as shipped with NA recoded to "N". | 1 passing | ||
| TFL-PREP-003 | COMPLFL and the derived DISCREAS reproduce EOSSTT exactly and partition the discontinued subjects. | 1 passing | ||
| TFL-PREP-004 | Baseline weight, height and BMI are merged onto ADSL from the ADVS records flagged ABLFL == "Y", so display code never joins subject-level data. | 1 passing | ||
| TFL-PREP-005 | Every prepared dataset is described in a manifest with row/column counts, a SHA-256 content hash, and the source package and version. | 1 passing | ||
| TFL-PREP-006 | The analysis-set registry maps analysis_set keys onto population flags and rejects unknown sets and datasets lacking the required flag. | 1 passing | ||
| TFL-PREP-007 | Treatment arms are ordered by dose, not alphabetically, in every prepared dataset. | 1 passing | ||
| TFL-SPEC-001 | An analysis method outside the documented vocabulary is rejected, naming the offending analysis and listing the known methods. | 1 passing | ||
| TFL-SPEC-002 | Missing required keys and missing per-method fields (variables, hierarchy, custom) are rejected before any {cards} call is attempted. | 2 passing | ||
| TFL-SPEC-003 | A display specification must carry a study identifier, a population label and a data cut-off — the ICH E3 header requirement — as non-empty strings. | 1 passing | ||
| TFL-SPEC-004 | Row-plan keys that YAML 1.1 silently coerces to booleans (bare n, y, no, on, off) are rejected with an explanation rather than rendered as FALSE. | 1 passing | ||
| TFL-SPEC-005 | A display row referencing an analysis the analysis spec does not define, or an id mismatch between the two specs, fails the build. | 1 passing | ||
| TFL-SPEC-006 | Every display committed to library/tfl/ validates, has matching ids, and declares a post_text variant. | 1 passing | ||
| TFL-ARD-001 | build_ard() emits one row per computed statistic, carrying the analyses[].name that produced it, and its continuous statistics equal a direct dplyr computation. | 2 passing | ||
| TFL-ARD-002 | subject_count counts distinct subjects over the analysis-set subject denominator, never over event records. | 1 passing | ||
| TFL-ARD-003 | hierarchical_count nests inner terms under their outer level, counts each subject once per level, and matches a direct dplyr computation. | 1 passing | ||
| TFL-ARD-004 | The total column is computed by the same code path as the treatment columns and is labelled with the grouping variable, not an internal constant. | 1 passing | ||
| TFL-ARD-005 | listing passes records through as one ARD row per record per listed variable, addressable by record index. | 1 passing | ||
| TFL-ARD-006 | An analysis may dispatch to a function in the display's custom.R; a missing custom function is a build failure, not a silent skip. | 1 passing | ||
| TFL-ARD-007 | An analysis filter restricts the records summarised, and a filter that is not one logical per row or references an unknown variable fails loudly. | 1 passing | ||
| TFL-ARD-008 | {cards}' per-statistic warning and error values are retained as ARD columns; statistics computed on empty groups are recorded, not dropped. | 1 passing | ||
| TFL-ARD-009 | A binding address resolves to exactly one ARD row; zero or multiple matches raise an error. | 1 passing | ||
| TFL-QNT-001 | Quartiles use the SAS-compatible type-2 quantile definition, not R's default type 7. | 1 passing | ||
| TFL-IO-001 | ard.json conforms to the owned schema: schema, display, created, provenance, rows, with the full thirteen-key row shape on every row. | 1 passing | ||
| TFL-IO-002 | The provenance envelope records spec and display hashes, per-dataset hashes and versions, the R and package environment, and the git commit. | 1 passing | ||
| TFL-IO-003 | write_ard() / read_ard() round-trip an ARD without loss; numeric statistics stay numeric. | 1 passing | ||
| TFL-IO-004 | Reading refuses a document that is not an opencsr/ard/v1 ARD; writing refuses rows missing schema columns. | 1 passing | ||
| TFL-IO-005 | Every committed ARD is readable, names only analyses its spec defines, and carries no statistic-level errors. | 1 passing | ||
| TFL-FMT-001 | Rounding is half away from zero (SAS behaviour), not R's round-half-to-even, and is stable against binary representation error. | 3 passing | ||
| TFL-FMT-002 | Proportions are scaled to percent and every statistic is rendered at its declared precision, including trailing zeros. | 2 passing | ||
| TFL-FMT-003 | The digit plan is declarative: a display-level plan applies by default and a row-level plan overrides it for variables with different collected precision. | 2 passing | ||
| TFL-RND-001 | Rendered HTML is a standalone document with inline CSS and no external stylesheet, script or CDN reference. | 1 passing | ||
| TFL-RND-002 | In-text and post-text variants render from one ARD; the in-text variant is a strict subset of the post-text variant and applies the declared percentage threshold exactly. | 2 passing | ||
| TFL-RND-003 | Columns follow the declared order, carry group headcounts, and silently omit declared columns absent from the ARD. | 1 passing | ||
| TFL-RND-004 | Every rendered display states its study, its analysis set and its data cut-off, and carries a source line. | 1 passing | ||
| TFL-RND-005 | Rendering a variant the display does not declare is an error. | 1 passing | ||
| TFL-RND-006 | Section headings left without data rows are dropped, and indentation distinguishes headings from the rows beneath them. | 1 passing | ||
| TFL-RND-007 | A listing renders one column per listed variable with the label declared in the display spec. | 1 passing | ||
| TFL-ITER-001 | regenerate() writes a complete iteration directory — spec snapshots, ard.json, rendered variants and a manifest — with spec snapshots byte-identical to the specs read. | 1 passing | ||
| TFL-ITER-002 | The iteration manifest records the actor, the change request, the regulatory identifier, the ARD hash, row counts, error counts, the rendered variants and the environment. | 1 passing | ||
| TFL-ITER-003 | A second regeneration — whether through regenerate() or regenerate_all() — allocates a new version and never overwrites an earlier one; current.json moves and the ledger grows. | 1 passing | ||
| TFL-ITER-004 | Version allocation takes the maximum of the filesystem and the ledger, so a partially written or partially deleted iteration cannot be silently overwritten. | 1 passing | ||
| TFL-ITER-005 | The committed t-ae-common ledger records a real two-iteration change-request story: differing spec and ARD hashes, a recorded request and actor, and unchanged values for the numbers the change did not touch. | 1 passing | ||
| TFL-ITER-006 | Regenerating a display whose spec id does not match its directory is an error. | 1 passing | ||
| TFL-ITER-007 | Every display in the library has a current iteration on disk, referenced by both current.json and the ledger. | 1 passing | ||
| TFL-RTF-001 | A rendered display encodes as a complete RTF document. | 1 passing | ||
| TFL-RTF-002 | Every cell of the rendered display appears in the RTF, and row labels arrive without the non-breaking-space indentation the HTML renderer uses. | 2 passing | ||
| TFL-RTF-003 | The display title, population label, footnotes, source line and column headers with their subject counts travel into the RTF. | 2 passing | ||
| TFL-RTF-004 | Listings and reduced in-text variants render as RTF as well as full summary tables do, each naming the variant it is. | 2 passing | ||
| TFL-RTF-005 | regenerate() writes an RTF beside every rendered variant and records its filename and sha256 in the iteration manifest. | 2 passing | ||
| TFL-RTF-006 | Every committed display has an RTF for each variant whose hash matches the manifest, so a hand-edited artifact fails the build. | 1 passing | ||
| TFL-VAL-001 | The values declaration validates: unique ids, a readable label on every value, and exactly one of an ARD source or a declared derivation. | 2 passing | ||
| TFL-VAL-002 | A malformed binding address or an operation outside the closed vocabulary is rejected with a message naming the value. | 1 passing | ||
| TFL-VAL-003 | Every ARD-sourced value equals its row in the committed ARD and carries that iteration's path and sha256. | 2 passing | ||
| TFL-VAL-004 | A derived value equals the arithmetic it declares over values defined before it; a forward reference is an error. | 2 passing | ||
| TFL-VAL-005 | A binding that resolves to no ARD row, or names a display with no committed iteration, fails the build rather than producing a blank value. | 1 passing | ||
| TFL-VAL-006 | Scaling and rounding are presentation only: the stored value stays the ARD's, and formatted carries the display format. | 1 passing | ||
| TFL-VAL-007 | The committed store matches a fresh build of the declaration, value for value. | 1 passing | ||
| TFL-QC-001 | Every testthat test name matches <REQ-ID>[, <REQ-ID>]: <description> (#<issue>). | no test | ||
| TFL-QC-002 | Every requirement ID cited by a test exists in a matrix under quality/requirements/. | no test | ||
| TFL-QC-003 | Every requirement in a matrix is cited by at least one test — no untested claims. | no test | ||
| TFL-QC-004 | Requirement matrices use the five columns ID, Requirement, Type, Verification and Status, with five cells on every requirement row. | no test |
Test records
| Result | Suite | Test | Requirements | File |
|---|---|---|---|---|
| pass | r-unit | DSP-ALL-001: every display renders a table that actually contains numbers (#1) | DSP-ALL-001 | test-displays.R |
| pass | r-unit | DSP-ALL-002: every display declares a regulatory identifier and a source line (#1) | DSP-ALL-002 | test-displays.R |
| pass | r-unit | TFL-ARD-001: build_ard returns one row per computed statistic, tagged with its analysis (#1) | TFL-ARD-001 | test-ard-build.R |
| pass | r-unit | TFL-ARD-001: continuous statistics equal a direct dplyr computation (#1) | TFL-ARD-001 | test-ard-build.R |
| pass | r-unit | TFL-ARD-002: subject_count uses the analysis-set subject denominator (#1) | TFL-ARD-002 | test-ard-build.R |
| pass | r-unit | TFL-ARD-003: hierarchical_count nests preferred terms under their SOC (#1) | TFL-ARD-003 | test-ard-build.R |
| pass | r-unit | TFL-ARD-004: the total column is produced by the same code path as the arms (#1) | TFL-ARD-004 | test-ard-build.R |
| pass | r-unit | TFL-ARD-005: listing passes records through as one row per variable (#1) | TFL-ARD-005 | test-ard-build.R |
| pass | r-unit | TFL-ARD-006: an analysis may dispatch to a function in custom.R (#1) | TFL-ARD-006 | test-ard-build.R |
| pass | r-unit | TFL-ARD-007: an analysis filter restricts the records it summarises (#1) | TFL-ARD-007 | test-ard-build.R |
| pass | r-unit | TFL-ARD-008: cards' per-statistic warning and error columns survive into the ARD (#1) | TFL-ARD-008 | test-ard-build.R |
| pass | r-unit | TFL-ARD-009: a binding address must resolve to exactly one ARD row (#1) | TFL-ARD-009 | test-ard-build.R |
| pass | r-unit | TFL-FMT-001: round_half_up preserves NA, Inf and vector length (#1) | TFL-FMT-001 | test-formatting.R |
| pass | r-unit | TFL-FMT-001: round_half_up rounds half away from zero, unlike base::round (#1) | TFL-FMT-001 | test-formatting.R |
| pass | r-unit | TFL-FMT-001: round_half_up survives binary representation error (#1) | TFL-FMT-001 | test-formatting.R |
| pass | r-unit | TFL-FMT-002: format_stat applies the collected-precision defaults (#1) | TFL-FMT-002 | test-formatting.R |
| pass | r-unit | TFL-FMT-002: format_stat scales proportions to percent and pads decimals (#1) | TFL-FMT-002 | test-formatting.R |
| pass | r-unit | TFL-FMT-003: format_stat honours an explicit digit plan (#1) | TFL-FMT-003 | test-formatting.R |
| pass | r-unit | TFL-FMT-003: the row-level digit plan overrides the display digit plan (#1) | TFL-FMT-003 | test-formatting.R |
| pass | r-unit | TFL-IO-001: ard.json matches the owned schema, top to bottom (#1) | TFL-IO-001 | test-ard-io.R |
| pass | r-unit | TFL-IO-002: the provenance envelope is complete and machine-checkable (#1) | TFL-IO-002 | test-ard-io.R |
| pass | r-unit | TFL-IO-003: write_ard / read_ard round-trip an ARD without loss (#1) | TFL-IO-003 | test-ard-io.R |
| pass | r-unit | TFL-IO-004: reading refuses a document that is not an opencsr ARD (#1) | TFL-IO-004 | test-ard-io.R |
| pass | r-unit | TFL-IO-005: every committed ARD is readable and internally consistent (#1) | TFL-IO-005 | test-ard-io.R |
| pass | r-unit | TFL-ITER-001: regenerate writes a complete, self-describing iteration (#1) | TFL-ITER-001 | test-regenerate.R |
| pass | r-unit | TFL-ITER-002: the manifest records who, why, from what and how many (#1) | TFL-ITER-002 | test-regenerate.R |
| pass | r-unit | TFL-ITER-003: a second regeneration never overwrites the first (#1) | TFL-ITER-003 | test-regenerate.R |
| pass | r-unit | TFL-ITER-004: version allocation is robust to ledger and filesystem drift (#1) | TFL-ITER-004 | test-regenerate.R |
| pass | r-unit | TFL-ITER-005: the committed t-ae-common ledger tells a two-iteration change-request story (#1) | TFL-ITER-005 | test-regenerate.R |
| pass | r-unit | TFL-ITER-006: regenerate refuses specs whose ids disagree with each other or the directory (#1) | TFL-ITER-006 | test-regenerate.R |
| pass | r-unit | TFL-ITER-007: every display in the library has a current iteration on disk (#1) | TFL-ITER-007 | test-regenerate.R |
| pass | r-unit | TFL-PREP-001: screen failures are excluded from every prepared dataset (#1) | TFL-PREP-001 | test-data-prep.R |
| pass | r-unit | TFL-PREP-002: ITTFL is derived from randomisation, SAFFL is used as shipped (#1) | TFL-PREP-002 | test-data-prep.R |
| pass | r-unit | TFL-PREP-003: COMPLFL and DISCREAS reconstruct EOSSTT exactly (#1) | TFL-PREP-003 | test-data-prep.R |
| pass | r-unit | TFL-PREP-004: baseline vitals are merged from ADVS onto ADSL (#1) | TFL-PREP-004 | test-data-prep.R |
| pass | r-unit | TFL-PREP-005: the manifest describes every prepared dataset with a sha256 hash (#1) | TFL-PREP-005 | test-data-prep.R |
| pass | r-unit | TFL-PREP-006: the analysis-set registry rejects unknown sets and applies flags (#1) | TFL-PREP-006 | test-data-prep.R |
| pass | r-unit | TFL-PREP-007: treatment arms are ordered by dose, not alphabetically (#1) | TFL-PREP-007 | test-data-prep.R |
| pass | r-unit | TFL-QNT-001: quartiles follow the SAS-compatible type-2 definition (#1) | TFL-QNT-001 | test-ard-build.R |
| pass | r-unit | TFL-RND-001: rendered HTML is standalone — no CDN, no script, no external asset (#1) | TFL-RND-001 | test-render.R |
| pass | r-unit | TFL-RND-002: the 5% threshold keeps exactly the terms that reach it (#1) | TFL-RND-002 | test-render.R |
| pass | r-unit | TFL-RND-002: the in-text variant is a strict subset of the post-text variant (#1) | TFL-RND-002 | test-render.R |
| pass | r-unit | TFL-RND-003: columns follow the declared order and carry group counts (#1) | TFL-RND-003 | test-render.R |
| pass | r-unit | TFL-RND-004: every display states study, analysis set and data cut-off (#1) | TFL-RND-004 | test-render.R |
| pass | r-unit | TFL-RND-005: rendering rejects a variant the display does not declare (#1) | TFL-RND-005 | test-render.R |
| pass | r-unit | TFL-RND-006: section headings without data rows are dropped, indentation is preserved (#1) | TFL-RND-006 | test-render.R |
| pass | r-unit | TFL-RND-007: a listing renders one column per listed variable with its label (#1) | TFL-RND-007 | test-render.R |
| pass | r-unit | TFL-RTF-001: a rendered display encodes as a complete RTF document (#1) | TFL-RTF-001 | test-rtf.R |
| pass | r-unit | TFL-RTF-002: every cell of the rendered display survives into the RTF (#1) | TFL-RTF-002 | test-rtf.R |
| pass | r-unit | TFL-RTF-002: row labels arrive without the HTML indentation trick (#1) | TFL-RTF-002 | test-rtf.R |
| pass | r-unit | TFL-RTF-003: column headers carry the treatment arms and their subject counts (#1) | TFL-RTF-003 | test-rtf.R |
| pass | r-unit | TFL-RTF-003: the title, population, footnotes and source line travel with the table (#1) | TFL-RTF-003 | test-rtf.R |
| pass | r-unit | TFL-RTF-004: a listing renders as RTF as well as a summary table does (#1) | TFL-RTF-004 | test-rtf.R |
| pass | r-unit | TFL-RTF-004: the in-text variant renders its own, smaller RTF (#1) | TFL-RTF-004 | test-rtf.R |
| pass | r-unit | TFL-RTF-005: regenerate writes an RTF beside every rendered variant (#1) | TFL-RTF-005 | test-rtf.R |
| pass | r-unit | TFL-RTF-005: the manifest records each variant's RTF and its hash (#1) | TFL-RTF-005 | test-rtf.R |
| pass | r-unit | TFL-RTF-006: every committed display has an RTF whose hash matches its manifest (#1) | TFL-RTF-006 | test-rtf.R |
| pass | r-unit | TFL-SPEC-001: an unknown analysis method is rejected by name (#1) | TFL-SPEC-001 | test-spec-validation.R |
| pass | r-unit | TFL-SPEC-002: missing required analysis keys are reported together (#1) | TFL-SPEC-002 | test-spec-validation.R |
| pass | r-unit | TFL-SPEC-002: per-method required fields are enforced (#1) | TFL-SPEC-002 | test-spec-validation.R |
| pass | r-unit | TFL-SPEC-003: a display must identify its study, population and data cut-off (#1) | TFL-SPEC-003 | test-spec-validation.R |
| pass | r-unit | TFL-SPEC-004: bare YAML `n` read as a boolean is rejected with an explanation (#1) | TFL-SPEC-004 | test-spec-validation.R |
| pass | r-unit | TFL-SPEC-005: a display row naming an unknown analysis fails the build (#1) | TFL-SPEC-005 | test-spec-validation.R |
| pass | r-unit | TFL-SPEC-006: every committed display in the library validates (#1) | TFL-SPEC-006 | test-spec-validation.R |
| pass | r-unit | TFL-VAL-001: a declaration missing a label, an id or a source is rejected (#1) | TFL-VAL-001 | test-values.R |
| pass | r-unit | TFL-VAL-001: the committed declaration validates and every value is uniquely named (#1) | TFL-VAL-001 | test-values.R |
| pass | r-unit | TFL-VAL-002: a malformed binding address or unknown operation is rejected (#1) | TFL-VAL-002 | test-values.R |
| pass | r-unit | TFL-VAL-003: every ARD-sourced value equals its row in the committed ARD (#1) | TFL-VAL-003 | test-values.R |
| pass | r-unit | TFL-VAL-003: every value carries the iteration and hash of the ARD it came from (#1) | TFL-VAL-003 | test-values.R |
| pass | r-unit | TFL-VAL-004: a derivation naming a value declared after it is an error (#1) | TFL-VAL-004 | test-values.R |
| pass | r-unit | TFL-VAL-004: a derived value equals the arithmetic it declares (#1) | TFL-VAL-004 | test-values.R |
| pass | r-unit | TFL-VAL-005: an unresolvable binding fails the build rather than producing a blank value (#1) | TFL-VAL-005 | test-values.R |
| pass | r-unit | TFL-VAL-006: percentages are scaled and rounded at presentation, never in the store (#1) | TFL-VAL-006 | test-values.R |
| pass | r-unit | TFL-VAL-007: the committed store matches a fresh build of the declaration (#1) | TFL-VAL-007 | test-values.R |
Traceability
No generated output yet, so the data → ARD → display chain is empty.
Provenance
- Generated
- 2026-07-27T04:44:02.378Z
- Environment
- os Darwin 23.6.0 · node v24.14.0 · vitest 2.1.9 · r 4.3.3
- CI run
- Not recorded for this evidence set