Verified, outcome-blind weighting in WFC 2.0

This workflow keeps study outcomes out of target selection, cell merging, and weight planning. It has two entry styles:

Both styles stop before approval. A separate human attestation is required to execute a plan. Decision makers can receive a compact report, while statisticians can receive the complete evidence tables.

Start with design-only and analysis data

The design table contains only fields with declared sampling or calibration roles. Outcomes remain in a separate table joined later by a unique ID.

library(WFC)

dims_safe <- wf_dims(
  sex = c("F", "M"),
  age = c("18-34", "35+")
)
design_frame <- data.frame(
  id = sprintf("r%02d", 1:16),
  sex = rep(c("F", "M"), 8),
  age = rep(c("18-34", "18-34", "35+", "35+"), 4),
  base_weight = 1,
  stringsAsFactors = FALSE
)
analysis_frame <- data.frame(
  id = design_frame$id,
  satisfaction = seq(40, 70, length.out = 16),
  approved = rep(c(0, 1), 8),
  stringsAsFactors = FALSE
)

design <- wf_prepare_design(
  design_frame,
  id = "id",
  calibration = c("sex", "age"),
  base_weight = "base_weight"
)
design
#> <wf_design_data>
#>  Rows: 16
#>  Calibration: sex, age
#>  Identity: dc0371546f87ed367d24cc68df79d4a54ee5b5590527a72c8545d00ff4176472

If analysis_frame or either outcome column were passed to wf_prepare_design(), construction would stop with wf_error_safety because the outcome column has no permitted design role.

Import a target with its own evidence record

WFC installs matching synthetic CSV and Excel examples:

Each data file has a distinct SHA-256, even though the tables have identical content. The DCF record names the publisher, dataset, citation, reference period, population scope, retrieval date, license, transformation, selection timing, and demo status.

csv_file <- system.file(
  "extdata", "safe-target-example.csv", package = "WFC"
)
csv_source <- paste0(csv_file, ".source.dcf")
cat(paste(readLines(csv_source, warn = FALSE), collapse = "\n"))
#> publisher: WFC synthetic example
#> dataset_title: Synthetic population target example
#> citation: WFC package synthetic example; not an external authority
#> reference_period: synthetic
#> population_scope: synthetic demonstration population
#> retrieved_at: 2026-07-15
#> license: CC0-1.0
#> checksum_algorithm: sha256
#> checksum: fcb8a35aa6a7ca0090305f0f88fb741b88d67e3409691c3e697e89bdeb5458c6
#> transformation: Generated Cartesian example with equal counts
#> selected_before_outcomes: true
#> demo_only: true

target_verified <- wf_import_target(
  csv_file,
  csv_source,
  dims_safe,
  key_map = c(sex = "sex", age = "age"),
  count = "count",
  production = FALSE
)
target_verified$identity
#> [1] "c91c501793b9200848e881aee22dcd7a0a548e7973104c1259dfcf6845590e90"

production = FALSE is required only because the bundled file is explicitly a demo. Do not use that setting to import an authoritative production source that has incomplete evidence.

Excel uses the same import form and its own companion evidence file:

xlsx_file <- system.file(
  "extdata", "safe-target-example.xlsx", package = "WFC"
)
if (requireNamespace("openxlsx", quietly = TRUE)) {
  target_from_excel <- wf_import_target(
    xlsx_file,
    paste0(xlsx_file, ".source.dcf"),
    dims_safe,
    key_map = c(sex = "sex", age = "age"),
    count = "count",
    production = FALSE
  )
  identical(target_verified$groups, target_from_excel$groups)
}
#> [1] FALSE

Use wf_target_template() to create a blank data file and companion DCF for a new source. Blank metadata and selected_before_outcomes: false deliberately block import until an accountable person completes the evidence record and updates the checksum.

Plan without calculating weights

The bundled example stops here by design: a demo_only target can demonstrate import and checksum verification but cannot enter planning. The remaining code is a runnable template after csv_file and csv_source are replaced with a completed, authoritative external source and wf_import_target() is called with its default production = TRUE. It is not evaluated during vignette build because no synthetic file may impersonate that authority.

Cell planning uses only sample support, base weights, declared category order or an explicit ladder, and the verified target. It cannot receive outcomes or a custom score. It never crosses the target boundary or widens limits.

cell_plan <- wf_plan_cells(
  design,
  target_verified,
  dims_safe,
  min_cell = 5,
  max_weight_ratio = 4
)
plan <- wf_plan_weights(
  design,
  target_verified,
  dims_safe,
  method = "raking",
  bounds = c(0.3, 3),
  min_cell = 5,
  cell_plan = cell_plan
)

plan$precheck
is.null(plan$weights)

The practitioner composition produces the same kind of reviewable plan and still computes no weights:

guided <- wf_guided_plan(
  design_frame,
  id = "id",
  calibration = c("sex", "age"),
  dims = dims_safe,
  target_file = csv_file,
  source_file = csv_source,
  source_type = "population",
  key_map = c(sex = "sex", age = "age"),
  count = "count",
  base_weight = "base_weight",
  production = FALSE
)

wf_report(guided, audience = "decision")$table
names(wf_report(guided, audience = "statistician")$sections)

Keep approval separate from execution

The next lines are intentionally not run while building this vignette. They represent two accountable actions: a human reviewer attests approval, then the approved plan is executed exactly once. Replace the illustrative identity with the actual reviewer; an automated process must not fill it in.

approval <- wf_approve_plan(
  plan,
  approver = "Reviewer name",
  role = "statistician"
)
locked <- wf_execute_plan(
  plan,
  approval,
  design,
  target_verified
)

An AI Agent can prepare and inspect the plan but cannot attest its own approval:

agent_refusal <- tryCatch(
  wf_approve_plan(
    plan,
    approver = "Agent",
    role = "assistant",
    actor_type = "agent"
  ),
  wf_error_safety = function(condition) condition$data
)
agent_refusal[c("code", "severity", "next_actions")]

There are no force, bypass, ignore-source, auto-approve, auto-relax, auto-widen, or silent method-switch flags.

Attach outcomes only after locking

After execution, attach the immutable weight by exact ID. Impact assessment computes fixed descriptive means or proportions and never calls planning or calibration code.

analysis_ready <- wf_attach_weights(
  analysis_frame,
  locked,
  id = "id",
  weight_name = ".weight"
)
impact <- wf_assess_impact(
  locked,
  analysis_frame,
  id = "id",
  outcomes = c("satisfaction", "approved")
)

wf_report(locked, audience = "decision")
wf_report(impact, audience = "statistician")
wf_audit_export(impact, "impact-audit.json")

Agent contract

Non-interactive callers should catch wf_error_safety and read its stable payload fields: code, severity, field, evidence, and next_actions. They should report the condition, preserve the identities already produced, and request the listed human action. They must not edit object fields, manufacture an approval, retry with wider limits, or select a new target after seeing study outcomes.