Changelog
Source:NEWS.md
DeclareDesign 2.0.0
CRAN release: 2026-10-25
DeclareDesign 2.0 is a ground-up reimplementation on tidyverse primitives. The declaration syntax is unchanged; the changes below are at the edges. vignette("declaredesign2.0") gives the reasoning for each and a grep table for porting a script.
Breaking changes
-
redesign()reaches a name the design reads (a value named above the design, a designer function’s argument, or adeclare_parameters()entry) and no longer a number written inside a step.redesign(design, N = 1000)overdeclare_model(N = 500)errors with the two ways to name the value; in 1.x it warned and returned the design unchanged. -
declare_estimator(inquiry = )takes the inquiry’s label as a string. Passing the inquiry step object is an error; in 1.x it linked the estimator to an inquiry called"inquiry"and returnedNAestimands. -
diagnose_design()andsimulate_design()dropmake_groups,add_grouping_variables, andfuture.seed, each with an error naming the replacement. Group withsimulate_design() |> group_by() |> diagnose_design(); seed withset.seed()before the call. -
redesign()’s first argument is.designandexpand_design()’s is.designer, and both take.expandafter the dots, so a parameter namedexpand, ordor any other prefix ofdesign, is no longer swallowed by partial matching.expand_design()dropsprefix, which named the designs it returneddesign_1,design_2, and so on; designs are now named by the values that distinguish them. -
draw_data()no longer runs part of a design on supplied data;data,start, andendare gone, and passing them stops with an error naming the replacement unless the design reads a parameter by that name. Useget_estimates(design, data)to run estimators on supplied data anddesign[i:j]for a slice. -
label_estimator(fn = )andlabel_test(fn = )are.method =;set_diagnosands(x = )isdesign =;reshape_diagnosis()dropsselectandexclude. -
compare_designs(),compare_design_code(),compare_design_summaries(),compare_design_estimates(),compare_design_inquiries(),compare_design_data(), andprint_code()are defunct and say so.compare_diagnoses()stays. -
declare_potential_outcomes()anddeclare_reveal()are defunct;declare_estimators()is gone. - Every table of estimates carries an
estimandcolumn and putsestimator,inquiry, andestimandafter the tidied statistics. Two estimators with one label are renamed apart with a message rather than an error.
Deprecations
-
expand_design()warns once per session; call the designer andredesign()the result:redesign(designer(), N = c(100, 200)). -
set_diagnosands()warns once per session; pass the diagnosands to the diagnosis instead,diagnose_design(design, diagnosands = ...). An attached set replaces the defaults rather than adding to them, and does not surviveredesign(), which now warns when it drops one. -
insert_step(),delete_step(), andreplace_step()warn once per session; rebuild the design withdesign[1:2] + new_step + design[4:5]instead. -
declare_population(),model_handler(), andtidy_estimator()warn once per session. -
declare_estimator(model = )is read as.method =with a warning.
New
-
declare_parameters()names the values a design can be redesigned over, anddeclare_notes()names the quantities it works out along the way;design_parameters()anddesign_notes()list them. A declared parameter is bound for every step after it, including a helper function that reads it. -
run_design(),draw_data(),draw_estimands(), anddraw_estimates()take named parameter values, sodraw_data(design, theta = 0.5)draws from a declaration that leavesthetafree (closes #497). The values go throughredesign(), so the rules about which names a design accepts are stated in one place, and supplying several values is refused rather than narrowed: sweep withredesign()and draw from each. - A design is a value: it carries the objects its expressions read, so it survives
saveRDS(), reaches a parallel worker, and does not change meaning when a workspace variable is edited (closes #293). - Step-level
drawsfan a step out and hold everything upstream fixed; the diagnosis of such a design carries avariance_decompositionattributing the variance of each per-simulation quantity to the step that generated it. - An estimator that errors on a draw under
simulate_design()is recorded as a row witherror = TRUE, one warning per run gives the counts, and diagnosands use the draws that succeeded (closes #385).run_design()anddraw_estimates()still stop. - Every simulation draw runs on its own L’Ecuyer-CMRG stream under any
future::plan(), so a seeded run gives the same table sequentially and in parallel, and the caller’s own generator is left in the same place either way: one draw on, whichever plan is active (closes #445).furrrdraws a value from the caller’s stream whenever it is handed a seed, so a parallel run used to leave it one uniform further on than a sequential one. -
progress = TRUEonsimulate_design()anddiagnose_design(), orprogressr::handlers(global = TRUE)once per session, reports progress through progressr, from parallel workers too. -
print(design)shows the calls that declared the steps, as 1.x did, and the parametersredesign()can reach.summary(design)runs the design once and says under each step what it did to the data (rows kept, columns added, dropped or changed, the estimand, the estimate), returning the pieces as asummary.designobject;run = FALSEskips the run. -
diagnose_design()accepts a simulations data frame in place of a design and honours its grouping.diagnose_simulations()does the same thing under a name that says what the argument is. -
default_diagnosands()andselect_diagnosands()build a diagnosands set from names;tidy()works on a diagnosis. - Estimator and step arguments pass to
.methodor the handler as written, so methods that do their own non-standard evaluation work (closes #463; also #456, #457, #482, #509).
Failures that are now reported
- A
declare_measurement(),declare_assignment(), ordeclare_sampling()step that supplies its own rows, rather than receiving them from the model, no longer appends a row id column. 1.x asked for this at every call site but never passed the flag on tofabricate(), so the steps avoided a strayIDcolumn only because they normally run with data already in hand, and a declaration that spelled outhandler = fabricategot theIDanyway. The rule now belongs to the verb, whichever handler is named. - An inquiry that no estimator targets keeps its own row in a diagnosis, with an NA estimator and its
mean_estimand, as in 1.x; the join had dropped it. - The warning that estimates and inquiries were matched ambiguously fires only when the match was ambiguous (closes #479). Two estimators both naming
ATE, with other inquiries declared beside it, produce more rows than either table had, because the unanswered inquiries keep their own rows; the old test was that row count alone, so it reported a multiplication that had not happened and advised naming an inquiry the estimators had already named. A shared-column match now warns only where one key value carries more than one row on both sides, and says which key. A match on no shared column warns as before. -
declare_sampling(handler = )takes a custom sampling function, as the other data verbs do. - An estimator naming an inquiry no step produced warns once, naming the labels that exist.
- A sampling step that produces no
Scolumn and has nofilterwarns once that every row was kept. - An error inside a step names the step and its verb, with the original error as the cause.
- A redesign of a name the design does not use warns; a bare vector handed to a vector-valued parameter warns that it is being read as several designs.
-
redesign()names the list it returns by the values that distinguish each design (closes #472).redesign(design, N = c(10, 20))givesN = 10andN = 20, so a simulation’sdesigncolumn andbind_rows(.id = "design")carry the values that vary; 1.x named themdesign_1,design_2, which is what a reader already knew from the position. A parameter whose value cannot be written down, such as a function or a data frame, leaves the whole list positional.