Changelog
Source:NEWS.md
masque 0.9.2 (development version)
New features
-
mask()gains acoordsargument to coarsen geographic coordinates as part of masking. Declare one or more latitude/longitude pairs (for examplecoords = list(c(lat = "GPS_S", lon = "GPS_E"))) and each pair is passed through synthesis untouched and then displaced in place by the on-landjitter_coordinates()geomask (a 5-20 km donut by default), instead of being copula-scrambled into implausible locations. A pair may be a barec(lat =, lon =)vector or alist(lat =, lon =, min_km =, max_km =, ...)that also carries jitter parameters; both spellings work (numbers supplied through the vector form are coerced back from strings). A declared pair always survives, coarsened; the recipe records that it was coarsened, andapply_recipe()retargets a pipeline to the real coordinates.
masque 0.9.1
Breaking changes
-
propose_roles()now protects a conservatively detected multi-environment allocation by default. Before 0.9.1, an environment column not recognised by the name heuristic could remaincovariate/scramble, which moved observations between environments. High-confidence categorical environment columns now becomedesign/keepin local mode anddesign/aliasin collaborate mode. Numeric environment columns remaindesign/keepand raise a classed disclosure warning in collaborate mode. Disable the new scope path explicitly when reproducing the former detector output:detect_design(df, env = FALSE) propose_roles(df, detect = FALSE)
New features
-
detect_design()gains an append-onlyenvargument and an orthogonal environment-scope contract. Useenv = NULLfor conservative automatic resolution,env = c("site", "year")for an explicit composite, orenv = FALSEfor the whole-table compatibility path. The returneddesign_summaryrecords scope confidence, environment basis, experiment groups, exact treatment-connectivity components, and advisory per-environment design summaries without storing the source data. -
print()on adesign_summarynow leads with scope and reports bounded, aggregated MET diagnostics.plot()supports compact base and ggplot2 environment overviews plus one selected field layout throughenvironment =. Connectivity and inner-design labels are diagnostic and do not claim to reconstruct the original randomisation. - New
jitter_coordinates()coarsens latitude/longitude in place by an on-land geographic-masking jitter (donut or Gaussian), so a synthetic table can carry realistic coordinates without revealing a true field or farm location. The donut default moves every point 5-20 km in a random direction, re-drawing until it lands on land (viamaps); the NA pattern and the latitude/longitude pairing are preserved. It complementssynthesise_geospatial(), which instead re-anchors coordinates at user-supplied fake centroids.
Bug fixes
Omitting
modefrommask()ormask_set()now inherits the mode stored bypropose_roles(), rather than silently falling back to local defaults. An explicit collaborate-to-local change raises a classedmasque_mode_downgradewarning.mask_set()rejects role plans carrying mixed mode provenance.mask()andmask_set()now warn (classedmasque_mode_unset) when nomodeis supplied and therolestable carries no mode provenance, instead of silently masking inlocalmode. Adata.table()wrap or asaveRDS()round-trip strips the tibble attribute that records the mode, so the warning turns a silent local-mode downgrade into a recoverable one.Site-only environment candidates now require replicated treatment evidence across sites for automatic promotion. A block nested within one farm is left for review rather than being misclassified as an environment.
Environment connectivity diagnostics now guard both dense incidence and environment-adjacency allocations. Oversized problems return an explicit
not_computeddiagnostic instead of attempting an unsafe allocation.Experiment-group diagnostics tolerate a small, recorded treatment overlap while rejecting genuinely interleaved groups. The evidence now reports the confined and shared-treatment fractions.
Explicitly pinned role actions are no longer overwritten when a detected environment recommendation promotes a column to the
designrole.A medium-confidence or ambiguous environment candidate is now preserved as
design/keepin both modes rather than falling through to acovariatedefault. Previously an unreplicated environment named with a site token outside the design-name heuristic (for examplecounty,locationorfarm) could be silently row-permuted by a defaultmask(), moving observations between environments. Such a column is kept byte-identical and is never auto-aliased; passenv =to enable environment-aware masking.Invalid (non-syntactic) column names are now legalised in every
cleanmode, not only"auto", and the repair is raised as a classedmasque_name_repairedwarning and recorded in the recipe. Previously, underclean = "off"an invalid name such asGY_%VARMAXwas silently rewritten bymake.names()inside numeric synthesis with no map recorded, so the synthesised column no longer matched its source: the original column survived un-masked alongside the synthetic copy – a leak – and the round-trip broke.mask()now legalises names up front in all modes, remaps therolestable, records the map, andsynthesise_numeric_local()no longer rewrites names.unmask()now restores the original (pre-legalisation) column names, so a legalised name round-trips symmetrically withapply_recipe().
masque 0.8.2
New features
- New
role_options()renders the two-axis vocabulary as data: every role and action pair, the storage kinds the pair works for, and the reason it is constrained when it is. The grid is generated from the same compatibility rulesroles_validate()enforces atmask()time, so it cannot drift from the validator.role_options(kind = "factor")filters to the combinations available for one column’s storage kind.
Minor improvements and fixes
- The Getting started vignette gains an “Editing the plan as code” section: vectorised
set_role(), direct data-frame edits withNAaction re-resolution, therole_options()grid, and hand-flaggingpii_suspectedon a sensitively valued column the name heuristic cannot see.
masque 0.8.1
Bug fixes
- The guided review’s edit option (
e) no longer destroys the session when the spreadsheet editor cannot start.utils::edit()on a data frame requires the X11 dataentry widget - missing on macOS without XQuartz and on headless systems - and its error previously propagated out ofmasque(), losing the proposal and the user’s place. The editor call is now guarded: on failure a dependency-free console editor takes over (pick a column, then a role and an action from numbered menus). Every console edit is applied throughset_role(), so vocabulary validation and default-action re-resolution match the scriptable path exactly, and the roles table’s provenance attributes survive. - The review prompt now accepts the bracketed forms it advertises (
[e]is read ase).
masque 0.8.0
Safety fix: HIGH leakage findings are never silenced (breaking behaviour)
This release corrects a safety-contract defect in the guided workflow. In 0.5.0 through 0.7.1, masque() and mask_set() wrapped their internal mask() calls in suppressWarnings(), so the HIGH-leakage warning raised by the collaborate-mode audit never reached the caller - and the guided summary could then print sharing language for output the audit had flagged. Generation must never imply release; the behaviour now matches the documentation.
-
masque()andmask_set()no longer suppress warnings frommask(). The HIGH-leakage finding is raised as a classed warning (masque_high_leakage) so callers can handle it programmatically. - The package-managed writers -
masque()’soutandwrite_set()- now refuse to write while a HIGH finding stands: nothing is written, and the flagged columns are listed with the remedy. A newallow_highargument (defaultFALSE) overrides the refusal after the custodian’s own review; the override raises amasque_high_overridewarning and is recorded in the recipe’s warnings, so the exception stays auditable. Local mode is never gated. - The guided completion summary is status-first: it reports the audit tally (HIGH / medium / low), prints a
BLOCKEDblock with the next corrective action when HIGH findings stand, and no longer prints “share only the synthetic output”.print()on a masque object shows the same tally. - The unconditional local-mode advisory is no longer a
warning(). It forced callers (and this package’s own examples) to blanket-suppress, which is exactly how the HIGH warning was lost - an alarm-fatigue defect. The reminder is recorded on the recipe and shown byprint()and the guided summary instead. Genuine findings keep the warning channel to themselves. -
mask()now errors on unused...arguments instead of silently ignoring them, so a misspelled argument (e.g.sedd = 1) can no longer look like success. - Documentation: README and the Confidentiality model vignette now describe the write gate, state plainly that
save_recipe()does not encrypt, that generating a synthetic table is not a release decision, and thatmasquecontributes evidence to Safe Data / Safe Outputs while the remaining Five Safes are organisational.
masque 0.7.1
Minor improvements and fixes
- The
masque_recipeno longer carries thedetect_design()artefact thatpropose_roles()stashes on the roles table (attr(roles, "design"), plus theproposed_actions/modeprovenance). The recipe is contractually runtime-minimal, and the design object is not part of its contract; it inflated the saved recipe and, because its serialised size varies across R versions, could pushsave_recipe()output past the documented small-file budget on R-devel. Saved recipes are now smaller and deterministic across toolchains. No public API or round-trip behaviour changes –recipe(m)@roleskeeps every role and action assignment.
masque 0.7.0
A conditional clone mode that preserves the treatment-to-outcome relationship, so a causal model fitted on the synthetic recovers the real treatment effect – not just the marginal distribution.
New features
-
mask(),mask_set(), andmasque()gain aconditionalargument (defaultFALSE). Withconditional = TRUE, the numeric block is re-simulated within each treatment-by-design stratum rather than from one global Gaussian copula, so a row’s synthetic outcome inherits the location of the treatment that row carries. The treatment-to-outcome map – the quantity a causal model reads – survives the clone, within sampling tolerance. The defaultconditional = FALSEpreserves the exact 0.6.0 behaviour (global copula, marginal and structural fidelity only), so existing scripts are byte-identical.The motivation: structural and marginal fidelity alone are not sufficient for causal inference. A clone that matches every marginal and the global covariance can still sever the relationship between an arm and its response, because the outcomes are drawn from the pooled distribution and the treatment labels are relabelled independently. A model fitted on such a clone returns a null effect. Conditional cloning is the data-side analogue of preserving a conditional mean embedding rather than a pooled marginal.
Minor improvements and fixes
- The
masque_reciperecords two new fields,conditional(logical) andconditioning_cols(the treatment and retained design columns the clone stratified on), andprint(recipe(m))now reports the clone fidelity (marginal / structural versus conditional). Recipes written by masque 0.6.0 and earlier read back with the back-compatible defaults (conditional = FALSE, no conditioning columns). - Strata too small to fit a stratum-local copula are pooled into a graceful global fallback, so a conditional clone never fails on a sparse design cell; with no treatment or design column to condition on, the path degrades cleanly to the global copula with a note.
masque 0.6.0
A two-axis roles step, a hygiene layer, column-name aliasing, a multi-table set layer, and a single guided verb turn masque into an end-to-end tool for confidential tabular data.
Breaking changes
- The roles table is now two-axis: a
rolecolumn (what a column is) and a newactioncolumn (whatmask()does to it). The role vocabulary changed – the oldkeepandignoreroles become thekeepanddropactions, and new rolesdate,id,text, andotherjoindesign/treatment/outcome/covariate. -
mask()no longer requires a column roledoutcome. - Roles tables produced by masque 0.5.0 and earlier are upgraded automatically by
roles_validate()(and therefore bymask()) with a one-time deprecation warning that preserves the old mode semantics, so existing scripts keep working. Re-runpropose_roles()to silence the warning and adopt the new schema.
The guided verb
- New
masque()is the front door: one call reads the input (a data frame, a file, a folder, an Excel workbook, or a named list), proposes column roles, pauses for review in an interactive session, masks, audits, and - given anoutpath - writes the result. It dispatches a single table throughmask()and a multi-table input throughmask_set(), and returns the same object the lower-level verbs do, so it stays fully scriptable: pass an editedrolestable to skip the prompt. (The internal S7 result class previously bound tomasqueis nowmasque_obj; the class name is unchanged.)
Two-axis roles (breaking, with an upgrade path)
- The roles table now carries two columns instead of one:
role(what a column is) andaction(whatmask()does to it).roleis one ofdesign,treatment,outcome,covariate,date,id,text,other;actionis one ofkeep,scramble,alias,drop.propose_roles()resolves a mode-appropriate default action for every column, so the table you review is the masking plan that runs. - New roles answer the three gaps the previous vocabulary left open: a first-class
daterole (date/time columns are row-permuted with class and NA pattern preserved), and the explicit “retain untouched” and “skip entirely” choices are now thekeepanddropactions, available on any column rather than only the oldkeep/ignoreroles. -
propose_roles()gains amodeargument. The proposed actions differ betweenlocalandcollaborate(for example a treatment is kept locally but aliased for collaboration); the table records the mode it was prepared for. - New
set_role()helper edits a roles table ergonomically. Re-assigning a column’s role re-resolves its default action; passing an explicitactionpins the column so a later mode change leaves it alone. Directroles$role[...] <-edits still work. -
mask()no longer requires anoutcomecolumn. With none marked, the Gaussian copula simply re-simulates every scrambled numeric column jointly. -
role = "design", action = "alias"is a new opt-out from byte-identical design preservation: the design structure is kept but the site / block labels are hidden behind opaque aliases and restored byunmask(). The default for design columns remainskeep(byte-identical). -
roles_validate()validates the (role, action, kind) combination and fails closed on impossible pairings (a scrambled design column, an aliased numeric, a scrambled id). It returns the validated table with anyNAactions resolved. - Roles tables produced by masque <= 0.5.0 (no
actioncolumn; thekeep/ignoreroles; themask_levelscolumn) are upgraded automatically with a one-time deprecation warning, preserving the v1 mode semantics exactly.
Multi-table sets
- New
mask_set()masks a whole multi-table dataset at once - a folder of CSV / TSV /.fstfiles, a multi-sheet Excel workbook, or a named list of data frames - returning one synthetic table per input table and a single private recipe bundle. -
Cross-table-consistent aliasing. A column that appears in several tables (a site code, a genotype name, a plot id) is detected as a link and aliased identically everywhere it occurs, so a join written against the synthetic set still resolves on the masked data. Links are proposed automatically and printed; override with the
linksargument, or setlinks = FALSEto mask each table independently. - New
read_set()ingests the folder / workbook / list into a named list of data frames (clean rectangles only - a missing header row or non-rectangular sheet fails with an explanatory error). Newwrite_set()writes a masked set back out, mirroring the input format (workbook in, workbook out; folder in, folder of CSVs out). The private recipe bundle is never written bywrite_set(). -
synthetic(),recipe(),apply_recipe(), andunmask()all dispatch on the set:synthetic()returns the named list of tables,recipe()the bundle, and the round-trip verbs operate table by table. -
data.tablejoins Imports (fastfread/fwrite);readxlandwritexlare Suggested for the Excel paths.
Depth controls
- New
alias_namesargument onmask()hides the column names themselves - the last identifying surface a kept or design column exposes.TRUEreplaces every retained name with an opaque alias (col_001,col_002, …); a character vector aliases just the named columns. The original-to-alias map lives in the (private) recipe and is inverted byapply_recipe()andunmask(), so a pipeline written against the aliased synthetic round-trips. This realises the long-reservedcolumn_name_maprecipe slot.reveal_maps()now also prints the column-name map.
Hygiene
- New
clean_table()verb (and acleanargument onmask(), default"auto") tidies a dirty table before masking: column names are legalised (valid, unique R names), and leading / trailing whitespace is trimmed from names and from character / factor labels. Both fixes are reported throughcli, recorded in the recipe, and re-applied byapply_recipe()so retargeting still lines up. - Near-duplicate labels - pairs that differ only in case (
"north"vs"North") or by a single edit ("Compass"vs"Compas") - are reported, never merged: deciding whether two similar labels are the same value is a judgement masque leaves to the user. Setclean = "report"to preview fixes without applying them, orclean = "off"to skip hygiene entirely.
masque 0.5.0
New feature release: joint-treatment masking plus role-table usability and first-class date handling. mask() and roles_validate() now accept designs with two or more treatment factors (factorial, split-plot trials).
Usability
- New
keeprole: users can now explicitly mark a column for byte-identical pass-through in both local and collaborate mode. This is distinct fromdesign, which remains for experimental-design structure, and fromignore, which is still dropped in collaborate mode. -
propose_roles()now proposes Date / POSIX / difftime columns ascovariaterather thanignore. Date/time covariates are row-permuted, retain their original class, and preserve the cell-level NA mask. - Unsupported column classes now default to
keepwith a clear note, avoiding a confusing attempt to synthesize objects masque does not know how to mask.
Masking and recipes
- Collaborate-mode logical covariates now receive opaque level aliases, matching the documented categorical-covariate contract.
apply_recipe()andunmask()round-trip those aliases back to logical values. -
detect_design()and its candidate proposer now honour user-suppliedkeeproles so explicitly-kept columns are not promoted into design hints.
New features
-
mask()now masks every column flaggedtreatment, not just one. Each treatment factor is aliased independently. A single treatment keeps the historicaltrt_NNNcollaborate-mode alias prefix; with two or more, the column name is folded into the prefix (<col>_trt_NNN, e.g.variety_trt_001) so the opaque labels stay distinct and self-documenting, mirroring the categorical-covariate<col>_LNNconvention. Aliasing each factor separately (rather than the treatment combination) preserves the per-factor structure that factorial models fit, and keeps each column’s alias namespace — and thereforeaudit_mask()’s leakage accounting — unchanged. -
roles_validate()no longer errors when more than one column is flaggedtreatment. The round-trip path (apply_recipe(),unmask()) already inverted multiple per-column level maps, so recovery of every treatment factor works unchanged.
masque 0.4.1
Maintenance release: contract-sharpening corrections plus the documentation and metadata that were prepared for v0.4.0 but not released. No new public exports. The two behaviour changes below are deliberate fail-closed corrections to existing exports; user code that depended on the silent failure mode will need to be updated.
Behaviour: fail-closed corrections
-
apply_recipe()andunmask()now error when a non-NA value is not present in the recipe’s level map. Previously the row was silently coerced toNA, which could quietly poison downstream model matrices. Schema drift or a new treatment level in the input now fails closed with the offending values listed. -
apply_recipe()now verifies that the NA mask oforiginalmatches the recipe’s recordedintegrity_fp. A mismatch errors with guidance. Newcheck_integrity = TRUEparameter (default) gives an escape hatch (check_integrity = FALSE) for workflows where the missingness has legitimately changed since the recipe was built.
Bug fixes
-
unmask(x, rec)now passes through atomic numeric, integer, logical, andDate/POSIXctvectors unchanged, matching the documented numeric pass-through contract. Previously these inputs errored when the recipe held no level maps. -
audit_mask()’sexact_match_pctnow divides by the number of jointly-observed comparable cells, not bynrow(df). Columns dominated by NAs no longer underreport leakage. The audit tibble gains a newcomparable_ncolumn for interpretability. -
synthesise_geospatial()now usesoriginal’s NA mask as the authority for cell-level preservation (previously usedsynth’s mask, which could let synthesised coordinates leak into rows that the original had missing). Adds anrow(synth) == nrow(original)check.
Documentation
-
roles_validate()error message for the multiple-treatment case is refreshed: drops the stale “v0.2 / deferred to v0.3” wording and guides the user to either edit the roles tibble or callpropose_roles(df, detect = FALSE)for byte-stable v0.2.x behaviour. - Stale “arrive in build-order steps 6-7” comment in
mask()’s roxygen removed. -
recipe_io.Rdoc and therecipe_anatomyvignette reword theinclude_simulator = TRUEno-op without pinning it to v0.2 / v0.3. -
roadmapvignette restructured around feature areas. The hard version pins (“v0.3”, “v0.4”) are gone — v0.3 / v0.4 shipped different features from the prior roadmap, so the pins were stale. -
getting_startedvignette: “vignette(‘roadmap’) — what’s planned for v0.3+” replaced by “features deliberately deferred from the current release”.
Test suite
- Local MET integration tests (
test-mask-end-to-end.R,test-mask-roundtrip-integration.R) callpropose_roles(df, detect = FALSE)so the suite is clean against the maintainer’s local fixtures while the multi-treatment design decision remains roadmap. - Three jitter tests that intentionally trigger the collaborate-mode HIGH-leakage warning now wrap with
expect_warning("HIGH leakage")so future warning regressions remain visible. - New tests cover: atomic numeric / integer / logical pass-through in
unmask(); fail-closed unknown-level handling inapply_recipe()andunmask();integrity_fpenforcement (positive, negative, and thecheck_integrity = FALSEescape hatch);synthesise_geospatial()NA-mask source authority and row-count check.
masque 0.4.0
Adds first-class geospatial synthesis. One new export, no breaking changes to the v0.3.0 surface.
New export
-
synthesise_geospatial(synth, original, anchor_col, lat_col, lon_col, anchor_centroids, site_spread_deg, jitter_deg, seed)— re-anchors the latitude / longitude columns in a masqued data frame at user-supplied centroids, while preserving (a) the count of distinct sites per anchor level, (b) the per-site replication distribution, and (c) within-site tight clustering with between-site spread. The original positions are never published; the function reads them only to count distinct sites. NA pattern in coordinates is preserved cell-by-cell. RNG hygiene viawithr::local_preserve_seed().Motivated by the masque release walkthrough, where state-centroid
- uniform-jitter (per-walkthrough recipe) failed to preserve the within-state clustering of real trial sites.
CRAN and r-universe readiness
- Added
cran-comments.mdfor first-submission notes. - Added
.github/workflows/R-CMD-check.yaml(r-lib standard matrix: Linux release / devel / oldrel-1, macOS release, Windows release). -
R CMD check --as-cranreports 0 errors, 0 warnings, 2 NOTEs (new-submission boilerplate and local HTML Tidy environmental).
masque 0.3.0
Adds automatic experimental-design detection and a sanity-check visualisation. New public surface: 3 exports, 1 vignette.
New exports
-
detect_design(df, roles = NULL, interactive = FALSE, threshold = 0.5, tie_delta = 0.02)— returns an S7design_summarywith the most likely design class (CRD,RCBD,IBD/alpha-lattice,row-column,split-plot,factorial, ornone), per-rule scores, evidence, and arecommended_rolestibble. Rule engine, not ML. -
design_summary— S7 class wrapping the detection result.print()is cli-styled and surfaces top-3 alternates so the user can see how confident the call was. Slots includeclass_label,treatment_col,block_cols,whole_plot_col,sub_plot_col,spatial_cols,scores,evidence,recommended_roles,candidates,warnings. -
plot_design_summary(x, df, engine = c("base", "ggplot2"))— also registered as an S7plot()method. Base-graphics sanity-check visualisation dispatched per class: replication tile, spatial layout, factor-nesting tree, treatment-frequency + NA-pattern.
Behaviour change
-
propose_roles(df)flips todetect = TRUEby default. The detected design’srecommended_rolesare overlaid on the name-based proposal, promoting structurally-identified treatments and blocks even when their column names don’t match the design / treatment regexes (e.g.,genin an alpha-lattice). Thedesign_summaryis stashed asattr(roles, "design"). Passdetect = FALSEto recover the v0.2.x byte-stable behaviour.
Design philosophy
- Detection is read-only.
mask()synthesis behaviour is unchanged. Onlypropose_roles()consumes detection output, and only as role hints. - Rule engine over ML: each of the six rules is a pure function returning a score in
[0, 1]with evidence; the orchestrator picks the top above threshold, breaking ties in favour of the simpler design (CRD < RCBD < factorial < IBD < row-column < split-plot). - Visualisation is sanity-check grade. For publication-quality field layouts use
desplot::desplot()orggplot2-based packages.
Suggests
-
agridat— canonical fixtures for tests and the new vignette. -
ggplot2— optional plot engine viaengine = "ggplot2"; base graphics is the default and the fallback.
Limitations
- The detector cannot distinguish a true split-plot from a factorial-in-blocks: both have the same data layout. The whole-plot / sub-plot assignment uses cardinality (fewer levels = whole-plot), which is heuristic.
- Detection on fewer than ~20 rows is unreliable. Pass
detect = FALSEfor toy fixtures.
masque 0.2.0
First public release of masque — a structurally faithful development surrogate for tabular datasets. Successor to the unreleased synthPR v0.1.0 (folder-scanning multi-file API), rewritten around a single-file data-frame-first interface and a round-trippable recipe object.
masque is not an anonymisation or differential-privacy tool. It produces development surrogates suitable for building and debugging pipelines, and a private recipe that re-targets a pipeline built against the synthetic clone back onto the original data. See vignette("confidentiality") for the threat model.
Design
-
Strict 5-role taxonomy for columns:
design,treatment,outcome,covariate,ignore. Multi-outcome supported. Date / POSIX columns and PII-pattern column names default toignore. -
Two modes with different safety postures:
-
local— realistic dev surrogate for the data owner. Column names and level vocabularies preserved. Treatment-level permutation is opt-in. Issues a load-time warning when the synthetic is extracted. -
collaborate— give the synthetic to a collaborator while keeping the recipe private. Treatment + categorical-covariate levels are opaque-aliased (trt_001,<col>_L01). Numeric draws are jittered within column resolution; integer columns are stochastically rounded.ignorecolumns are dropped.audit_mask()runs automatically and warns on HIGH leakage.
-
Public API (11 exports)
-
propose_roles(df)— heuristics-driven role tibble; the user edits and passes tomask(). -
roles_validate(roles, df)— fail-closed structural + semantic check. -
mask(df, roles, mode, seed, ...)— returns an S7masqueobject. -
synthetic(m)/recipe(m)— accessors that hide S7. -
apply_recipe(original, recipe)— forward translate original-namespace data into the synthetic namespace. -
unmask(x, recipe, column = NULL)— inverse on a data frame or atomic vector; round-trips a pipeline back to the original. -
save_recipe(rec, path, include_simulator = FALSE)/read_recipe(path)— runtime-minimal.rdspersistence (under 10 KB on a 17,000-row, 38-column MET fixture). -
audit_mask(m, original = NULL, print = TRUE)— first-class leakage audit returning the per-column severity tibble. -
reveal_maps(recipe)— explicit, banner-fenced unmasked-map reveal (never automatic;print(recipe)is redacted by default).
Synthesis engine
- Numeric: per-column empirical-quantile marginals + a single global Pearson copula correlation matrix sampled via Gaussian copula.
- Categorical: within-column row permutation that preserves the level set and marginal frequencies.
- NA mask: preserved cell-by-cell from the original.
- Design columns: byte-identical pass-through in both modes.
Confidentiality
- RNG hygiene throughout (
withr::with_seed/local_preserve_seed);mask()does not mutate the caller’s.Random.seed. -
recipeis runtime-minimal by default — no copula matrix or raw marginals stored. SHA-256 NA-mask fingerprint provided as an integrity check, not a privacy primitive. -
print(recipe)redacted by default;reveal_maps()is the only unmasked path. -
audit_mask()flags retained PII-pattern columns, unaliased treatments under collaborate, rare-level leakage, and numeric exact- match rates above the per-role thresholds.
Documentation
- Four vignettes:
getting_started,confidentiality,recipe_anatomy,roadmap. -
inst/extdata/john_alpha.csv— 72-row, 7-column public fixture derived fromagridat::john.alpha(John 1987, alpha design).