Changes in version 1.1.0 (2026-10-24) - Fixed two unclear refusals for fixed center and time effects. An all NA center column was refused with "values in center_weights_for_outcome_goal must sum up to 1", or with a message about the length of center_weights_for_outcome_goal when the weights were given. It is now refused with the same kind of message an all NA period column gets. A center or period whose rows all have zero weight counted as usable, so when no other center or period was left the fit failed with a rank deficiency message that blamed collinear predictors. Such a level no longer counts, and the message names the center or period that still has a positively weighted row, if there is one. - get_confidence_set() now refuses a fitted model with a factor, character or logical predictor that is not treatment coded with its first level as the reference and each coefficient named after its level. Examples are a model fitted with contr.sum, contr.SAS, a global options(contrasts = ...) that sets another coding, the default coding of an ordered factor, or a treatment matrix such as contr.treatment(3) whose columns are not named after the levels. The function reads every such predictor that way. For a binary outcome it returned a wrong interval and confidence set without any error. A treatment matrix whose columns are not named after the levels gave a wrong result only for the period, whose dummy is found by its level name, and is now refused for every column so that one rule applies. For a continuous outcome it usually failed with a message about unmatched columns, but it could also return a wrong interval without any error, for example for a factor with levels 0 and 1. The new message names each such column and shows how to refit the model. Models fitted by lago_optimization() already use this coding, so its results are unchanged. Results from direct calls with such a model should be recomputed after refitting it. - Fixed a data column named weights replacing the weights of the outcome model. glm() looked up its weights argument in the data first, so such a column replaced the weights argument of lago_optimization(), replaced center_sample_size for center level data, and weighted the fit even when no weights were given. A row with a missing value in that column was also dropped from the fit. The outcome model now always uses the weights argument, or center_sample_size for center level data, and a column named weights is used only where it is named, such as an additional covariate. This also applies to lago_sensitivity() and lago_budget(). Results from data with a weights column should be recomputed. - Continuous confidence covariance now uses the actual fitted model's prior weights. Identity covariance uses weighted bread and residual scores, with estimated residual scale divided by the positive weight row count minus the design column count when no clusters are present. Logit covariance retains the existing Gauss Newton bread and HC0 or CR0 sandwich, now with weighted scores. Both clustering dimensions and their intersection use the same fitted weights. Weights are relative observation weights, so a common positive rescaling leaves covariance unchanged for a fixed fit. Zero weight rows contribute nothing, malformed fitted weights are refused, and unclustered identity covariance requires positive residual degrees of freedom. Binary covariance and existing finite unit weight results are unchanged. This extends the existing continuous formulas, not a frequency weight, survey design or general GLM covariance. The existing continuous quasibinomial family variance mismatch is not changed. Results from continuous fits with nonconstant weights should be recomputed. - Fixed recommendations and confidence output that depended on global contrast options or polynomial coding of ordered covariates. Every categorical predictor in the outcome model now uses treatment contrasts, including factor, ordered factor, character and logical covariates and center characteristics. This overrides explicit contrast attributes without changing input data or global options. Additional categorical covariates are held at their reference level. For a categorical center characteristic with two levels, optimization values 0 and 1 select the reference and other level. Numeric predictors and the existing center and period checks are unchanged. Results from affected runs should be recomputed. - Fixed wrong results when the center or period column was not coded with one effect per level. glm() fits an ordered factor with polynomial contrasts, and a global options(contrasts = ...) such as contr.sum changes the coding of every factor, but the package read the resulting coefficients as one effect per center or period. With fixed center effects, a binary outcome or any run without the confidence set returned a wrong recommendation and estimate without any error, and a continuous outcome failed when it computed the confidence set. With fixed time effects an ordered period was refused except at its first level, and under a global coding such as contr.sum or contr.helmert whether a period was refused depended on its label. A period that was not refused could give a wrong result in the same way. The outcome model now always fits center and period with treatment contrasts, and an ordered center or period is converted to an unordered factor with the same levels, with a message. Results from such runs should be recomputed. - Fixed center or time effects with fewer than two centers (or periods) are now refused with a message that says whether the data has no observed period, only one center (or period), or only one with a row the outcome model can use, and names that level, instead of failing inside glm() with "contrasts can be applied only to factors with 2 or more levels". A fit with center or time effects where no row is usable at all is refused with a message that says so. - Fixed results that used rows the outcome model never fitted. glm() drops a row with a missing value in any model variable, but the confidence set was computed over every row, and the center weights covered every center in the data. With a continuous outcome and missing values the confidence set came back empty or wrong, with or without fixed center or time effects, and in some cases the call failed (for example with fixed center effects and a missing covariate, or a missing period with fixed time effects), so such results should be recomputed. A center whose every row has a missing model variable still got a weight: lago_optimization() failed when it computed the confidence set, and otherwise (without the confidence set, after shrinking, and in lago_sensitivity() and lago_budget()) it returned results with mismatched center weights, silently or with at most R's generic "longer object length" warning (none when the number of centers is a multiple of the number fitted), so every run with such a center should be recomputed. The confidence set now uses the rows the model was fitted on, and the center weights now cover only the centers it fitted. A center the model could not fit is left out of the default weights with a warning that names it (the other centers are still sized by all of their rows, and the shrinking target and default grid steps still use every row, since the stage-1 intervention was delivered either way), and naming it in center_effects_optimization_values, or giving it a non-zero weight in center_weights_for_outcome_goal, is refused with a message that names it. A factor center column with unused levels no longer shifts the default weights onto the wrong centers, and an explicit NA center level (as from addNA()) is kept as a center when another center is left out (a continuous outcome's confidence set still cannot use an explicit NA level that a fitted row has in the center or period column, or as a non-reference level of any other factor column, and now says so instead of reporting a missing cluster id or returning an empty set), and a row with a missing center no longer counts as a facility when checking center_weights_for_outcome_goal. For center level data, a center with no center_sample_size on any row is refused with a message that names it when the default weights are computed, and a center whose size is missing on only some rows is sized by its first observed value. - Fixed lago_optimization() failing for a continuous outcome when the data is a tibble, such as the bundled BB_data. With the identity link it failed with "undefined columns selected" when fixed center or time effects were included, and with the logit link it failed with "Not compatible with requested type" with or without them. - The in-browser playground now supports fixed center effects: tick "Fixed center effects" to adjust the outcome model for each center, matching lago_optimization()'s include_center_effects, and choose whether to optimize for the size-weighted average center or for one named center (center_effects_optimization_values). The center column comes from the data's own center column when it has one, used as is, otherwise from a column you pick (site_name for BB_data), and the shown R code includes the line that builds it on a copy of the data. Fixed center effects replace center characteristics, which the package cannot fit together, and the center column is not offered as a component or an additional covariate. The sensitivity sweep, budget search and Share link carry the setting. A center with no row where all the model columns are observed is left out of the fit, and the page asks for another center if you chose it as the one to optimize for. - The documentation of center_effects_optimization_values now says it is a single center name (a character string), which is what lago_optimization() has always required, rather than a numeric vector. - Fixed an outcome goal or a power goal failing when the outcome column has missing values. The check of the outcome goal's direction compared it against the outcome mean without removing missing values, so any missing outcome stopped lago_optimization() with "missing value where TRUE/FALSE needed". The power calculation counted a row with a missing outcome toward its arm's size but made the arm's sum missing, so the default unconditional approach failed the same way and the conditional approach silently returned a power-implied outcome of 0, which dropped the power goal. Both now use only rows with an observed outcome, as the final treatment-versus-control test does, and a power goal now needs a group column with both arms present and an observed outcome in each. - Fixed the in-browser playground misreading a column named like a built-in JavaScript property (for example constructor or toString). Ticked as an intervention component, it made the generated R code, Run, the sensitivity sweep and the budget search fail, and its bounds and unit cost came up collapsed or blank instead of the column's defaults. As a center characteristic, its held-at value came up blank and was left out of the call. - The in-browser playground now supports a power goal (binary outcomes): tick "Power goal" to plan for the next stage's statistical power alongside, or instead of, the outcome goal, matching lago_optimization()'s power_goal, num_centers_in_next_stage, patients_per_center_in_next_stage, power_goal_approach, icc and power_goal_cluster_id. The treatment arm comes from the data's own group column when it has one (used as is, so it must hold only "treatment" and "control" with both present), otherwise from a complete 0/1 column you pick (pre_post for BB_data, as in the package tests), and the shown R code includes the lines that build it on a copy of the data. Direction must be Maximize, and Run stays disabled with a hint until the power settings are complete. The ICC cluster column list offers only columns with at least two non-missing centers with an observed outcome in each arm, checked when an ICC is entered. The sensitivity sweep can vary the power goal, and the budget search and Share link carry the settings. On BB_data the stage-1 data already powers the comparison, so the power goal alone recommends no intervention, and the printed result reports the power-implied outcome. - The in-browser playground now has an optional "Additional covariates" section: tick any columns (numeric or not, for example a site or arm label) to adjust the outcome model for them, matching lago_optimization()'s additional_covariates. A column can play only one role at a time (intervention component, center characteristic, or additional covariate). The chosen covariates ride along in the Share link. - The in-browser playground now supports fixed time effects: when the loaded data has a period column (so, an uploaded CSV, since no bundled dataset has one) a "Fixed time effects" checkbox appears with a period value to optimize at, matching lago_optimization()'s include_time_effects and time_effect_optimization_value. (Time effects are not carried in the Share link, which is offered only for bundled datasets.) - The in-browser playground now has an optional "Interaction terms" section: with two or more intervention components ticked, tick a pair to model their product (for example coaching_updt × launch_duration). It sets include_interaction_terms = TRUE and main_components, appends the a:b term to intervention_components, and prepends the line that creates the interaction's product column so the shown R code stays copy-pasteable. Bounds, costs and the confidence set stay per main component, and the sensitivity sweep, budget search and Share link inherit the interactions. - The in-browser playground's "Share link" now carries the whole page, not just the core optimization: the center characteristics and their held-at values, the interaction terms, the additional covariates, a budget you have entered, and the sensitivity sweep's input, range and step count all round-trip through the URL, so an opened link reproduces the full setup. Older or hand-trimmed links that omit these simply keep the page's own defaults for those controls. - The in-browser playground now has an optional "Center characteristics" section: tick numeric columns (for example a center's birth volume) and set the value each is held at, and the recommendation is computed for a center with those characteristics, matching lago_optimization()'s center_characteristics and center_characteristics_optimization_values. The sensitivity sweep and budget search inherit the same setting. A column can be an intervention component or a center characteristic but not both. - The in-browser playground now has a "Budget" section that surfaces lago_budget(): enter a cost budget and it finds the best outcome reachable within it and the intervention that reaches it (the reverse of setting an outcome goal). It reports the recommendation, its cost and estimated outcome (or that no intervention fits), notes when the budget does not bind, and draws the cost/outcome frontier as an interactive D3 chart with the budget line and the chosen point marked. The budget field auto-fills a sensible default from the current costs and bounds until it is edited. - Added lago_budget(), which answers the reverse of the usual LAGO question: given a fixed cost budget, what is the best outcome reachable, and with which intervention? It searches the outcome goal along the least-cost frontier lago_optimization() traces out and returns the most ambitious reachable goal whose recommended cost fits the budget (the lowest reachable outcome within budget for outcome_goal_intention = "minimize"), with print() and plot() methods and a frontier of the cost-vs-outcome points searched. Like lago_sensitivity(), it reuses a fitted lago result's call or takes the arguments directly, and it never computes the confidence set. It reports whether the budget is feasible and whether it binds. - Fixed the in-browser playground's default configuration not producing a result. It prefilled the first binary column as the outcome, which for BB_data is the pre_post pre/post period flag: perfectly separable from the coaching components, so the outcome model never converged and Run (and the sensitivity sweep) errored out of the box. Each bundled dataset now starts from a preset that fits (BB_data uses the pp3_oxytocin_mother outcome with the coaching_updt and launch_duration components; mtcars uses mpg with gear and qsec), and switching datasets or pressing Reset lands on that dataset's runnable preset. Uploaded CSVs keep the generic prefill. - The in-browser playground now has a "Sensitivity sweep" section that re-runs the recommendation across a range of one input, using lago_sensitivity(). Pick the input to vary (the outcome goal or a cost multiplier), a from/to range and a number of steps, and it draws how the recommended cost moves as an interactive D3 line (with each run's per-component recommendation, estimated outcome and status in a table beneath). The confidence set is skipped during the sweep for speed, and a failed run is shown as a gap rather than aborting the sweep. - The in-browser playground now has a "Share link" button that encodes the whole current setup (bundled dataset, outcome column and type, intervention components, their bounds and costs, the goal and direction) into the page URL and copies it to the clipboard. Opening that link restores the configuration on load, ready to run. Costs are encoded as per-component coefficient vectors, so a curve shaped in the cost designer is preserved along with plain unit costs. Sharing is offered only for the bundled datasets, since an uploaded CSV cannot be carried in a URL. - The in-browser playground and the cost designer now round-trip. A "Shape costs in the designer" button opens visualize_cost() in the browser pre-loaded with the intervention components, bounds and unit costs configured in the playground (the designer reads this from its page URL query and falls back to its built-in example when the query is absent or malformed; a plain visualize_cost(...) call is unchanged). In the designer, a "Use these costs in the playground" button sends the shaped cost functions back to the playground tab, which then uses them as cost_list_of_vectors for the optimization (shown in the reproducible R code) instead of the linear unit cost; adjusting a component's unit cost reverts it. - visualize_cost() now has a "Cost function form" toggle that switches all components between the linear and cubic cost functions live, instead of the form being fixed for the whole session by default_cost_fxn_type. Switching resets each component to that form's initial coefficients and shows the right number of coefficient sliders (2 for linear, 5 for the cubic's degree-4 total cost). The cubic form opens on a visibly curved (but still valid) demo cost rather than the near-linear one cost_fxn_calculator() returns for typical unit costs, so the toggle clearly shows a cubic. This also gives the in-browser cost designer a cubic option. - The total cost curve in visualize_cost() can now be reshaped by dragging: each curve carries a draggable handle at several points along it, and dragging one refits the cost function through the new set of points (and updates the sliders), instead of the single right-endpoint handle that only rescaled the whole curve. - The fitted outcome model's Call:, shown in the "Outcome model fit" section of print() and summary(), now displays the actual model formula instead of the literal word formula. This also makes the console output identical across R versions: a change in recent R to how the unrecorded formula deparses had otherwise made the output (and its snapshot tests) version dependent. - Added a live in-browser demo to the documentation site (live-demo.html) that runs the real package client-side with webR (R compiled to WebAssembly), so anyone can try lago_optimization() with no installation, plus an interactive playground (playground.html) where you pick a bundled dataset or upload a CSV, configure the model with sliders and toggles, and see the recommendation drawn with the package's own D3 charts alongside a copy-pasteable R snippet. A GitHub Actions workflow builds the package to WebAssembly with the rwasm toolchain and publishes it as a small CRAN-like repository alongside the site. - Added a browser version of visualize_cost(): the same Shiny cost-function designer now runs client-side on the documentation site (/visualize-cost/), exported with shinylive, so you can shape each component's cost curve and copy the coefficient list with no install. visualize_cost() was refactored to build its app object separately from launching it, so the local and in-browser versions share one implementation. - lago_report() now renders an interactive HTML dashboard: the confidence set is a hover-enabled D3 plot (a scatter for two components, a strip for one) with the recommended intervention highlighted, and each intervention component gets interactive total-cost and marginal-cost curves. The report stays a single self-contained offline file (D3 is inlined, no CDN or server) and its API is unchanged; rendering now also uses jsonlite (a new Suggests). - Added an MCP (Model Context Protocol) server to the Python package (python -m lago.mcp_server) that exposes optimize and sensitivity as tools any MCP-aware AI agent can call, plus a sensitivity() function in the Python wrapper. - Added lago_sensitivity(), which re-runs an optimization across a sweep of one input (an outcome or power goal, or a "cost_multiplier" that scales all costs) and reports how the recommended intervention, its cost, and the estimated outcome move, with print() and plot() methods. - The package is now installed and loaded as LAGOtrials (call library(LAGOtrials)); the previous LAGO identifier clashed with an archived CRAN package. Function names, the lago result class, and the LAGO method name are unchanged. - visualize_cost() now stores the returned cost list in the lago_cost_list option instead of assigning it to the global environment; retrieve it with getOption("lago_cost_list"). - Added tests for the interaction-terms optimization path and the outcome-model fit warnings, and excluded the interactive visualize_cost() app from the coverage figure so it reflects the testable R code. - Added a CITATION.cff so the repository can be cited, plus contributing guidelines, a code of conduct and issue/pull-request templates. (#86) - Added test-coverage reporting, cross-platform (macOS, Windows, Linux) continuous integration, project-status and coverage badges, and a social-preview card. (#83, #84, #85) - Added a Python wrapper (in python/, importable as lago) that calls LAGOtrials through rpy2. (#81) - visualize_cost() now draws its cost curves client-side with D3, with hover read-outs, a draggable curve endpoint, and invalid-state highlighting. (#80) - The clustered variance estimator for logit outcomes is now computed by a compiled Rcpp kernel, so Rcpp is a new dependency. (#79) - lago_optimization() now refuses an additional covariate whose column is entirely NA, naming it. (#77) - A rank-deficient outcome model is now refused up front with an error naming the aliased terms. (#74, #76) - Added a warning when glm() drops an additional covariate as collinear. (#76) - Fixed confidence-interval bounds for a binary logit outcome being reported outside [0, 1]. (#75) - Fixed the estimated outcome being scaled by center weights that did not sum to 1. (#72, #75) - Added a warning when a binary outcome's estimated outcome is reported outside [0, 1]. (#75) - Added a warning when a numeric additional covariate observed away from 0 is held at 0 for the confidence set. (#75) - Fixed the confidence interval and set being computed on the logit scale for an identity-link binomial model. (#74) - center_weights_for_outcome_goal must now be numeric, finite and non-negative, checked at both entry points. (#73, #74) - Fixed the numerical optimizer failing with an opaque error when every restart failed, so it now gives an actionable message. (#73) - Fixed a factor covariate named like "center" or "period" being miscounted as a fixed effect when get_confidence_set() is called directly. (#73) - Fixed outcome_goal_intention = "minimize" ignoring the outcome goal on a logit link. (#70) - Fixed the recommended intervention sometimes falling outside the intervention bounds. (#70) - Fixed the numerical optimisation returning its most expensive candidate solution instead of the cheapest. (#70) - Fixed the estimated outcome being wrong when more than one center characteristic was supplied. (#70) - link = "probit" and link = "log" are no longer accepted, since only logit and identity were ever implemented. (#70) - Fixed $est_outcome_ci sometimes reporting the wrong interval instead of the one at the recommended intervention. (#68) - Fixed the confidence set occasionally dropping one of its qualifying interventions. (#68) - Fixed confidence_set_size_percentage understating the size of the confidence set. (#68) - A confidence set containing exactly one grid intervention is no longer discarded as empty. (#68) - Fixed confidence-interval bounds being attached to the wrong interventions under two-way clustering. (#68) - get_confidence_set() now returns rec_int_ci, the interval at the recommended intervention (its return shape changed for direct callers). (#68) - The confidence set, its size, and the estimated-outcome interval can differ from earlier releases wherever the previous values were wrong. (#68) - Fixed get_confidence_set() pairing the prediction matrix with the model coefficients by position instead of by name. (#68) - Fixed the estimated outcome being reported as 0 for the reference period when fixed time effects were included. (#68) - Fixed the estimated-outcome interval being computed at the wrong time period. (#68) - Fixed an additional covariate or center characteristic named like "center" being miscounted as a fixed center effect. (#68) - Fixed a factor or character additional covariate or center characteristic being paired with the wrong coefficient or erroring. (#68) - Fixed the confidence set changing when the rows of data were reordered. (#68) - Fixed passing more than one center characteristic to the confidence set failing or duplicating a column. (#68) - Added runnable @examples to every exported function that lacked them. (#67) - Restyled the print() and summary() console output with boxed cli sections. (#65) - The fitted outcome model ($model) and the estimated-outcome interval ($est_outcome_ci) are now returned on the result. (#65) - Added lago_report(), which renders a self-contained HTML report of a result. (#64) - lago_optimization() now returns a "lago" object with print(), summary(), and plot() methods. (#61) - Added an "Optimizing an intervention with LAGO" vignette. (#61) - Added a hex logo and refreshed the README header. (#56, #61) - Added a quiet argument to lago_optimization() that skips progress messages. (#60) - visualize_cost() gained a copy-to-clipboard button, a numeric cost summary, and returns the cost list on close. (#59) - Expanded the reference documentation for the bundled datasets. (#58) - Added a GPL-3 LICENSE file. (#56) - Fixed a crash when a single intervention component was used. (#54) - Added a pkgdown documentation site published to GitHub Pages. (#53) - The overall intervention-effect test result is now returned in the result object. (#49) - Added an automated test suite (testthat) and continuous-integration checks. (#45) - lago_optimization() now accepts a standalone power goal, an outcome goal, or both together. (#40) - Added non-fatal outcome-model fit diagnostics. (#36) - visualize_cost() sliders now support per-component default and custom ranges. (#34) - Supplying optimization_grid_search_step_size now switches the optimization method to grid search automatically. (#32) - Added an icc argument so the power goal can account for within-center clustering. (#29) Known limitations - The confidence set is the set of interventions whose confidence interval covers the outcome goal, which is a two sided test and does not depend on outcome_goal_intention. Under outcome_goal_intention = "minimize" it can therefore contain interventions whose estimated outcome is above the goal, and which cost more than the recommendation, because their interval still reaches the goal from above. Read the confidence set as the interventions the data cannot distinguish from the goal, not as the interventions that meet it. The estimated outcome and its interval, reported as est_outcome_goal and est_outcome_ci, are computed at the recommended intervention and are direction independent, so they are unaffected.