--- title: "Migrating from FACETS to mfrmr" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Migrating from FACETS to mfrmr} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r, include = FALSE} is_cran_check <- !isTRUE(as.logical(Sys.getenv("NOT_CRAN", "false"))) knitr::opts_chunk$set( collapse = TRUE, comment = "#>", fig.width = 7, fig.height = 5, eval = !is_cran_check ) ``` This vignette walks FACETS users through the closest `mfrmr` workflow: preparing data, fitting an `RSM`/`PCM` many-facet Rasch-family model with FACETS-oriented settings, generating related diagnostic and reporting tables, and reviewing the output-contract boundary between the two systems. `GPCM` can be fit in `mfrmr`, but its slope-aware score semantics are intentionally outside the score-side FACETS output-contract route. The local numerical comparison uses FACETS 64-bit 4.5.0. The online update history lists 4.5.3 (October 2026); a current web manual is not a version-pinned executable specification. The 4.5.1 history says the gtheory interface was replaced while the linked help still names gtheory. Keep the executable, manual edition and statistical decomposition distinct. The bounded executed comparisons below do not certify all FACETS options or versions. ## Mental model The two stacks share the same psychometric framework but differ in operating model. Before treating a legacy workflow as covered, inspect the public coverage boundary: ```r facets_feature_coverage() facets_feature_coverage("not_implemented") ``` This matrix describes the availability of package-native output surfaces. `implemented` does not by itself mean that the two programs use the same estimand, conditioning, extreme-score rule, degrees of freedom, or numerical contract. | Concept | FACETS (Linacre 2026) | mfrmr | |---|---|---| | Input | Specification file plus data file | `data.frame` in long format | | Estimation | JMLE by default | `MML` by default; `JML` is the closest estimation route for a JMLE-oriented comparison | | Fit-statistic basis | Residuals at JMLE estimates | Residuals at EAP person measures under `MML` (shrunken toward the mean); refit with `method = "JML"` for a JMLE-style residual basis | | Models | Multiple model statements, rating scales, partial credit, and other response families can coexist | One response-model family per fit: `RSM`, `PCM`, or `GPCM` | | Output | Tables 0-30 plus graphic files | Returned R objects with `summary()` and `plot()` methods | | Anchoring | Element/group anchors, rating-scale calibration, and reusable starting values | Element and group anchors; no general threshold/scale anchors or fixed-calibration starting-value bundle | | Repeated cells | Multiple observations may be represented within a design cell | Exact Person-by-facet duplicates are retained but force Data review; distinguish legitimate repeats with an event/occasion facet | | Bias / interaction | Table 13 cell effects; Table 14 pairwise comparisons | `estimate_bias()` and `bias_interaction_report()`; conditional screens with the differences below | | Wright map / variable map | Graphic variable-map output | `plot(fit, type = "wright")` and `plot()` | | Fair average | Table 7 fair-M average | `fair_average_table()` | | Reproducibility | Specification, input data, FACETS version, and recorded run environment/settings | `build_mfrm_manifest()` plus `build_mfrm_replay_script()` | ## Visualize an existing FACETS analysis `read_facets_results()` reads supported Scorefile exports without refitting. The included small synthetic example has one row per facet element, with fixed anchors and extreme scores. Inspect `summary()` for available measures and fit statistics; these counts do not assess the quality of the source analysis. ```{r facets-import-data} library(mfrmr) score_directory <- system.file("extdata", "facets-example", package = "mfrmr") facets_result <- read_facets_results(score_directory, facet_map = c("Person", "Rater", "Task"), source_version = "4.5.0") summary(facets_result) facets_result$table[, c("Facet", "ElementID", "Label", "Estimate", "SourceStatus")] ``` ```{r facets-import-measures, fig.alt="Reported FACETS measures for six synthetic Persons. Downward and upward triangles mark extreme-score adjustments, a square marks a direct/group anchor flag whose subtype is not determined by the import, and circles mark estimated measures. No confidence intervals are shown."} plot(facets_result, type = "measure", facets = "Person") ``` Squares identify FACETS `Status=1`, which can denote a direct anchor or a member of a group anchor. The import retains `SourceStatus = "anchored"`, but this does **not** establish that the individual's measure was fixed. A group member may be estimated around the constrained mean. `GroupRaw` can also be a reporting group; its value, the number of imported group members and a reported SE cannot identify the anchor subtype. Inspect the original `Labels=` for the anchor mode and [Table 7](https://www.winsteps.com/facetman64/table7.htm) for row annotations. A Table 7 G annotation alone need not distinguish the G and X input modes. `summary()` therefore calls this count `AnchorFlagged`, replacing `Anchored` from earlier candidate versions. For saved imports, call `plot()` again to refresh the legend and notes; the source values and availability remain intact. Extreme members of G/X groups can carry Status -2/-3 instead of 1, so `AnchorFlagged` is not a count of every member of an anchored group. Their triangles identify the source's extreme-score status. A finite reported location does not establish which adjustment produced it: G and X can report different locations. The import preserves these locations and marginal SEs, while keeping their Infit/Outfit placeholders unavailable for fit plots. `type = "fit"` shows measure versus Infit or Outfit; `type = "infit_outfit"` compares the two mean squares. Inspect the plotted rows and unavailable reasons before interpreting missing points: ```{r facets-import-availability} fit_points <- plot_data(facets_result, component = "plot_data", type = "infit_outfit", facets = "Person") fit_points[, c("Label", "Infit", "Outfit", "Available", "Reason")] plot_data(facets_result, component = "notes") ``` For your own analysis, supply named comma/tab/semicolon files, a vector of per-facet exports, or a directory of `score.N.txt` files. Use the actual source encoding (for example `encoding = "CP932"` for an applicable Japanese Windows export). For FACETS text output, the following specifications retain the column headings and quoted labels needed by the reader: ```text Scorefile = score.txt Heading lines = Yes QM = Double CSV = Semicolons ``` `CSV = Commas` or `CSV = Tabs` selects the other supported separators. Keep the generated facet heading line; the Scorefile reader finds the column header. Check that the files exist and that their labels are intact before importing. A successful process exit alone does not establish a successful export. The reader's `encoding` converts an existing file; it neither changes FACETS input settings nor restores labels already lost in the external output. You can keep FACETS' exported IDs and assign local display names in R. The map uses the imported `Facet` and `ElementID`, not row positions or source labels. Store IDs as text to retain any leading zeros. Here only two Persons are renamed; other elements retain their original labels: ```{r facets-import-display-labels} display_names <- data.frame(Facet = "Person", ElementID = c("1", "2"), DisplayLabel = c("受検者 甲", "受検者 乙")) labelled <- plot(facets_result, facets = "Person", label_map = display_names, draw = FALSE) labelled$data$plot_data[, c("ElementID", "Label", "DisplayLabel")] ``` The original result and its labels remain unchanged; `labelled$data$label_map` retains the supplied correspondence. Unknown or repeated keys are errors. Repeated display names are distinguished by element key. Only measure plots draw element labels; other element plots keep the names in their returned data. This map does not apply to Graphfile category labels. Japanese text also needs a graphics device and font with the required glyphs. With Cairo support and Meiryo installed on Windows, save the plot as follows; choose an available Japanese font on another system. Check the saved file for missing glyphs. This does not require Japanese input to FACETS or change its results. See R's [Cairo device documentation](https://stat.ethz.ch/R-manual/R-devel/library/grDevices/html/cairo.html). ```{r facets-import-save-japanese, eval=FALSE} png("facets-japanese.png", width = 7, height = 5, units = "in", res = 200, type = "cairo", family = "Meiryo") plot(facets_result, facets = "Person", label_map = display_names) dev.off() cairo_pdf("facets-japanese.pdf", width = 7, height = 5, family = "Meiryo") plot(facets_result, facets = "Person", label_map = display_names) dev.off() ``` Fixed-width imports require the supported ASCII all-field layout. Element IDs distinguish duplicate labels. Source signs and reported values are preserved; no automatic recentering or sign reversal takes place. A point-only Wright map (`type = "wright"`) additionally requires `common_scale = TRUE` at import after checking compatible units and origins in the original FACETS specification. Extreme-score rows are flagged; their status does not identify the adjustment rule used by FACETS. Their fit values can be placeholders and are unavailable in fit plots. Source SEs are retained in the table; no confidence intervals are constructed. These descriptive results do not provide new-person scoring, threshold reconstruction or validation of the FACETS analysis. `saveRDS(facets_result, ...)` preserves the complete result; `as.data.frame(facets_result)` exports its element table. See `?read_facets_results` and `?plot.mfrm_facets_results` for the supported scope. For category-probability and expected-score curves, export a FACETS Graphfile with headings. This separate synthetic PCM example contains one coordinate row per scale and reported measure; it is a different analysis from the binary score example above. ```{r facets-import-graph} graph_path <- system.file("extdata", "facets-graph-example", "graph.txt", package = "mfrmr") facets_curves <- read_facets_results(graph_file = graph_path, source_version = "4.5.0") summary(facets_curves, component = "curves") ``` ```{r facets-import-category-curves, fig.alt="Four reported category-probability curves for Scale 1 of a synthetic FACETS PCM analysis, on the original Graphfile measure axis. Categories 0 through 3 are distinguished by color and line type. Lines join exported points; there are no confidence intervals."} plot(facets_curves, type = "ccc", scales = 1) ``` Use `type = "expected"` for the exported expected-score curve. Inspect the actual coordinates and source-consistency checks with: ```{r facets-import-graph-values} expected_points <- plot_data(facets_curves, "plot_data", type = "expected", scales = 1) head(expected_points[, c("Scale", "Measure", "Value", "Available", "Reason")]) head(facets_curves$graph$diagnostics) ``` The reader preserves Graphfile scale numbers and category codes. A scale number identifies a Table 8 subtable; it does not automatically identify an item. Even binary exports may use category codes 1/2 instead of the original 0/1 responses. Check the source specification before interpreting expected scores. The graph Measure axis is separate from the element measure declarations; importing both files does not align them. When both exports belong to your analysis, supply `file = ...` and `graph_file = ...` in one call, after checking that correspondence. Missing or invalid coordinates remain in `as.data.frame(facets_curves, component = "curves")` and break the plotted lines. Probability sums are checked using exported decimal precision, without renormalizing. These checks do not certify model fit; connecting segments do not calculate new model predictions. ## Why results and decisions can differ Match the statistical question before comparing numbers. A FACETS-style table name does not guarantee the same calculation. For an existing FACETS study, first align the response model, retained categories, orientation, anchors and centering, observations and replication weights, and extreme-score treatment. Compare unrounded logits before user scaling. Then compare response probabilities, mean squares, df and standardization separately. FACETS uses [score-residual and measure-change convergence criteria](https://www.winsteps.com/facetman64/convergencecriteria.htm); an optimizer's success code alone does not establish agreement with them. The corrected candidate has a bounded executed comparison against FACETS 4.5.0: complete binary ordinary JML data with unit weights and matched signs. Locations, SEs, MnSq, PTMEA and WHEXACT YES/NO agree within external report precision; bias estimates, baseline expected scores and the zero-bias summary also agree. This is not a drop-in equivalence claim for other models or options. | Quantity | Current definition | Consequence for interpretation | |---|---|---| | Fit ZSTD | Diagnostics default to FACETS fourth-moment df. `whexact = FALSE` is YES and `TRUE` is NO; both use the cube root. | Use `fit_df_method = "engine"` and `whexact = "legacy_linear"` to reproduce the former engine/TRUE convention. `"both"` exposes df sensitivity. Cutoff decisions may change. | | Point-measure correlation | PTMEA now uses the oriented measure sum for each response, including Person rows. | Old Person-only correlations must be recomputed. | | Displacement | Updates now include facet orientation and the score derivative. | Negative-facet released estimates now move in the correct coordinate; this is still a conditional one-step diagnostic. | | Facet chi-square | The statistic and df use the same finite estimates and positive finite SEs. | Excluded levels are reported; a missing SE cannot silently contribute df. | | Expected point-measure correlation | Ordinary additive RSM/PCM JML exposes `PTMEAExpected` and `PTMEAExpectedAsymptotic`. The former includes Linacre's published `(N-2)/N` variance correction with frequency count `N`. | Matched PCM output agrees within print precision. This is a conditional reporting approximation; below count two or with unbounded/incomplete calibration, the corrected reference remains unavailable. Recreate diagnostics to obtain it. | | PCM fair averages | Expectations under administered scales are averaged using response weights. | Averaging thresholds first can give different scores and is no longer used. | | Bias cell tail area | Continuous weighted count minus 1 is retained; `DFDisplay` separately rounds positive df for display comparison. FACETS 4.5.0 reports count 8 and df 7 in the executed example, although Table 13 says count minus 2. | Checked fractional-count native tails match an integer-df finite sum extended outside its valid domain and can change a 0.05 screen. mfrmr retains the continuous Student reference and records probability availability; nonpositive df withhold the tail. These are conditional screens, not calibrated fairness tests. | | Bias expected score | `Expctd Score` and `Obs-Exp Average` now refer to the main analysis. | Separate after-bias columns show how much of that departure the shift explains. | | Bias summary chi-square | Tests the cells against zero with df equal to the cell count. | A common nonzero bias no longer disappears through centering. | | Pairwise bias | Uses `target + bias/TargetSign`, conditional cell SEs and modified Rasch-Welch df rounded down, matching FACETS 4.5.0 unequal-count results. | `df_method = "classical"` retains the earlier continuous Welch reference. Missing references withhold comparisons; neither mode includes calibration uncertainty. | | Unexpected responses | Defaults to `abs(z) >= 3`, `rule = "residual"`. | The older probability OR rule is opt-in; rankings now treat tied modes symmetrically. | Definitions are given in the official [WHEXACT](https://www.winsteps.com/facetman64/whexact.htm), [point-measure](https://www.winsteps.com/facetman64/gstats.htm), [Table 7](https://www.winsteps.com/facetman64/table7.htm), [fair-average](https://www.winsteps.com/facetman64/fairaverage.htm), and [Table 13](https://winsteps.com/facetman64/table13.htm) documentation. The count-minus-1 observation above describes the executed version and example, not a correction to the external manual or a claim about every FACETS version. Recompute `diagnose_mfrm()` from a saved fit, then recreate `estimate_bias()` and reports. Corrected native diagnostics and bias results carry `calculation_version = 2`; do not add that field manually to old values. These diagnostic changes do not require refitting the calibration. For a negatively oriented rater, the model predictor contains `-severity + bias`, so its context-specific severity is `severity - bias`. The [Table 14 examples](https://www.winsteps.com/facetman64/table14.htm) make this direction explicit. Reversing the contrast leaves a two-sided tail area unchanged but reverses the substantive feedback. Pairwise df also needs its own version specification: the current [t-statistics manual](https://www.winsteps.com/facetman64/t-statistics.htm) describes a 2025 modification alongside classical Welch. The executed FACETS 4.5.0 unequal-count comparison confirms the modified formula and integer truncation for the tail area. `DFUnrounded` and `DFMethod` retain that choice; `df_method = "classical"` reproduces the earlier reference. Corrected pairwise tables carry `CalculationVersion = 3`. Recreate `build_fixed_reports()` and `bias_pairwise_report()` from the current bias bundle. First recreate old bias bundles from the saved fit and current diagnostics; no main calibration refit is needed. Historical report values are not automatically corrected by reloading them. Local and contrast SEs describe conditional cell uncertainty; calibration uncertainty and cross-cell covariance are not propagated. The earlier claim that these contrasts were generally conservative was incorrect. Check `ScreenUnavailable` in the pairwise summary before reading `Flagged`; the summary covers the full family even when `top_n` limits the table. The [Table 4 rule](https://www.winsteps.com/facetman64/table4.htm) and its [default threshold](https://www.winsteps.com/facetman64/unexpected.htm) now match the native residual-only default. To retain the exploratory probability screen, request `abs_z_min = 2, prob_max = .30, rule = "either"`. A fixed .30 cutoff has no universal interpretation: five equally likely categories each have probability .2, so it flags every response. For tied modes, `MostLikely` is missing, `MostLikelyCount` records the tie and `CategoryGap` uses the nearest mode. This ranking cannot decide which rater is unfair or which ratings to remove. Category reports now distinguish the observed generating-measure average (`AvgMeasure`), model-expected generating-measure average (`ExpectedMeasure`), Person-only mean and expected response score. Category residual MnSq is descriptive: conditioning on the observed category removes the universal unit baseline, so category ZSTD and its flags are unavailable. This does not turn a missing fit flag into evidence that the category functions well. In `category_structure_report()`, `MedianThreshold` now means a cumulative 50% boundary; `AndrichThreshold` retains the adjacent-category step. Recreate saved structure reports. Half-score crossings outside `theta_range` stay missing, and modal peaks describe only the requested grid. The random chi-square likewise cannot establish normality: with equal SEs and positive adjusted variance it is fixed by the number of eligible elements, regardless of shape. Chi-square summaries retain unavailable flags and use the full family before truncation. For external files, fixed-field import requires validated all-field headings and element number after the statistics; other layouts should use named delimited columns. Native score exports contain observations, not the elements in a FACETS Scorefile. Native Tables 5 and 11 omit parts of FACETS' response moments and variance reports. A single co-observation graph component also does not prove identification; inspect the fitted design audit. The historical `conditional_probability` plotting alias is ordinary P(k), not FACETS' adjacent-conditional probability; use `ccc` to name the native quantity clearly. Match denominators as well as cutoffs. [FACETS Totalscore](https://www.winsteps.com/facetman64/totalscore.htm) defaults to `No`, while `fair_average_table()` defaults to `totalscore = TRUE`. Native raw `TotalScore`/`TotalCount` and weighted `WeightdScore`/`WeightdCount` are distinct; `ObservedAverage` uses the weighted values. Scores 0 and 2 with weights 1 and 3 have raw total/count 2/2 but weighted total/count 6/4 and average 1.5. Keeping both counts is useful for describing the data, provided the comparison uses the same [replication semantics](https://www.winsteps.com/facetman64/weighting.htm) and extreme-row inclusion. Weights do not by themselves establish the independence or sampling uncertainty of repeated ratings. ### When a native definition has a useful purpose - **JML extremes:** an all-low/all-high free Person has an unbounded ordinary JML estimate. Keeping this status separate from a finite adjusted display avoids an optimizer-dependent ranking. FACETS also recognizes the boundary and supplies an explicitly adjusted finite value through [Xtreme](https://www.winsteps.com/facetman64/xtremescore.htm). The default `xtreme_method = "display"` retains mfrmr's average-score target and leaves fair scores unchanged. For ordinary additive RSM/PCM JML, `fair_average_table(..., xtreme = .3, xtreme_method = "facets")` instead solves a corrected weighted-total equation using actual administered calibration and the element's minimum positive eligible weight. It updates that element's fair scores and reports conditional information SEs while retaining primary estimates and fixed anchors. `estimate_bias(..., xtreme = .5)` separately extrapolates extreme cells within the specified search limits. Group-mean anchors and fitted interactions are unsupported. Matching origin constraints resolves the checked interior partial-anchor differences; broader extreme-score anchoring and exclusion remain unverified. Neither its reporting SE nor an optimizer's finite stopping value supplies uncertainty for the original infinite MLE or a validated decision probability. - **FairM references:** non-Person FairM values use the mean of finite Person estimates, excluding infinite JML estimates. `FairMReferencePersons` and `FairMExcludedPersons` identify the reference, and no eligible Persons leaves the score unavailable. The reference describes this finite subset; it is not an estimate of the mean ability of an unrestricted population. - **MML and posterior scoring:** integration over a specified population can stabilize sparse Person scores and makes posterior uncertainty available. It also introduces distributional assumptions and shrinkage. A score near a pass/fail cut can move for that reason. It is not automatically preferable to JML for every population, assignment design or individual decision. Posterior cut probabilities condition on the stated calibration and scoring prior; they are not probabilities of passing a future administration. - **Missing categories and unidentified comparisons:** preserving the declared score ladder and refusing an unsupported contrast avoids silently changing the question. FACETS permits different category/identification conventions; see [Table 8.1](https://www.winsteps.com/facetman64/table8_1ratingscale.htm) and [subset connectedness](https://www.winsteps.com/facetman64/subset-connectedness.htm). A supported within-subset comparison can remain meaningful when a comparison across subsets is unidentified. Missing output must identify its affected target rather than imply that every descriptive result is unusable. - **Uncertainty:** posterior SD, conditional information SE, structural covariance and fit-enlarged SE answer different questions. The implemented `RealSE = ModelSE * sqrt(max(Infit, 1))` agrees with the FACETS [enlargement formula](https://www.winsteps.com/facetman64/standarderror.htm), but this does not make their underlying ModelSE equal or make RealSE a general dependence-robust SE. A supplied fixed anchor is not a newly estimated parameter; missing sampling SE does not establish that the external anchor is known without error. ### Decisions near a cutoff Record the estimand, method, scale, reference population, eligibility, statistic and exact comparison rule with a decision. Read both the magnitude of the departure and its uncertainty. A fit flag or a bias-screening tail area alone does not identify an unfair rater or justify deleting ratings. Use unrounded values for an available calculation. A FACETS token such as `2.00` at a ZSTD cutoff cannot reveal whether the unreported value was below or above 2; retain a boundary-indeterminate comparison. Do not choose between YES/NO, df conventions, priors or category recoding after seeing which one crosses a desired cutoff. A change of convention that changes a decision requires a stated rationale and sensitivity report, even if both calculations are internally valid. Missing uncertainty is not evidence that a criterion has been met. For pass/fail work, keep the operational cut rule separate from a model's expected score or fair average. `classification_table()` describes its supplied calibrated rating plan and cut rules; posterior scoring answers a different conditional question. Neither automatically validates the cuts, future rating plan or substantive consequences. ## Ordinary JML, corrected JML and FACETS FACETS' [estimation documentation](https://www.winsteps.com/facetman64/estimationconsiderations.htm) states that it does not correct JMLE estimation bias. Accordingly, ordinary `method = "JML"` under matched RSM/PCM models, anchors and origin constraints is the relevant mfrmr comparison. Equal fitted values establish neither unbiased estimation nor calibrated intervals. More Persons alone need not remove the incidental-parameter bias when each Person still supplies few ratings. These operations have different targets and should be recorded separately: | Operation | What it changes | Relationship to mfrmr | |---|---|---| | FACETS ordinary JMLE | Solves the joint estimation equations. | Compare with ordinary RSM/PCM JML under aligned model and numerical controls. | | FACETS `Xtreme` | Supplies finite extrapolations for extreme totals. | `xtreme_method = "facets"` implements the documented reporting convention within its supported scope; it is not a general estimation-bias correction. | | mfrmr `jml_correction_order` | Changes the profiled structural score equation. | An experimental GPCM method requiring the same facet to own steps and slopes; it does not reproduce FACETS JMLE. | For the corrected route, `fit_mfrm(model = "GPCM", method = "JML", jml_correction_order = ...)` solves the adjusted equation described in `?fit_mfrm`. This is an existing implementation, not an RSM/PCM correction switch. It currently excludes anchors and supplies local-root `RootSE`, not validated structural-truth confidence intervals. Its fixed/random-roster covariance describes repeated independent Person cohorts; the common-plan delta procedure instead holds each Person's ability fixed across repeated responses. Passing that RootSE into the common-plan interval calculation would change both the estimator and the sampling target. Corrected JML is therefore not admitted by `confint.mfrm_fair_average()`. The benefit of retaining both estimators is that external compatibility and a possible reduction in incidental-parameter bias can be studied separately. The cost is a distinct mathematical, numerical and inference contract: correction order, remaining bias, variance and solver stability all require evaluation. FACETS agreement cannot select the correction order or qualify its uncertainty. Keep ordinary JML as the comparison/default route and select corrected JML explicitly within its documented scope. FACETS' separate `Bias` interaction analysis also should not be confused with correction of an estimator's bias. ## Using TAM JML as a third comparison TAM's `tam.jml()` helps check the common RSM/PCM likelihood, but its default corrections differ from ordinary mfrmr JML. Match category support, cumulative difficulties, anchors and location constraints first. The [TAM documentation](https://alexanderrobitzsch.github.io/TAM/reference/tam.jml.html) describes its separate adjustment and bias arguments. | Comparison | What to align or retain | |---|---| | Ordinary interior JML | Use `bias = FALSE` and `adj = 0` for the TAM comparison. This requires finite interior solutions; it is not a recipe for extreme totals. | | Category support | If a Rater-Task column lacks the highest allowed category, inspect returned `A` and `B`. With custom full-category `A`, supply intended category scores in `B`; a generated score matrix can define a different model. | | Classical correction | The checked TAM `bias = TRUE` multiplies free item coefficients by `(I-1)/I`, using response-column count. Its returned Person estimates and SEs are not all recomputed at the corrected coefficients. This is distinct from mfrmr's profile-score adjustment selected by `jml_correction_order`. | | Extreme totals | TAM `adj` modifies calibration scores. mfrmr's explicit `xtreme_method = "facets"` conditions on saved calibration and retains infinite primary estimates. Matched extrapolations do not establish matched calibration procedures. | | Person uncertainty | Compare TAM `theta` with MLEs and retain `WLE` separately. `errorWLE` is not the MLE's SE. Conditional information, joint sampling uncertainty and coverage are separate questions. | | Fit statistics | `tam.jml.fit(trim_val = Inf)` exposes untrimmed residual fit; its default trims large squared residuals. Unit-weight untrimmed fit has a matched fourth-moment transform, without certifying every FACETS reporting field. | | Weights | TAM normalizes Person `pweights`; these are not arbitrary response replication weights. With missingness and unequal weights, its checked item Outfit denominator also differs from normalization by observed weight sum. | | Anchors | Match the origin constraints as well as fixed values. FACETS ignores centering on anchored facets; ordinary mfrmr JML offers `centering_scope = "anchor_origin"` for direct and group anchors. This does not extend the executed TAM group-anchor comparison scope. The checked TAM version routes Person anchors through version 1; an all-item-fixed case with a Person anchor fails. | Executed TAM 4.3-25 comparisons support close ordinary-JML agreement for matched complete/sparse RSM/PCM designs and supported partial Person anchors. They do not establish general coverage, every FACETS anchoring convention, or a new JML import route: `import_tam_fit()` remains MML-only. Retain unavailable uncertainty instead of substituting WLE SEs or conditional information for it. Inspect returned anchors too. In the checked TAM 4.3-25 version-1 path, `theta.fixed` with `xsi.fixed = NULL` still recenters every Person after an update, moving a supplied Person anchor. A free-intercept probe did not retain the anchor and reached its iteration limit. An empty fixed-item matrix prevented that recentering in this version, but the probe still did not reach the required score-equation accuracy within the same limit. This is a version-specific diagnostic observation, not a general workaround recommendation. The matching reference instead uses an independently fitted unanchored TAM solution translated to the specified Person origin, preserving every response probability. Keep that algebraic comparison separate from a successful direct anchored TAM execution. ## A one-shot legacy-compatible call If the goal is to translate a FACETS-style script with minimal R-side plumbing, use `run_mfrm_facets()` (alias `mfrmRFacets()`): ```{r facets-mode} library(mfrmr) data("mfrmr_example_operational", package = "mfrmr") run <- run_mfrm_facets( data = mfrmr_example_operational, person = "Person", facets = c("Rater", "Criterion"), score = "Score", model = "RSM", method = "JML" ) names(run) ``` The wrapper returns the same `fit_mfrm()` and `diagnose_mfrm()` objects that a step-by-step pipeline produces, plus the iteration log, fair-average table, and rating-scale table: ```{r facets-mode-summary} jml_status <- summary(run$fit, profile = "fit", detail = "brief") jml_status$overview[, c( "Model", "Method", "Converged", "InferenceReady", "ConvergenceSeverity" )] jml_status$readiness head(run$fair_average) ``` `method = "JML"` is shown here for a JMLE-oriented migration comparison. Do not infer readiness from `Converged` alone: require `InferenceReady = TRUE` for the numerical-readiness criteria and review the terminal-gradient guidance when severity is `"review"` or `"fail"`. Numerical readiness does not override a Data, Design, or Stability hold; use the readiness table before interpreting or reporting the fit. Choose the estimator for the intended question. The default MML route integrates over a fixed N(0, 1) population and exposes Person posterior SDs; other supported population specifications must be selected explicitly. Its assumptions and shrinkage must be justified for the target population. A posterior SD and a JML sampling SE are different quantities. ## Translating the specification file The mapping below covers the most common FACETS specification keywords. ### FACETS and labels ``` Facets = 3 Models = ?,?,?,R5 Labels = 1, Examinee 1 = P01 ... 2, Rater 1 = R1 ... 3, Criterion 1 = Content ... ``` translates to: ```r fit_mfrm( data = examinee_long, person = "Examinee", facets = c("Rater", "Criterion"), score = "Score", rating_min = 1, rating_max = 5, model = "RSM" ) ``` `Models = ?,?,?,R5` becomes `model = "RSM"` and the `R5` rating-scale declaration becomes `rating_min = 1, rating_max = 5`. For a partial-credit specification, pass `model = "PCM"` and identify the facet that carries the step thresholds with `step_facet = "Rater"` (or the appropriate facet name). The declaration preserves the intended category map, but it is not a threshold anchor. A missing boundary category remains review evidence for the separate element-boundary contract. A missing internal category in a polytomous fitted ladder creates an unsupported adjacent-step contrast and stops fitting before optimization. Review or revise the data support rather than copying FACETS category-dropping behavior implicitly. ### Anchoring A FACETS `Labels=` block with direct anchoring: ``` Labels = 2, Rater, A 1 = R1, 0.0 2 = R2, 0.5 * ``` becomes an `anchors` data frame: ```r anchors <- data.frame( Facet = "Rater", Level = c("R1", "R2"), Anchor = c(0.0, 0.5), stringsAsFactors = FALSE ) fit <- fit_mfrm(..., method = "JML", anchors = anchors, centering_scope = "anchor_origin", anchor_policy = "error") ``` Use labels that match the input data. `Anchor` is a recognized value column; `estimate` is not. A malformed nonempty anchor schema stops fitting. For migration, also use `anchor_policy = "error"` for review issues and inspect the normalized anchors before interpreting results. These values must be on mfrmr's logit scale. Convert user-scaled FACETS measures back using the original origin and multiplier; copying a scaled number into a logit anchor changes the model. FACETS [disables centering on anchored facets](https://www.winsteps.com/facetman64/centerfacet.htm). The explicit `"anchor_origin"` option follows this rule for direct and group anchors in ordinary additive RSM/PCM JML. One unanchored, active facet must be declared `noncenter_facet`; other unanchored facets remain centered. Thus, a Person anchor requires a different noncenter facet, such as Rater. Merely adding a Person anchor while centering every other facet can produce FACETS overconstraint warnings and nonzero score residuals. Correcting that specification resolved the checked sparse interior RSM/PCM discrepancies. Multiple fixed values may impose substantive restrictions beyond the origin. Other estimators and fitted interactions are outside this option. An origin anchor does not guarantee a finite maximum. Without Person group constraints, if every retained response of the Person anchors is at the same declared minimum or maximum, free Person and noncenter-facet locations can diverge together while respecting the fixed values. This case stops before optimization, without requiring `lpSolve`. An extreme anchor with additional interior or opposite-boundary anchor support does not by itself meet this rule; other boundary diagnostics still apply. In matched sparse RSM/PCM examples, FACETS 4.5.0 retained all observations and the extreme Person's fixed value. Its reported calibration implied an expected anchor total of about 0.299 for an observed total of 0, or 17.701 for 18. These values are consistent with the specified `Xtreme=0.3`, but the ordinary likelihood can still increase along a direction that preserves the anchors. The exact internal adjustment is not established by this comparison. mfrmr therefore does not reproduce these finite outputs as ordinary JML estimates. This is a difference in estimation behavior, not an import or rounding error. Other detected joint additive recession directions affecting calibration also stop this route; passing these checks is not a general finite-MLE certificate. Adding an informative interior Person anchor can restore a finite ordinary solution, but does not by itself establish native calibration agreement. In matched RSM/PCM examples with this support, FACETS retained the fixed extreme Person's responses. Fair scores and conditional Rater-by-Task bias calculations agreed with independent calculations using the native reported calibration. The main estimates still differed from ordinary JML: free-Person score residuals remained, and the maximum-score examples reached the main iteration limit. These results separate conditional reporting agreement from calibration agreement; they do not identify every internal cause of the native behavior. For ordinary additive RSM/PCM JML, `fair_average_table(..., totalscore = FALSE)` now retains finite direct anchors under both `xtreme_method` conventions. An anchored element's all-minimum/all-maximum responses do not themselves make it an unanchored infinite estimate. Other unanchored extreme elements still trigger exclusion. The PCM fair score and its conditional plot derivative use the same retained scale mixture. Recreate affected older tables and plots from the saved fit and matching diagnostics; no calibration refit is required. `estimate_bias()` also retains directly fixed finite anchors in ordinary additive RSM/PCM JML, including with the default `xtreme = 0`. Person-by-Rater cells should not disappear merely because that fixed Person's observed scores are all minimum or maximum. Without extrapolation, a boundary cell keeps its bounded point and unavailable screening SE/tail area. Recreate affected bias tables and collections from the saved fit and matching diagnostics. The [FACETS Xtreme specification](https://www.winsteps.com/facetman64/xtremescore.htm) has separate main-effect and bias corrections. To match its usual bias correction use `estimate_bias(..., xtreme = .5)`; `.3` is the usual main-effect correction. Executed Person-by-Rater checks agree with the conditional calculation on matched main calibrations. The correction can even change the sign relative to the observed departure: an all-zero cell with expected total below 0.5 needs a positive shift to attain the amended total 0.5. Read `PrimaryBias`, `ExtremeAdjustment` and `Obs-Exp Average` together. This remains an extrapolated screen for expert review, not a fairness finding. For an executable example, each row of the bundled ratings is a Person's score from one Rater on one Criterion. Treat the R01 anchor below as a supplied external value for illustration, not an independently validated calibration: ```{r anchor-origin} ratings <- load_mfrmr_data("example_core") rater_anchor <- data.frame(Facet = "Rater", Level = "R01", Anchor = 0.7) anchored_fit <- fit_mfrm( ratings, person = "Person", facets = c("Rater", "Criterion"), score = "Score", method = "JML", anchors = rater_anchor, centering_scope = "anchor_origin" ) anchored_fit$config$anchor_review$facet_summary ``` The table's `Centered` and `OriginBasis` columns show which origin constraints were applied. This leaves Person noncentered, fixes R01, frees the other Rater locations from a zero-sum requirement, and centers Criterion. Existing `"facet"` (default) and `"remainder"` conventions remain available and impose different constraints with partial anchors. Refitting is required to change conventions. All options condition on supplied anchors; they do not propagate uncertainty in previously estimated anchor values. For a group origin, translate the native group total before fitting. In FACETS `Labels= ...,G`, contributions 0.2 and 1.0 for two members fix their sum at 1.2. mfrmr uses the mean 0.6, repeated for both rows: ```{r group-anchor-origin} ratings <- load_mfrmr_data("example_core") group <- data.frame( Facet = "Rater", Level = c("R01", "R02"), Group = "panel", GroupValue = 0.6 ) group_fit <- fit_mfrm( ratings, person = "Person", facets = c("Rater", "Criterion"), score = "Score", method = "JML", group_anchors = group, centering_scope = "anchor_origin", anchor_policy = "error" ) group_fit$config$anchor_review$facet_summary subset(group_fit$facets$others, Facet == "Rater") ``` The first table identifies the Rater origin as `group_anchors`. In the second, R01 and R02 average 0.6 but their individual measures are estimated; the other Raters need not sum to zero. The supplied group mean is a substantive assumption, and its uncertainty is not propagated. Unequal `GroupValue` rows are conflicting mean targets, not contributions to be averaged by the normalizer. FACETS [group codes](https://www.winsteps.com/facetman64/elements.htm) also need translation: group 0 specifies direct anchors, so use `anchors`, not `Group = "0"`. A missing native contribution does not participate in the group anchor. The native `X` option excludes extreme elements; mfrmr retains its declared members and does not reproduce that option automatically. Executed interior RSM/PCM examples agree closely when the group only selects an origin. Adding a second, restrictive group exposed a smaller but real difference: FACETS 4.5.0 preserved both targets, yet a constraint-preserving change increased the ordinary likelihood. mfrmr therefore retains constrained maximum likelihood rather than emulating that numerical result. Matching group means does not establish identical estimates, SEs or interval coverage. General group-anchor extreme extrapolation remains unsupported. `review_mfrm_anchors()` normalizes and reports on the constraint block before the fit runs, surfacing duplicate/direct-group overlap and local support-count issues. It does not evaluate assignment connectedness, source-fit readiness, or cross-run element invariance; use the design-network route and substantive identity evidence separately. ### Bias and interaction For FACETS Table 13 cell-bias output between Rater and Criterion, the closest mfrmr screening route is: ```r diag <- diagnose_mfrm(fit) bias <- estimate_bias(fit, diag, facet_a = "Rater", facet_b = "Criterion") summary(bias) ``` `estimate_all_bias()` enumerates every non-person facet pair in one call. ```r screens <- estimate_all_bias(fit, diagnostics = diag) summary(screens) # Pair counts, availability and magnitude head(summary(screens)$cells) # Signed effects, SEs and observed counts plot(screens) # All retained cells grouped by pair plot(screens, pair = "Rater x Criterion") # Individual heatmap ``` These are exploratory conditional screens: baseline calibration and Person uncertainty are held fixed. `ScreenPositive` counts available statistics with absolute value at least two; it is `NA` when none is available. Review errors, unavailable cells and observed counts before interpreting zero flags. The batch does not supply collection-wide multiplicity control, and a flagged cell alone does not establish unfairness. ### Wright map / variable map For a shared-logit visual display of persons, facet levels, and step thresholds, first create the FACETS-organized summary and retain its result object: ```r review <- summary(fit, profile = "facets", detail = "brief") review$decision res <- review$results # Primary final-scale figure: all locations and available facet uncertainty. plot(res, type = "wright", renderer = "native", show_ci = TRUE, top_n = Inf, preset = "publication") ``` Read `review$decision` before using the FACETS-organized tables. It reports the same stored mfrmr readiness decision shown by `print(fit)`; the `facets` profile changes the organization of the review, not the estimator and not the evidence threshold. `FormalInference = "No"` withholds the corresponding formal inferential claim even when a familiar table or plot is available. It does not turn every descriptive result into an unusable result: report a valid descriptive target with its limitation, and identify which comparisons or uncertainty statements are unavailable. `plot()` is the corresponding explicit helper when the Wright map is the main figure. For readers who expect the FACETS Table 6-style asterisk ruler and horizontal, rubric-labelled category transitions, define one label for every retained original score: ```r rubric_labels <- setNames( your_rubric_labels, fit$prep$score_map$OriginalScore ) plot(res, type = "wright", renderer = "facets", show_ci = FALSE, category_labels = rubric_labels, preset = "publication") ``` `show_ci = FALSE` is the closest FACETS-style visual grammar. Setting `show_ci = TRUE` deliberately creates a hybrid display: the ruler is FACETS-style, but the intervals are mfrmr uncertainty estimates. Neither renderer implies that FACETS performed the estimation or that the two programs are numerically equivalent. For the Bond-and-Fox-style follow-up requested by many FACETS users, put Infit on the horizontal axis and the measure on the vertical axis. Person rows remain opt-in: ```r plot(res, type = "fit_pathway", fit_stat = "Infit", include_person = TRUE, top_n_person = 12, person_labels = "none", facet_labels = "flagged") ``` Use `draw = FALSE` or `plot_data(fit, type = "wright")` when you need the underlying coordinates for a custom `ggplot2`, base-R, or Quarto graphic. ### Fit df and ZSTD review FACETS users often compare Infit/Outfit MnSq together with ZStd columns. In `mfrmr`, treat MnSq as the primary fit statistic and use the df/ZSTD columns to explain how the same MnSq values were standardized. The direct review path is: ```r diag <- diagnose_mfrm(fit, residual_pca = "none", fit_df_method = "both") fm <- fit_measures_table(fit, diagnostics = diag, facet = "Rater", fit_df_method = "both") fm$facets_table fm$df_sensitive plot(fm, type = "df_sensitivity") ``` `df_sensitivity` reports the engine-vs-FACETS-style df comparison row by row; `df_sensitive` keeps only rows where the df convention changes the |ZSTD| flag or materially changes the ZSTD interpretation. The same status taxonomy is used by `facets_fit_review()`, so a table-oriented review and an external FACETS comparison use the same language. ### Group anchoring and DFF FACETS `G` group anchors constrain sums over specified elements. mfrmr's `GroupValue` is the mean target for the group. For example, the following schematic input constrains the mean of two Rater elements to zero; it does not define response groups for a DFF analysis: ```r group_anchors <- data.frame( Facet = "Rater", Level = c("R1", "R2"), Group = c("PanelA", "PanelA"), GroupValue = c(0.0, 0.0), stringsAsFactors = FALSE ) fit_g <- fit_mfrm(..., group_anchors = group_anchors, anchor_policy = "error") diag_g <- diagnose_mfrm(fit_g) dff <- analyze_dff(fit_g, diag_g, facet = "Criterion", group = "FirstLanguage", data = ratings_with_language, method = "refit") ``` Replace `...` with the fitting arguments for your data and supply `ratings_with_language` containing the response-group column `FirstLanguage`. Use diagnostics from `fit_g`, not from a different fit. Assigning the same element to two different groups is not this mean-constraint construction. A group-anchor target is an external mean constraint, not an empirical link. Its use requires a defensible equal-mean or known-target assumption for the declared elements and does not connect otherwise unobserved rating subsets. ## Reviewing output contracts and fit tables When migrating an existing study, `facets_output_contract_review()` checks whether the package-generated report components satisfy the FACETS-style output contract encoded in the package: ```r contract_review <- facets_output_contract_review( fit, diagnostics = diag, branch = "facets" ) summary(contract_review) contract_review$missing_preview contract_review$metric_checks ``` The resulting object reviews column coverage and package-native metric checks. It is not a claim that `mfrmr` has reproduced FACETS estimates numerically. For external numerical comparison, use an exported FACETS fit table and `facets_fit_review()`. When that comparison involves an `MML` fit, remember that mfrmr evaluates residual-based fit statistics at shrunken EAP person measures while FACETS uses JMLE estimates, so MnSq differences can reflect the residual basis rather than a fit-computation difference; refit with `method = "JML"` before attributing such gaps. See `facets_fit_df_guide()` for this boundary and for the separate df/ZSTD standardization conventions. If you already have a FACETS fit table on disk, read it first and then run the fit review. This does not run FACETS; it consumes an exported or otherwise harmonized table. ```r facets_fit <- read_facets_fit_table( "score.2.txt", facet_map = c("1" = "Person", "2" = "Rater", "3" = "Criterion") ) review <- facets_fit_review( fit, diagnostics = diag, facets_fit = facets_fit, external_zstd_tolerance = 0.05 ) review$df_sensitivity review$df_sensitive review$external_table_quality review$external_comparison plot(review, type = "df_sensitivity") ``` Use `external_comparison` for the supplied FACETS table and `df_sensitivity` for the engine-vs-FACETS-style df convention check. This separation keeps external numerical differences distinct from ZSTD differences caused by df standardization. `external_table_quality` is the first place to look if the FACETS export only contains ZStd and T.Count columns, or if duplicate `Facet` x `Level` rows were supplied. ## Producing FACETS-style output files For traceability or downstream tools that expect FACETS output files, `facets_output_file_bundle()` writes a parallel set of fixed-width or CSV exports: ```r files <- facets_output_file_bundle( fit, diagnostics = diag, out_dir = tempdir(), include = c("graph", "score") ) ``` For RSM and PCM the score-side helpers are available. Under `GPCM` the score-side bundle is intentionally restricted; see `?gpcm_capability_matrix` and the `mfrmr-gpcm-scope` vignette for the documented limitation. ## Recommended next steps For a Person comparison on the **same** scale exposure, supply `reference_plan` to `fair_average_table()`. Its `reference_scores` component uses each Person's primary estimate with the actual fitted locations and scale profiles in that one weighted plan. This is a distinct expected-score target: PCM FairM/FairZ still retain each Person's administered scale mixture. Score direction remains defined by the rubric; a higher standardized expected score is not automatically a judgment of better performance or fairness. `confint(fair, parm = ..., nsim = ..., seed = ...)` provides an experimental ordinary-JML refit route for common-plan scores and named paired contrasts. When anchors were estimated from the same or overlapping samples, supply their `anchor_source`, a generating fit on the union of response events where needed, and explicit calibration/scoring row maps. The maps assert which observations are actually shared: equal labels alone do not establish event identity. Every replication re-estimates calibration and anchors before scoring; shared responses are generated once. Without the source and correspondence, estimated- anchor intervals are unavailable. `anchor_uncertainty = "fixed"` is an explicit conditional alternative, not a substitute for propagating estimated anchors. Explicit `generator_method = "jeffreys"` offers an experimental refit option when extreme Person estimates prevent an ordinary generating model. It fits a canonical information-penalized model on the generating data, then simulates shared events once and reruns the ordinary calibration/anchor/scoring estimator. The original ordinary-JML points remain unchanged; extreme Persons retain limiting expected scores. Inspect `attr(result, "generating_model")` and `attr(result, "generating_target")` to distinguish the generator from the reported estimate. Declare the rubric and retain its full coding when fitting. This option is not available for delta or bounded intervals, and it does not resolve infinite estimated anchors or unqualified structural calibration. A finite generator alone does not justify nominal coverage or score-bias reduction. The bias correction for canonical parameters does not automatically transfer to expected scores: even the binomial probability `(S + .5)/(n + 1)` has bias `(.5 - p)/(n + 1)`. The option is separate from FACETS Xtreme and mfrmr's corrected-GPCM estimator; it leaves `CIEligible = FALSE`. An alternative is to select `jml_penalty = "jeffreys"` with `method = "JML"` in **every** original calibration, scoring and union fit. This changes the reported points as well as every replicate fit. The separate `mfrm_jeffreys_fit` result supplies Person, facet and step points and numerical checks; it does not inherit ordinary SEs or FACETS diagnostics. For its common-plan scores, call `fair_average_table(fit, reference_plan = plan)`. The existing `confint()` interface propagates estimated anchors and shared events using the same penalized estimator throughout. Its generating method defaults to Jeffreys, using the supplied penalized union fit. Mixing ordinary and penalized fits across stages is refused. With `method = "delta"`, the analytic derivative follows the penalized equations, including the change in the penalty when estimated anchors change. Shared response events contribute to both stages before computing covariance. This is a local model-based approximation, not inverse penalized curvature or a coverage guarantee. Inspect `summary(fit)$checks` and `attr(result, "settings")$estimator` to record which procedure was used. Declared empty categories are retained; unidentified designs and numerical failures are still refused. Finite-sample score bias, coverage and global optimization remain unqualified. This option is separate from FACETS Xtreme and corrected GPCM, and is not a FACETS-compatible default. There is a concrete reason to retain the experimental label. With six binary responses and true success probability 0.5, exact enumeration gives 78.125% coverage for nominal 95% Jeffreys delta intervals. At probability 0.25, it is 78.4424%. The local variance formula is correct; the plug-in normal interval still fails to achieve its nominal coverage. More Persons do not necessarily provide more responses for the particular Person being scored. Use `Available` to check whether a result was computed, and `CIEligible` and the saved notes to assess its inferential status. Basic refit intervals also have counterexamples: with six responses and true probability 0.1, exact refit quantiles give coverage 45.2709%. An all-zero sample's upper basic limit equals the fitted score 0.5/7, below the true probability. Even infinitely many refits do not fix this; Monte Carlo precision and sampling coverage are different requirements. The conservative bounded method retains its distinct assumptions and can be wide in sparse designs. Read the interval table's `Status`, `Available`, `Unresolved` and `ExpectedTailDraws` together. Numerical availability does not qualify coverage: the initial method is a pointwise fitted-model approximation, with no correction for JML incidental-parameter bias or guarantee under misspecification. Unresolved trials stay in the denominator and can make tail limits unavailable. The four `LowerMCLower`/`LowerMCUpper` and `UpperMCLower`/`UpperMCUpper` columns separately enclose the Monte Carlo error of the two endpoints, at `mc_level` (default .95). `attr(result, "monte_carlo")` retains the underlying binomial ranks. Infinite enclosures expose insufficient draws or unresolved refits; `MCFinite = TRUE` means finite, not necessarily precise enough. These diagnostics cover both endpoints for one fixed target under a fixed generating model and independent replications. They do not guarantee coverage across targets, adaptive reruns, or repeated samples of the original data. The plot still shows the empirical interval; its saved payload retains the Monte Carlo diagnostic for inspection. `summary()` and `plot()` reuse the saved result, and `saveRDS()` preserves its replications, checks and provenance. These intervals concern expected scores, not future observed ratings; they do not certify unfairness. The method's help contains an executable example and the supported estimation/design restrictions. `confint(fair, parm = ..., method = "delta")` evaluates a deterministic local approximation through the same estimator. Supply the same calibration fit, union fit and event maps when anchors are estimated. Omit `nsim` and `seed`. The result adds `SE`, `Reason` and `attr(result, "covariance")`; its `covariance_components` attribute separates direct scoring, propagation through calibration and the signed shared-event cross term. These terms retain the covariance needed for paired differences; a marginal anchor SE cannot replace them. The count columns for simulations are `NA` because this method does not run replications. Delta limits use a local normal approximation centered on the observed estimate. They require finite estimates and positive information, and do not correct JML bias or establish coverage. Missing bounds retain their reasons; bounds outside the score support are not clipped. Compare the numerical approximation and availability under the intended design before using either method for decisions. The help for `confint.mfrm_fair_average` gives the derivative, uncertainty basis and references. These procedures require native fitted models; descriptive FACETS-result imports do not provide the source model or joint uncertainty. `confint(fair, parm = ..., method = "bounded")` is a conservative alternative for the same true common-plan score or contrast. It uses scoring responses directly, so extreme Person estimates and the absence of an estimated-anchor covariance do not require a fitted generating model. Omit calibration/generator arguments and simulation controls for this method. Its finite-sample probability bound requires independent bounded responses, a fixed exogenous assignment, plan and contrast, and stable expected scores within each Person-plan cell. It is pointwise and does not justify selecting comparisons after seeing scores or claiming nominal coverage conditional on successful fitting. Declare both `rating_min` and `rating_max` from the rubric when fitting, and retain the full score coding with `category_policy = "preserve"`. Observed sample extrema and internally collapsed categories are not accepted as known support. The interval result saves this basis in `attr(result, "settings")$score_support`. A retained empty category that prevents fitting is still a failure; changing the rubric to obtain an interval changes the inferential target. The tradeoff is width: every unobserved plan cell keeps its full score range instead of borrowing a model prediction. Inspect `Informative` and `attr(result, "support")`; an available full-range bound is explicitly uninformative. `Estimate` retains the JML point, while `DesignEstimate` is a separate unbiased weighted mean only with complete plan support. The interval can be asymmetric around or exclude the JML point. It is not a bias correction, a FACETS interval convention, or a solution to dependent ratings. Failure to construct the prerequisite fitted result remains an end-to-end availability failure, even though the concentration inequality itself needs no fitted model. After a FACETS-oriented package-native fit is in hand, the recommended mfrmr reporting workflow extends the analysis with: - `review_mfrm_anchors()` before anchored fitting, and `detect_anchor_drift()` / `plot()` when common elements define a cross-form or cross-wave link. - `diagnose_mfrm(diagnostic_mode = "both")` for the strict marginal screen alongside the residual stack. - `rating_scale_table()`, `category_structure_report()`, and `category_curves_report()` for category-functioning evidence. - `fair_average_table()` when FACETS Table 7 fair-average review is needed. - `plot(fit, type = "wright")` or `plot()` for a variable-map view of targeting and threshold placement. - `estimate_bias()`, `bias_interaction_report()`, and `bias_pairwise_report()` when FACETS Table 14-style local interaction screening is substantively relevant. - `reporting_checklist()` for a manuscript-readiness summary. - `build_apa_outputs()` for Method and Results paragraphs and APA tables. - `build_mfrm_manifest()` and `build_mfrm_replay_script()` for the reproducibility bundle alongside the FACETS-style handoff out of the box. The `mfrmr-workflow` vignette covers the full sequence end to end; the `mfrmr-reporting-and-apa` vignette focuses on manuscript preparation; the `mfrmr-linking-and-dff` vignette covers anchoring, drift, and DFF in detail.