Current stable release: 0.9.0.
nomologR is a guided, evidence-based workflow for empirical scale
development and construct validation. It coordinates established R
engines while adding transparent diagnostics, literature-linked
explanations, decision logging, and theory-aware guidance.
Core rule: Flag, explain, and document. Never silently delete.
Documentation · Roadmap · v1.0.0 milestone · Open issues
Install the stable release from the JUhalt R-universe:
install.packages(
"nomologR",
repos = c(
"https://juhalt.r-universe.dev",
"https://cloud.r-project.org"
)
)To install the current GitHub version:
# install.packages("pak")
pak::pak("JUhalt/nomologR")nomologR is distributed through R-universe, which builds each GitHub
release, usually within a few hours. v0.3.0 is the first release
submitted to CRAN
(#39); until CRAN
accepts it, install from R-universe or GitHub.
nomologR is written for graduate students (master’s and doctoral)
learning scale development and construct validation, for the faculty
and advisors who teach them, and for researchers who need a
defensible, reproducible measurement workflow. Methods are documented
from historical to contemporary practice, with literature references in
every analysis function’s help page.
- Get started — the workflow at a glance and a suggested learning path.
- Research
basis
— where each method came from, what current literature recommends, and
what
nomologRimplements. - Walkthroughs that run on simulated teaching data with known answers: exploratory structure, measurement-model evidence, total and subscale scores, measurement invariance, nomological networks, guided workflow, from content review to empirical screening, and reproducible reports.
The teaching datasets are:
nomo_demo_continuous: two correlated factors, with a cross-loading item, a weak item, and missing data;nomo_demo_ordinal: the same structure as five-category ordered items;nomo_demo_network: three constructs, an observed outcome, and two groups with a known source of non-invariance;nomo_demo_walkthrough: twelve items reviewed by an expert panel incontentvalidR, built so that content review and the empirical screen disagree.
library(nomologR)
scr <- nomo_screen(nomo_demo_continuous)
fac <- nomo_factors(nomo_demo_continuous, seed = 2026)
efa <- nomo_efa(nomo_demo_continuous, factors = fac)
efa$item_summaryFor new measures,
contentvalidR is the
natural upstream companion: it addresses conceptual/content
representation and substantive validity. nomologR begins when
item-level empirical data are available and follows the measure through
dimensionality, measurement modeling, reliability, construct-validity
evidence, invariance, and theory-specified nomological networks.
The two meet at a handoff. contentvalidR’s content_handoff() records
which items content review carried forward, why each held-back item was
held back, and under which rule. nomo_screen(data, items = handoff)
and nomo_run(data, scales = handoff) take it directly, so the reasons
travel with the items:
run <- nomo_run(responses, scales = handoff)Only carried items are analyzed. Held-back items are reported in
contentvalidR’s own words, and a report from the run opens with the
content review. Neither package depends on the other.
The two packages share a walkthrough on the same simulated item set. The
content-review half is contentvalidR’s One item set, both
stages.
The empirical half is this package’s From content review to empirical
screening,
which shows items that pass one stage and fail the other.
The Milestone 1 module audits candidate items without modifying the supplied data. Screening includes item/case missingness, response distributions, category use, zero/near-zero variance, concentration, corrected item-rest and inter-item relationships, reverse-key/coding review signals, optional continuous-like shape summaries, integrated review tables, and five diagnostic plot views.
items <- data.frame(
item1 = c(1, 2, 3, 4, 5, 5),
item2 = c(1, 2, 3, 4, 4, 5),
item3 = c(5, 4, 3, 2, 1, 1),
item4 = c(1, 2, NA, 4, 5, 5)
)
scr <- nomo_screen(items)
summary(scr)
plot(scr)The package flags reasons to inspect an item; it does not automatically reverse-score, delete, collapse, or recode it.
Milestone 2 asks a different question:
How many latent dimensions deserve investigation?
The default core workflow triangulates four pieces of retention
evidence:
- common-factor parallel analysis (primary);
- Velicer original MAP / TR2;
- Velicer revised MAP / TR4;
- empirical Kaiser criterion (EKC).
Scree information, KMO/MSA, and Bartlett’s test are kept as supporting evidence rather than factor-count decision rules.
set.seed(42)
f <- rnorm(300)
dat <- data.frame(
i1 = 0.8 * f + rnorm(300, sd = 0.6),
i2 = 0.8 * f + rnorm(300, sd = 0.6),
i3 = 0.7 * f + rnorm(300, sd = 0.7),
i4 = 0.7 * f + rnorm(300, sd = 0.7),
i5 = 0.8 * f + rnorm(300, sd = 0.6)
)
fac <- nomo_factors(dat, seed = 2026)
fac
summary(fac)Different jobs need different amounts of computation and triangulation:
nomo_factors(dat, criterion_set = "minimal")
nomo_factors(dat, criterion_set = "core") # default
nomo_factors(dat, criterion_set = "extended")
nomo_factors(dat, criterion_set = "all")minimal: parallel analysis + original MAP (TR2)core: adds revised MAP (TR4) + EKCextended: adds NEST + Hull (CAF) when their assumptions are supportedall: adds comparison data + legacy Kaiser-Guttman (> 1)
The legacy Kaiser-Guttman result is displayed for historical context but
is excluded from the synthesis. If a requested method is
incompatible with the current indicator/correlation/missing-data setup,
it is marked skipped with a reason rather than being silently replaced
by another analysis.
Parallel analysis itself contains analytical choices. nomologR
computes three rules from the same null simulations:
fac$parallel$sensitivity
nomo_factors(dat, parallel_rule = "percentile") # default
nomo_factors(dat, parallel_rule = "mean")
nomo_factors(dat, parallel_rule = "crawford")The selected rule drives the primary PA suggestion; the other rules remain visible as sensitivity evidence.
plot(fac) # observed vs selected PA null reference
plot(fac, type = "parallel_rules") # PA decision-rule sensitivity
plot(fac, type = "scree") # component + common-factor scree
plot(fac, type = "map") # original TR2 + revised TR4 MAP curves
plot(fac, type = "evidence") # criterion-by-criterion suggestions
plot(fac, type = "concordance") # where recommended evidence clusters
plot(fac, type = "kmo") # item-level MSAThe concordance view first groups closely related variants into criterion families (for example, original and revised MAP belong to one MAP family). This avoids making two variants of the same criterion look like two independent votes. Internally split families remain visible rather than being forced into a single count. A synthesis may say:
“Parallel analysis suggests 2 factors, and 4 of 5 available criterion families point to that same count. At the criterion-family level, MAP points to 1. Compare the plausible neighboring solutions in EFA.”
It should never say:
“The scale has exactly 2 factors.”
Under correlation = "auto", continuous, binary, ordinal, and genuinely
mixed item sets are routed to Pearson, tetrachoric, polychoric, or mixed
correlations as appropriate. The selected method and modeling
assumptions are also exposed through the convenience fields
fac$correlation and fac$modeling_types.
When EKC is used with a non-Pearson correlation matrix, nomologR keeps
the criterion available but surfaces an explicit qualification that its
reference series is approximate under that correlation model.
Numeric-discrete storage is deliberately not treated as proof of ordinal measurement. For numeric Likert items, make the modeling choice explicitly:
fac_ord <- nomo_factors(
dat_likert,
types = c(
i1 = "ordinal",
i2 = "ordinal",
i3 = "ordinal",
i4 = "ordinal",
i5 = "ordinal"
)
)types is an explicit researcher decision, not a request for nomologR
to guess. A valid override is applied before default-type rejection,
which means an otherwise ambiguous storage format can be used when the
researcher has encoded it intentionally. The override is recorded in the
decision log.
For example, an ordinary R factor is nominal by default. If its factor levels already encode the intended response order, the researcher can declare those items ordinal:
response_levels <- c(
"Strongly disagree",
"Disagree",
"Agree",
"Strongly agree"
)
dat_factor$q1 <- factor(dat_factor$q1, levels = response_levels)
dat_factor$q2 <- factor(dat_factor$q2, levels = response_levels)
fac_factor <- nomo_factors(
dat_factor,
items = c("q1", "q2", "q3", "q4"),
types = c(
q1 = "ordinal", q2 = "ordinal",
q3 = "ordinal", q4 = "ordinal"
)
)
fac_factor$modeling_types
fac_factor$decision_logThe control is deliberately bounded by storage-safety checks:
types = "ordinal"does not reorder categories. For factor-coded items, the existing factor-level order is used. Set that order intentionally first.- Character/text columns are not silently converted to ordered scores. Recode them deliberately to numeric/factor/ordered storage before modeling.
types = "continuous"requires numeric storage.types = "binary"requires exactly two observed response values.- Constant or all-missing items fail first with a direct data-quality error; an override cannot manufacture variance that is not present.
This is the intended balance in nomologR: researchers retain control
over substantive modeling choices, while consequential assumptions
remain visible, documented, and protected from silent coercion.
Milestone 3 turns a researcher-controlled factor count into a transparent common-factor exploratory model. The default uses MINRES with oblimin rotation, keeps factor correlations visible, and reports evidence that may deserve review without silently deleting indicators or refitting a different model.
The cleanest handoff is directly from nomo_factors():
set.seed(2026)
f1 <- rnorm(500)
f2 <- 0.35 * f1 + sqrt(1 - 0.35^2) * rnorm(500)
dat2 <- data.frame(
A1 = .82 * f1 + rnorm(500, sd = .55),
A2 = .78 * f1 + rnorm(500, sd = .60),
A3 = .75 * f1 + rnorm(500, sd = .62),
A4 = .80 * f1 + rnorm(500, sd = .58),
B1 = .82 * f2 + rnorm(500, sd = .55),
B2 = .78 * f2 + rnorm(500, sd = .60),
B3 = .75 * f2 + rnorm(500, sd = .62),
B4 = .80 * f2 + rnorm(500, sd = .58)
)
scr <- nomo_screen(dat2)
fac <- nomo_factors(dat2, criterion_set = "core", seed = 2026)
efa <- nomo_efa(dat2, factors = fac)
summary(efa)
efa$item_summaryPassing a nomo_factors object carries forward its item set,
modeling-type decisions, correlation model, missing-data strategy, and
explicit smoothing choice where applicable. Those decisions are recorded
as inherited, not misrepresented as new EFA-stage researcher
overrides.
The public result keeps the exploratory evidence reproducible and inspectable:
- neutral factor labels (
F1,F2, …), while the underlying engine object remains available inefa$fit; - pattern and structure matrices;
- communalities, uniquenesses, and loading complexity;
- primary/secondary loading diagnostics and loading gaps;
- factor correlations;
- reproduced and residual correlation matrices;
- ranked localized residual pairs and off-diagonal RMSR;
- KMO/Bartlett supporting adequacy evidence where available;
- a structured decision log.
Item-level numerical references are intentionally framed as review prompts:
- primary loading around
.40; - secondary/cross-loading around
.30; - communality around
.40.
Each item receives KEEP, REVIEW, or STRONG REVIEW. KEEP means no
configured numeric EFA flag fired; it is not a declaration that
theory, content coverage, wording, redundancy, or later validity
evidence has approved the item.
plot(efa, type = "pattern")
plot(efa, type = "items")
plot(efa, type = "residuals")
plot(efa, type = "factor_correlations")The pattern heatmap preserves loading sign; the loading plot distinguishes primary from secondary loadings and displays both teaching references; residual and factor-correlation plots show unique matrix information rather than duplicating symmetric cells.
A factor count may also be supplied directly:
efa2 <- nomo_efa(dat2, factors = 2)Alternative common-factor extraction and rotation choices remain
explicit. An orthogonal rotation is allowed but logged as a choice
requiring substantive justification. A non-positive-definite correlation
matrix stops by default; smooth = TRUE makes any smoothing
intervention explicit and records it.
For numeric Likert indicators, modeling level should be declared rather than inferred from integer storage alone:
efa_ord <- nomo_efa(
dat_likert,
factors = 2,
types = c(
q1 = "ordinal", q2 = "ordinal",
q3 = "ordinal", q4 = "ordinal"
)
)The full Checkpoint A walkthrough is in the “From item audit to exploratory structure” vignette.
Milestone 4 adds a guided CFA layer around lavaan::cfa(). The
underlying lavaan fit is retained in cfa$fit; nomologR adds
diagnostics, literature-linked teaching references, plots, and decision
logging without silently changing the researcher-specified model.
model <- nomo_model(list(
F1 = c("A1", "A2", "A3", "A4"),
F2 = c("B1", "B2", "B3", "B4")
))
cfa <- nomo_cfa(
model,
data = dat2
)
cfa
summary(cfa)The CFA layer reports convergence and captured engine warnings, cases used, standardized loadings with uncertainty, factor correlations, chi-square, CFI, TLI, RMSEA with confidence interval, SRMR, localized residual correlations, and Heywood/improper-solution diagnostics.
Modification indices are available as post-hoc diagnostics only:
head(cfa$top_modification_indices)They never free parameters or trigger automatic respecification.
plot(cfa, type = "loadings")
plot(cfa, type = "fit")
plot(cfa, type = "residuals")
plot(cfa, type = "modification_indices")Fit-index values are teaching references rather than pass/fail laws. The package deliberately asks users to interpret global fit, localized strain, parameter estimates, estimator, sample characteristics, and theory together.
For continuous indicators, leaving estimator = NULL preserves lavaan’s
ordinary continuous-data default. Robust ML estimators such as "MLR"
remain explicit researcher choices.
Declared ordered indicators request WLSMV by default:
cfa_ord <- nomo_cfa(
model,
data = dat_ord,
ordered = names(dat_ord)
)Incompatible ordered-indicator ML/FIML combinations stop with an explanation rather than being silently substituted.
nomo_missing() refits the same model under listwise deletion and FIML,
or under listwise and pairwise deletion for ordered indicators. It
reports where the estimates, reliability, or theory evidence differ:
nomo_missing(cfa, data = dat2)Listwise and pairwise deletion require data missing completely at random, and FIML requires data missing at random (Enders & Bandalos, 2001). A difference larger than half the reference standard error is flagged, following Schafer and Graham’s (2002) rule for when bias becomes practically important. The flag comes with the caveat that the difference estimates bias only if the data are missing at random. That assumption cannot in general be tested from the data at hand, so agreement is reported as insensitivity to the choice, never as proof that either strategy is unbiased. See “From CFA to a defensible measurement model”.
nomo_split() supports a reproducible exploratory/confirmatory split
when the gain in independence justifies the loss of precision:
s <- nomo_split(
dat2,
validation_prop = .50,
seed = 2026
)
fac_cal <- nomo_factors(s$calibration, seed = 2026)
efa_cal <- nomo_efa(s$calibration, factors = fac_cal)
cfa_val <- nomo_cfa(model, data = s$validation)The split is explicit, reproducible, and logged as a design choice; no split ratio is presented as universally optimal.
nomo_compare() places fitted CFA models side by side. When the models
are nested (checked automatically), it reports the difference test that
matches the estimator; it also reports changes in fit indices, AIC and
BIC when they are defined, and side-by-side loadings, reliability, and
construct-separation evidence. A rationale is required, and no model is
selected automatically.
full <- nomo_cfa(
"A =~ a1 + a2 + a3 + a4 + a5\nB =~ b1 + b2 + b3 + b4 + b5",
data = nomo_demo_continuous
)
no_b5 <- nomo_cfa(
"A =~ a1 + a2 + a3 + a4 + a5\nB =~ b1 + b2 + b3 + b4 + 0*b5",
data = nomo_demo_continuous
)
cmp <- nomo_compare(
full = full,
no_b5 = no_b5,
rationale = "Does the weakly loading item b5 contribute to factor B?"
)
cmp
summary(cmp)To test whether an item is needed, fix its loading to zero instead of dropping the column: models with different observed variables describe different data, so they receive descriptive evidence only.
Milestone 5 completes the confirmatory measurement-model layer by separating score reliability, convergent evidence, and construct-separation evidence rather than collapsing them into one validity verdict.
rel <- nomo_reliability(cfa)
val <- nomo_validity(cfa, htmt = "both")
summary(rel)
summary(val)
plot(rel)
plot(val, type = "ave")
plot(val, type = "discriminant")nomo_reliability() uses current semTools::compRelSEM()
infrastructure. Model-based omega/composite reliability is primary for
the congeneric CFA workflow. Coefficient alpha is retained as a familiar
secondary statistic and is explicitly qualified by its stronger
assumptions.
For ordered indicators, the requested score scale remains visible. Observed ordinal-score omega is supported; observed-scale alpha is not silently replaced with a different latent-response or numeric-score estimand.
Sampling uncertainty can be requested explicitly:
rel_ci <- nomo_reliability(
cfa,
ci = "bootstrap",
ci_boot = 1000,
ci_seed = 2026
)
summary(rel_ci)
plot(rel_ci)Bootstrap intervals are optional because they require repeated CFA refitting. Point estimates remain the original reliability estimates; the bootstrap adds uncertainty rather than substituting a new estimand.
nomo_validity() keeps standardized loading evidence connected to the
fitted CFA and uses average variance extracted (AVE) as convergent
evidence. AVE is deliberately not reported as a reliability
coefficient.
The familiar AVE reference around .50 is a review prompt. A value
above or below it does not, by itself, declare a construct valid or
invalid.
For multi-construct models, nomo_validity() aligns:
- latent-factor correlations, including available CFA uncertainty;
- HTMT2 as the preferred congeneric-oriented construct-separation statistic;
- original HTMT as a comparison;
- optional Fornell-Larcker output as legacy/supporting evidence.
val <- nomo_validity(
cfa,
htmt = "both",
fornell_larcker = TRUE
)
summary(val)Values above the configured HTMT-family review reference prompt investigation of theoretical distinctiveness, item wording/content, cross-loadings, and construct overlap. They do not automatically merge constructs or delete indicators.
The package also refuses to manufacture simple-structure evidence when the estimand is ambiguous. Cross-loaded models, mixed indicator composites, and multi-group/multilevel HTMT requests are surfaced with explicit limitations rather than silently pooled or redefined.
A model can reproduce the covariance structure well while its indicators still provide weak reliability and convergent evidence. Conversely, two constructs can each show strong loadings, omega, and AVE while remaining empirically difficult to distinguish from one another.
That is the central Checkpoint B lesson:
Good global fit, high reliability, convergent evidence, and construct separation answer different measurement questions.
The full walkthrough is in the “From CFA to a defensible measurement model” vignette.
When a scale reports a total score and subscale scores, nomo_model()
writes higher-order and bifactor structures, and nomo_hierarchical()
reports how much of each score reflects the general factor:
subscales <- list(
Focus = paste0("x", 1:4),
Drive = paste0("x", 5:8),
Poise = paste0("x", 9:12)
)
bifactor <- nomo_cfa(nomo_model(subscales, structure = "bifactor"), data = dat)
h <- nomo_hierarchical(bifactor)
nomo_table(h, "indices") # omega total, omega hierarchical, ECV, PUC
nomo_table(h, "subscales") # what each subscale adds beyond the general factorThe indices carry their estimands and no pass/fail thresholds. A
bifactor model usually fits at least as well as the alternatives even
when it did not generate the data, so nomologR compares structures
with nomo_compare() but does not choose between them. See “Total and
subscale scores”.
Once a measurement model is established, nomo_scores() produces sum,
mean, regression, or Bartlett scores and reports what they are and are
not.
scored <- nomo_scores(fit, method = "sum")
nomo_table(scored, "diagnostics")Adding items is not arithmetic: it assumes a parallel model, with equal unstandardized loadings and equal residual variances, so for unit-weighted scores that constrained model is fitted and compared with the model you supplied. Every method reports Grice’s three criteria — validity, univocality, and correlational accuracy.
The third matters before scores are used in later analyses. Correlations
among scores do not reproduce correlations among the factors, and the
direction of the discrepancy depends on the scoring method and the model
rather than being a constant that could be corrected for, so nomologR
reports it rather than adjusting for it. Where a question can be asked
of the latent variables instead, asking it of scores replaces an
unbiased answer with a biased one.
One design is an established exception. For a linear regression among factors, Skrondal and Laake (2001) proved that regression-method scores for the predictors and Bartlett scores for the outcome, each block scored from a measurement model of its own, give consistent coefficients. The article applies it next to the two ways of getting it wrong. See “Scoring a measurement model”.
Milestone 6 makes the nomological network an explicit theory test rather than a post-hoc collection of correlations. Predictions are machine-readable before the model is interpreted:
h <- nomo_hypotheses(
"A -> B" = positive(min = .20),
"A <-> C" = negligible(within = c(-.15, .15)),
"A -> criterion" = positive()
)
net <- nomo_network(
model,
data = calibration,
hypotheses = h,
validation_data = validation
)
net
nomo_table(net, "relations")
nomo_table(net, "replication")
plot(net, type = "effects")
plot(net, type = "replication")nomo_network() keeps theory concordance, uncertainty, measurement
context, a-priori/post-hoc provenance, and replication separate. A
non-significant p-value never establishes negligibility; quantified
negligible predictions require a researcher-specified equivalence
region.
When a validation sample is supplied, the exact same prespecified fitted model is used again. The package does not respecify the validation model from the primary-sample results.
Milestone 7 evaluates equality constraints without treating one fit-change cutoff as a universal law:
inv <- nomo_invariance(
model,
data = dat,
group = "group",
levels = c("configural", "metric", "scalar"),
localize = TRUE
)
inv
nomo_table(inv, "fit")
plot(inv, type = "change")
plot(inv, type = "local_strain")Continuous and ordered indicators use identification-appropriate sequences. For categorical models, binary, three-category, and 4+ category structures are handled differently where identification requires it.
Partial invariance remains an explicit researcher decision:
release <- nomo_partial(
level = "metric",
syntax = "F =~ x2",
rationale = "Substantive and diagnostic review supports inspecting this loading."
)
inv_partial <- nomo_invariance(
model,
data = dat,
group = "group",
levels = c("configural", "metric", "scalar"),
partial = release
)Localized score diagnostics can identify where equality constraints are
strained, but nomologR never searches until it finds a
partial-invariance solution that “passes.”
The full walkthroughs are in the “Nomological network” and “Measurement invariance” vignettes.
8. nomo_run() — guided orchestration without hidden decisions
Milestone 8 connects the package components into a resumable workflow while keeping consequential choices explicit.
run <- nomo_run(
data = dat,
scales = list(WellBeing = c("w1", "w2", "w3", "w4"))
)
run <- nomo_run(
resume = run,
decisions = list(factor_count = 1L)
)
run <- nomo_run(
resume = run,
decisions = list(
cfa_model = list(
value = "WellBeing =~ w1 + w2 + w3 + w4",
rationale = "Prespecified one-factor measurement model."
)
)
)The guided object retains completed component results, sample roles, stage settings, explicit researcher decisions/rationales, and component evidence logs. Optional invariance and theory-specified network branches can be added for future stages without recomputing completed work.
Teaching mode presents consequential pauses as Observation / Reason / Options / Consequence. Research mode provides a compact view of the same underlying analysis.
nomo_run() never silently deletes items, creates a CFA model from EFA,
frees invariance constraints, respecifies a model, or declares a
construct valid/invalid.
If the measurement evidence prompts a change, nomo_revise() creates a
child workflow that keeps the parent as its documented ancestor:
revised <- nomo_revise(
run,
cfa_model = "WellBeing =~ w1 + w2 + w3 + w4\nw1 ~~ w2",
rationale = "Item wording suggests w1 and w2 share method variance.",
origin = "post_hoc"
)
nomo_table(revised, "lineage")The revision records what changed, why, and whether the change was
prespecified or post hoc; compares the parent and revised models with
nomo_compare(); and recommends confirming a post-hoc revision in
independent data. Revisions chain, so $lineage and the report keep the
whole history.
The full walkthrough is in the “Guided workflow with nomo_run()” vignette.
nomo_report() creates an archival report from a guided workflow,
including methods, evidence, researcher decisions, deviations,
citations, and session information: a self-contained HTML file, or a
Word document when file ends in .docx. See the reproducible
reporting
walkthrough
for the documented inputs and limitations.
The report cites the methods the workflow actually used, not every method the package offers. The same information is available directly:
nomo_methods(run) # methods this workflow used
nomo_methods(lineage = "historical") # shown for recognition, and why
nomo_methods(run, references = TRUE) # a reference list with DOIsEach method records its stage, whether it is historical, contemporary, or emerging practice, how the package uses it, its estimand and assumptions, and DOI-verified references. The research basis article explains the lineage labels.
For a thesis or manuscript, nomo_apa_table() formats a result as an
APA 7 table — a bold number, an italic title, no vertical rules, and
notes below — that knits directly into R Markdown or Quarto:
nomo_apa_table(cfa, "loadings", number = 1)
nomo_apa_table(reliability, number = 2)
nomo_apa_table(validity, "discriminant", number = 3)
nomo_apa_table(network, number = 4)Leading zeros follow the statistic rather than its value: reliability, correlations, CFI, and p lose theirs because they cannot exceed 1, while TLI, RMSEA, SRMR, and standardized loadings keep theirs because they can. No table labels a result as passing or failing. The discriminant table gives each pair of constructs its latent correlation with a confidence interval beside HTMT2 and HTMT, rather than the Fornell-Larcker matrix.
nomo_report(run, apa_tables = TRUE) appends the same tables for every
result a guided run holds, numbered in order, as a Manuscript tables
appendix.
The detailed release plan lives in
ROADMAP.md.
v0.9.0 is the last minor release before v1.0.0, whose release candidate is
planned for 2026-10-17
(#113). It is about how the
package presents itself and what it teaches:
- redesigned console output, plots, and report tables, with one flag wording throughout (#89);
- the historical record in
nomo_methods(): when each method was introduced, and what took over from each historical one, drawn into a timeline in the research-basis article; - two articles, "Teaching with nomologR" and "From content review to empirical
screening", with the walkthrough data shared with
contentvalidR(#60); - APA tables for convergent and discriminant evidence, and an item audit that reviews each item within its declared scale;
- every help page naming the fields of the object it returns (#114).
No computed estimate changed from v0.3.0; NEWS lists the changes to returned
columns, wording, and arguments.
v0.3.0 is the first release submitted to CRAN
(#39). Its scope was
kept deliberately lean for that reason
(#38;
milestone):
nomo_screen()andnomo_run()accept a handoff fromcontentvalidR, so items that passed content review are screened and modeled here, with the reasons for each held-back item quoted in the log (#46);- the guided workflow and its report carry careless-responding screens, scores, missing-data sensitivity, and APA tables (#73);
- a written API-stability and deprecation policy, described under Stability below (#74);
- full test coverage again, which found four defects, now fixed (#72).
Certification is recorded in #75.
v0.2.1 completed the v0.2 workflow for what happens after a
measurement model is established
(#70). It added
nomo_scores() (#33), careless-responding indices (#34), factor
determinacy and construct replicability (#56), disclosure of
relationships estimated between observed variables (#62), a parallel
reliability bootstrap (#42), nomo_apa_table() and Word reports (#35),
nomo_missing() (#32), and reports rendered from inside R Markdown and
Quarto documents (#40).
v0.2.0 made the workflow research-backed from historical to
contemporary practice and more useful to graduate students and
researchers. Scope was selected in
#22. It added learning
foundations (#25) with teaching datasets, runnable vignettes, and the
research-basis article; readable invariance labels (#31); maintenance
(#36); model comparison with nomo_compare() (#27); auditable revision
lineage with nomo_revise() (#28); the methods registry with
nomo_methods() (#26); interval-based replication-status language for
sign changes (#30); bifactor and higher-order models with
nomo_hierarchical() (#29); and release certification (#37).
v1.0.0 is planned as a joint release with
contentvalidR, the
package’s content-validity counterpart
(#53). It is the
stability promise: after it, existing behavior changes only after a
deprecation period. Its scope is set in
#113. The companion
article on the shared item set is done
(#60).
Larger extensions, such as ESEM, IRT/DIF, Bayesian SEM, longitudinal
invariance, and multiple imputation, are candidates for 1.x. They are
additive, so they do not need to precede the freeze. None was rejected,
and none is a commitment until selected.
The complete v0.1.0 release track is:
- Data & Item Audit —
nomo_screen()complete - Factor-Retention Evidence —
nomo_factors()complete - Exploratory Factor Analysis —
nomo_efa()complete - Confirmatory Factor Analysis —
nomo_cfa()complete - Reliability + convergent/discriminant evidence — complete
- Theory-Specified Nomological Network — complete
- Measurement Invariance — complete
- Guided pipeline —
nomo_run()complete - Reproducible report —
nomo_report()complete - v0.1 release hardening and infrastructure — complete
v0.1.0 was the first stable public release.
contentvalidR is the partner
package for content validity. Its content_handoff() records which items passed
content review and why the others were held back, and nomologR reads that record
so the empirical stage starts from those decisions without retyping anything. A
handoff brings:
- the items carried forward, and the items held back. Each held-back item's status and recommendation are quoted in the decision log. nomologR never reinstates it; doing so is a researcher decision to record.
- the scales, which become the run's scales;
- the declared keying and the response scale, which the keying notes and the careless-responding indices use. The data are never recoded.
Pass the handoff itself, not h$items, so everything it records travels with
the items:
nomo_screen(responses, items = h) # screens the carried items
nomo_run(responses, scales = h) # runs the empirical stage on the handoff's scalesHow nomologR reads a handoff.
- Each item's decision comes from
carried, which says whether it travels, andstatus, which says why. recommendationstates the same decision in the workflow's own words. Likerule, it is prose, so nomologR quotes it and never matches on it.- A decision is never re-derived by comparing a statistic with its criterion, or by branching on the version that produced the handoff.
- A
keyingofNAmeans nobody said, never that an item is forward-worded.
Versions. The handoff is schema version 1. Within it, fields are only
added, and nomologR ignores fields it does not know. A schema version nomologR
does not read is refused, with both packages' versions named. The reader is
tested against handoffs from contentvalidR 0.6.0 through 0.10.0. They are
generated from each release tag and stored with their checksums, and neither
package depends on the other.
For more, see:
vignette("content-review"), which carries one reviewed item set through the screen;contentvalidR's handoff guide and its joint walkthrough.
From v0.3.0, the first CRAN release, nomologR’s public interface
changes only after a deprecation period, so code written against one
release keeps working in the next.
- What it covers. The exported functions, their documented arguments
and defaults, the documented fields of returned objects,
nomo_table()types, and decision-log columns. - Deprecation. A breaking change, including a changed default that changes results, is deprecated for at least one minor release first. The deprecated form keeps working, warns once per session, and is listed in NEWS.
- Additions. New functions, arguments, fields, and log rows can arrive in any release, so address fields by name.
- Experimental.
nomo_missing()and the layout ofnomo_apa_table()tables are experimental until 1.0.0.
The full policy is on the package help page, ?nomologR. The
contentvalidR handoff is versioned by schema. Within a version, fields
are only added, and anything else is a new version agreed between the
two packages (#46).
- Measurement before structure.
- Evidence accumulates; validity is not a single statistical test.
- Cutoffs are reference points, not universal laws.
- Estimator and correlation choices should respect item type.
- Modification indices do not authorize automatic model respecification.
- Nomological evidence begins with explicit theoretical predictions.
- Null predictions require evidence beyond
p > .05. - Consequential decisions should be visible and reproducible.
Use citation("nomologR") for the installed package version. The
default-branch
CITATION.cff
describes the current development source; each release tag retains the
citation metadata appropriate to that release.
The current development source is licensed under the GNU General Public License, version 3 only (SPDX: GPL-3.0-only). See LICENSE.md and the preserved attribution in inst/NOTICE.
Previously published releases, including 0.1.0, retain their original
MIT license. Stable installation currently retrieves that release; the
next published release will carry GPL version 3 only. This source
transition does not relabel existing tags or release artifacts.