CLAUDE.md says this table is the signal to the ferx-core team about what to
prioritise. It had gone materially stale: seven rows asked for ferx features that
ferx already has, and two of those this package already translates. A stale
"untranslatable" row is worse than no row -- it asks another team to build
something that exists, and it understates what the translator can do.
Re-measured against ferx-core origin/main (ae6c208c) and ferx-r's own example
models. Corrections, each with its evidence recorded in the table:
- Multiple DVIDs / endpoints, and PD structural models (Emax, indirect response),
are not merely supported by ferx -- WE ALREADY TRANSLATE THEM. A ferx-r example
model carries `y[CMT=2]` / `y[CMT=3]` / `CMT=2: DV ~ ...` and an Emax readout,
and pkpd_cmt.mod, pkpd_ir.mod and qss_tmdd.mod all translate with zero
$unsupported entries.
- `$SIGMA BLOCK` correlated residuals: ferx has `block_sigma`,
`validate_block_sigma_single_error_order()` and `build_residual_correlations()`.
- `R1`/`D1`, `ALAG2`/`ALAG3`, `F2`/`F3`: `DoseAttr::from_indexed_name` accepts
f / lagtime / alag / d / r with a compartment index. These are #16's payload.
- `THETA (FIX)` was listed as lossy. It is not: ferx's theta regex takes FIX inside
or outside the parens, and `.emit_theta()` already emits it. Measured.
The heading conflated two things with different owners, so they are now separate
tables: "ferx cannot express it" is a ferx-core ask; "ferx supports it, we do not
translate it yet" is our backlog.
MIXTURE and categorical/count/TTE are left in place but flagged SUSPECT rather
than deleted -- ferx-core#996 landed mixture mu-referencing yesterday and there
are src/categorical, src/survival and src/markov modules, so both rows are
probably wrong, but I did not measure them and removing a row on a hunch is the
same error as keeping a stale one.
One new gap found while measuring, and it is silent: `$OMEGA ... FIX` emits a
FREE omega. nonmem2rx surfaces the flag (`ui$iniDf$fix` is TRUE), ferx accepts
`omega NAME ~ value FIX` (validated clean), and `.emit_omega()` simply has no fix
handling -- so a pinned IIV silently becomes an estimated one, with no warning and
no $unsupported entry. `$SIGMA ... FIX` is presumed the same and is marked as not
separately measured.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Why
CLAUDE.md says the "ferx feature roadmap" table is the signal to the ferx-core team about what to prioritise. It had gone materially stale: seven rows asked for ferx features that ferx already has, and two of those this package already translates.
That is not cosmetic in either direction. A stale
Untranslatablerow asks another team to build something that exists, and it understates what the translator can do to anyone reading the plan to decide whether this tool fits their model.Re-measured against ferx-core
origin/main(ae6c208c) and ferx-r's own example models, which CLAUDE.md names as the authority on what the format accepts.What changed
The heading conflated two things with different owners, so they are now two tables:
Every row now carries its evidence, and a row whose evidence is older than this pass says so rather than being silently carried forward.
Rows corrected:
y[CMT=2],y[CMT=3],CMT=2: DV ~ proportional(...);pkpd_cmt.modandqss_tmdd.modtranslate with zero$unsupportedpkpd_ir.modtranslates with zero$unsupportedS2,S3per-compartment scalingobs_scale/y[CMT=N](#24, #25)$SIGMA BLOCKcorrelated residualsblock_sigma,validate_block_sigma_single_error_order(),build_residual_correlations()R1/D1,ALAG2/ALAG3,F2/F3DoseAttr::from_indexed_nameacceptsf/lagtime/alag/d/rwith a compartment index -- these are #16's payload, not a ferx gapTHETA (FIX)FIXinside or outside the parens, and.emit_theta()already emits it. Measured:$THETA 1.2 FIX->theta THETA3(1.2, -1e15, 1e15, FIX), no warningRows deliberately kept but flagged SUSPECT:
MIXTUREand categorical/count/TTE. ferx-core#996 landed mixture mu-referencing on 2026-08-20 and there aresrc/categorical,src/survivalandsrc/markovmodules, so both are probably wrong -- but I did not measure them, and deleting a row on a hunch is the same error as keeping a stale one. Flagged so the next person measures instead of trusting.Alternatives considered
Delete the rows that look obsolete. Rejected for the two I could not measure -- see above. The whole failure being fixed here is a table asserting things nobody checked.
Fold this into a code PR. Rejected: one PR per logical change, and this one is worth reviewing on its own because it changes what another team is being asked to build.
Formats affected
Documentation only. No code, no tests, no emitted output changes.
ferx format dependency
.ferxsyntax generatedBreaking changes
Tests
R CMD check --as-cran-- unaffected;plans/is not part of the built packageDocumentation
roxygen2::roxygenize()-- noR/changeNEWS.md-- not added; this is an internal planning document with no user-visible effectReviewer hints
The two SUSPECT rows are the judgement call worth a second opinion: keeping a row I believe is wrong, flagged, versus deleting it unmeasured. I chose flagged.
Skimmable: the evidence column on rows marked DONE -- those are corroborated by the corpus sweep, which reports zero
$unsupportedacross all 18 bundled models.Open questions
One new gap found while measuring, and it is silent.
$OMEGA ... FIXemits a free omega:No warning, no
$unsupportedentry. nonmem2rx does surface the flag (ui$iniDf$fixisTRUEforeta1), ferx does acceptomega NAME ~ value FIX(validated clean on the pinned engine), and.emit_omega()simply has no fix handling where.emit_theta()does. So a source that pins an IIV -- to a literature value, say -- silently gets one that is estimated instead.$SIGMA ... FIXis presumed identical and is marked as not separately measured. Recorded in the new table; not filed as an issue yet.