                      Changes in version 2.5.1.902                      

  - Bug fix (att_gt(), balanced panel): the never-treated group is now
    re-checked after the panel is balanced. Balancing drops units whole,
    so a covariate missing in one period for every never-treated unit
    removed the whole group after the check had passed; every ATT(g,t)
    was then NA — silently under faster_mode = TRUE, and reported as
    "overlap condition violated" under faster_mode = FALSE. The last
    treated cohort is now used as the comparison group (as when the data
    contain no never-treated group), with a warning, and the estimates
    equal those from the balanced sample. Under control_group =
    "notyettreated" the last cohort's pre-treatment cells are no longer
    computed in this case (it is a comparison group only).
    
      - Bug fix (att_gt(), balanced panel): a treated cohort that loses
        all of its units in balancing is now dropped with a warning;
        faster_mode = TRUE stopped with an internal error and
        faster_mode = FALSE returned NA for all of its cells.
    
      - att_gt() now warns when a (g,t) cell has no treated or control
        units (faster_mode = TRUE) or when its estimate is NaN (both
        modes) instead of setting it to NA silently, and faster_mode =
        FALSE stores an integer gname as double, as faster_mode = TRUE
        does.
    
      - Clearer errors when nothing can be estimated: no treated cohort
        left once the last cohort is used as the comparison group, no
        period left before its treatment date, a single period left, or
        a sample emptied by the missing-data drop. aggte() now says so
        when there is no post-treatment cell to aggregate.

                      Changes in version 2.5.1.901                      

  - Behavior change (att_gt(panel = FALSE)): a supplied idname must now
    be unique across all rows. panel = FALSE declares genuine repeated
    cross sections, in which every observation is a distinct sampling
    unit, so an idname that repeats (within or across periods)
    contradicts the requested data structure; it is now rejected up
    front with a message pointing at panel = TRUE /
    allow_unbalanced_panel = TRUE for data where the same units are
    observed in several periods, and at supplying an observation-level
    unique id — or omitting idname — for genuine repeated cross sections
    (a recurring household or region code can be passed through
    clustervars instead). panel = TRUE — including
    allow_unbalanced_panel = TRUE — is unaffected, and both faster_mode
    = TRUE and FALSE give the same error.
    
      - Behavior change (aggte()): min_e is now honored by type =
        "simple" and type = "group", symmetrically with max_e, which has
        always restricted them. Previously min_e was silently ignored
        for these two aggregations, so aggte(..., type = "simple", min_e
        = 1) returned the unrestricted overall effect. The defaults
        (min_e = -Inf, max_e = Inf) are unchanged, and negative values
        of min_e remain a no-op for these types because only
        post-treatment cells enter them. A min_e/max_e window that
        excludes every post-treatment cell now gives a clear error for
        type = "simple" instead of a generic "no valid estimates"
        message; for type = "group", a window that leaves some cohort
        with no cell at all (for example min_e = 1 and a cohort first
        treated in the last period) errors naming that cohort and
        pointing at na.rm = TRUE, which drops it exactly as it drops
        all-NA cohorts. The window for these two types is also now
        evaluated in the original units of tname (event time e = t - g),
        matching type = "dynamic" and the documentation; previously
        max_e counted recoded index steps here, which differed from the
        documented event time on irregularly spaced panels.
    
      - aggte() now warns that balance_e is ignored for type = "simple"
        and type = "group" (it only applies to type = "dynamic"),
        paralleling the existing warning for type = "calendar".
    
      - With only two time periods, att_gt() now reports a pointwise
        critical value when cband = TRUE, making the returned object
        self-consistent. Both pre-processors have always overridden
        cband to FALSE in this case (uniform bands coincide with
        pointwise intervals with two periods), but att_gt() kept using
        the user's cband when computing the critical value, so MP$c was
        a bootstrap sup-t value while MP$DIDparams$cband was FALSE, the
        summary was labelled "Pointwise", and aggte() inherited cband =
        FALSE. The override is now announced with a message and applied
        consistently.
    
      - att_gt(faster_mode = FALSE) now emits the same messages as
        faster_mode = TRUE when allow_unbalanced_panel = TRUE is
        resolved against the data ("You have a balanced panel. Setting
        allow_unbalanced_panel = FALSE." or "You have an unbalanced
        panel. Proceeding as such."). The slow path performed the same
        silent reset without saying so. Relatedly, faster_mode = FALSE
        now strips idname from clustervars before storing it (as the
        fast path already did), so both paths carry the same value in
        the returned DIDparams object.
    
      - Documentation corrections in ?att_gt, ?aggte, ?mboot,
        ?conditional_did_pretest, and the "Getting Started" vignette:
        the effect of listing idname in clustervars under panel = FALSE;
        allow_unbalanced_panel being ignored when panel = FALSE;
        fix_weights options being identical only for balanced panels (on
        unbalanced panels "base_period"/"first_period" also restrict the
        sample to units observed in the anchor period, with a warning);
        what pl/cores actually parallelize (the multiplier bootstrap
        only, above 2500 influence-function rows, and not on Windows)
        and where they are accepted but unused; the scope of
        min_e/max_e; the two-period cband behavior; the
        repeated-cross-sections guidance in the vignette; and the
        time-varying-weights message, which described balanced-panel
        behavior even when the unbalanced per-observation path would
        run.

                 Changes in version 2.5.1 (2026-07-08)                  

  - Bug fix (att_gt()): with control_group = "notyettreated" and no
    never-treated group, the presence of already-treated units (treated
    in or before the first period, possibly via anticipation) no longer
    affects the ATT(g,t) of the other groups. The last-treated cohort,
    which serves as the not-yet-treated comparison group in this design,
    was being deleted from the data along with the always-treated units
    — biasing the remaining estimates or turning them into NA — and is
    now retained. control_group = "nevertreated" was unaffected.
    
      - Bug fix (fix_weights = "varying" standard errors): on a balanced
        panel, fix_weights = "varying" reported standard errors
        (analytic and bootstrap, and all aggte() aggregations) that were
        exactly 2× too large. Point estimates were correct. The
        repeated-cross-section influence function is normalized over the
        2·n_units stacked observations; folding the pre- and post-period
        halves to the unit level was missing the corresponding 1/2.
        Fixed in both code paths; verified against a Monte Carlo (the
        corrected SE now matches the empirical sampling standard
        deviation, and equals the panel-estimator SE when weights are
        time-invariant). Repeated cross sections and unbalanced panels
        were unaffected.
    
      - aggte() now fails gracefully instead of with cryptic errors when
        an aggregation has an empty or all-NA selection: type =
        "calendar" with na.rm = TRUE drops calendar periods whose
        post-treatment cells are all NA (analogous to the existing type
        = "group" guard) rather than erroring; and type =
        "simple"/"dynamic" with a min_e/max_e window that excludes every
        post-treatment period now return a clear message instead of an
        internal "report this as a bug"/"non-numeric argument" error.
    
      - att_gt() now rejects a negative gname up front with a clear
        message in both faster_mode = TRUE and FALSE. Previously
        negative cohort codes (out of spec — gname must be 0 for
        never-treated or a positive treatment time) were silently
        accepted on the fast path but errored on the slow path.
    
      - The parallel multiplier bootstrap (bstrap = TRUE, pl = TRUE,
        cores > 1, when the influence function has more than 2500 rows —
        i.e. more than 2500 units, or more than 2500 clusters when
        clustered standard errors are used) is now reproducible under a
        fixed set.seed(). It previously used the default
        Mersenne-Twister RNG inside parallel::mclapply(), whose forked
        workers re-seed non-deterministically, so bootstrap standard
        errors and uniform-band critical values drifted between
        identical-seed runs. It now uses L'Ecuyer-CMRG parallel RNG
        streams (and restores the caller's RNG kind on exit). Point
        estimates were always unaffected.
    
      - Fixed a crash in the parallel bootstrap when biters < cores
        (with pl = TRUE, cores > 1, and more than 2500
        influence-function rows — units, or clusters under clustered
        inference): the per-core work split could produce a negative
        chunk size. The split is now always non-negative.
    
      - aggte(type = "calendar") now warns when min_e, max_e, or
        balance_e is supplied, since these event-study options do not
        apply to calendar-time aggregation (the unrestricted, correct
        calendar effects are returned, as before).

                 Changes in version 2.5.0 (2026-06-13)                  

This is a large release that consolidates all development since 2.3.0.
Headline changes: a substantially faster engine (group-time ATTs are
roughly 2.5-3x faster in common settings, and the conditional pre-test
is several times faster and far lighter on memory), first-class
clustered and unbalanced-panel inference, support for transformation and
factor covariates, a point-estimates-only mode, and a long list of
correctness fixes. Numerical results are unchanged up to floating-point
precision except where a bug fix is explicitly noted.

Dependencies

  - Requires DRDID (>= 1.3.0), which provides faster and more robust 2x2
    DiD estimators (used internally for every group-time ATT) and guards
    against silently-incorrect standard errors on ill-conditioned
    (near-singular) designs.

New features

  - New compute_inffunc argument in att_gt() (default TRUE). Set
    compute_inffunc = FALSE for a point-estimates-only run: it returns
    the group-time ATT point estimates (identical to a full run) without
    influence functions, standard errors, uniform bands, or the
    pre-test. This is faster and uses much less memory (no \eqn{n \times
    k} influence-function matrix is ever formed or bootstrapped), which
    helps for quick exploration or very large datasets. The result
    cannot be passed to aggte() (which now errors with a clear message);
    bstrap and cband are set to FALSE automatically.
    
      - Covariate formulas (xformla) may now use transformations and
        other model-matrix terms, e.g. ~ I(X^2), ~ log(X), ~ poly(X, 2),
        ~ X1 * X2. These previously errored because pre-processing
        stored the evaluated model frame (losing the raw variable and
        creating matrix-valued columns) instead of the raw covariates.
        Pre-processing now keeps the raw covariates so the design matrix
        can be rebuilt, and drops rows whose evaluated design is
        non-finite (e.g. log() of a non-positive value).
    
      - Factor covariates now produce exactly the same estimates,
        standard errors, and warning messages as adding their dummy
        columns by hand. Previously the faster_mode = FALSE path applied
        droplevels() within each 2x2 comparison, so a factor level
        absent from a comparison changed the design (and could error
        with "contrasts can be applied only to factors with 2 or more
        levels"). The design matrix is now built once over the full
        sample, with global factor levels, and row-subset per cell.
    
      - New fix_weights argument in att_gt() for explicit control over
        how time-varying sampling weights are resolved in each 2x2
        comparison: NULL (default, prior behavior), "varying"
        (per-observation weights via the RC estimators), "base_period"
        (fix at g-1), or "first_period". See ?att_gt. A runtime message
        points users to it when time-varying weights are detected in
        balanced panel data.
    
      - att_gt() accepts ... to forward additional arguments to a custom
        est_method function.
    
      - Added nobs() S3 methods for MP and AGGTEobj objects (number of
        unique cross-sectional units), and statistic (t-statistic) and
        p.value (pointwise, two-sided) columns to tidy() output for both
        classes, following broom conventions.
    
      - The influence-function matrix returned in MP$inffunc now carries
        the unit ids as rownames (the idname values; an internal
        observation index for repeated cross sections), and its
        row-order contract is documented in ?att_gt and ?MP. The row
        ORDER is mode-specific -- faster_mode = FALSE sorts units by id
        while faster_mode = TRUE uses an internal (period, cohort, id)
        ordering -- so external consumers of the influence functions
        (e.g. sensitivity analyses or custom cluster aggregation) must
        align rows by rowname, never by position. Values are unchanged;
        only the labels are new.

Performance and memory

  - att_gt(faster_mode = TRUE) (the default) is about 2.5-3x faster on
    common problems, with identical results. Each (g,t) cell previously
    built and extracted a data.table even though the cohort vectors are
    already materialized in the pre-computed tensors; the per-cell
    cohort is now a plain list of vectors passed straight to the DRDID
    estimators (panel, repeated cross-section, and unbalanced-panel
    paths), and the unbalanced-panel influence-function aggregation
    applies a sparse unit-aggregation operator built once per call
    (bit-identical to the previous per-cell rowsum()/data.table
    group-by, but without re-hashing the row-to-unit map for every (g,t)
    cell — about 2x faster end-to-end on large unbalanced panels).
    Earlier (g,t) work also feeds in: cumulative cohort sizes, cached
    pre-treatment periods, pre-computed cohort and period-membership
    masks on the repeated-cross-section / unbalanced-panel path, and
    sparse-triplet influence-matrix construction. One intentional
    edge-case change in the unbalanced-panel aggregation: NA/NaN entries
    in a 2x2 influence function (only reachable with a custom est_method
    that returns a finite ATT alongside a partially-NA influence
    function) now propagate to that unit's aggregated influence value,
    so the affected (g,t) standard error is NA, matching faster_mode =
    FALSE; previously the fast path silently zeroed them, understating
    the standard error.
    
      - The per-(g,t) overlap-check propensity logit (fit for every
        dr/ipw cell to detect propensity-score overlap violations) now
        uses fastglm's low-level entry point (fastglmPure) instead of
        the fastglm() wrapper, skipping its per-call input coercion and
        family/deviance bookkeeping. The fitted values -- and therefore
        the overlap decision -- are bit-identical.
    
      - Two further per-cell guard speedups, in both faster_mode paths,
        with bit-identical estimates, standard errors, influence
        functions, and warnings. (1) For intercept-only designs (xformla
        = NULL/~1, the default) the overlap and regression-feasibility
        guards use their closed forms -- an unweighted intercept-only
        logit fits every unit at mean(D), and the control-unit Gram
        matrix is the scalar control count -- skipping the per-cell
        logit fit entirely; within 1e-6 of the 0.999 cutoff the real fit
        is still used, so knife-edge decisions are unchanged. (2) For
        panel data with control_group = "nevertreated" and non-varying
        weights, the guard booleans are computed once per (group,
        covariate-period, weight-period) instead of being refit for
        every post-treatment cell of a group (the guards' inputs are
        bit-identical across those cells); failed cells still warn once
        per cell, and options(did.disable_check_cache = TRUE) restores
        the per-cell checks. Together these make a default no-covariate
        att_gt() run roughly 10-15% faster, with similar gains on
        covariate runs using never-treated controls.
    
      - att_gt(faster_mode = FALSE) builds the covariate design matrix
        once and assembles each 2x2 cell directly from precomputed
        per-period blocks (outcomes, weights, design) indexed by
        position, instead of rebuilding model.matrix() and reshaping
        (get_wide_data()) the long data for every cell. Bit-identical,
        with about half the transient allocation;
        options(did.disable_precompute = TRUE) restores the original
        per-cell assembly (the once-built design matrix is used either
        way, so the option is a debugging escape hatch for the cell
        assembly only, with identical results). The
        repeated-cross-section / unbalanced-panel slow path gets the
        same treatment: each (g,t) cell is assembled positionally from
        per-period row indices and plain column vectors precomputed once
        per call, replacing two full data.table subsets, a full-data
        %in%, and a droplevels() per cell (about 1.7x faster end-to-end
        on both the repeated-cross-section and unbalanced-panel paths,
        bit-identical, behind the same escape hatch); the fix_weights =
        "base_period"/"first_period" weight lookup likewise uses
        per-period vectors instead of a per-cell full-table subset. The
        repeated-cross-section / unbalanced influence-function
        aggregation now uses rowsum() instead of stats::aggregate()
        (about 40x faster on that step). faster_mode = TRUE and
        faster_mode = FALSE remain identical to numerical precision for
        every supported option.
    
      - Pre-processing for faster_mode = TRUE is leaner, with identical
        outputs: only the columns the call references
        (id/time/group/outcome/weights/cluster plus the raw xformla
        variables) are copied out of the input data, so wide data frames
        no longer pay a full-table copy (cutting the transient memory
        peak by roughly the size of the unused columns); the
        balanced-panel checks use an arithmetic row-count test instead
        of full by-unit groupings (the grouping now only runs when
        unbalanced units actually need to be identified); guaranteed
        no-op complete-case passes are short-circuited behind anyNA();
        the temporary asif_never_treated/treated_first_period columns
        are replaced by local vectors; and the period/crosstable count
        tables are derived without re-grouping the unit-level table.
    
      - The conditional pre-test (conditional_did_pretest()) is several
        times faster end-to-end and far lighter on memory. Its
        multiplier bootstrap (test.mboot()) previously looped over
        biters draws, each multiplying the full n x k x nX influence
        array by fresh weights -- O(n^2 k) work and an O(n^2 k)
        transient allocation per draw (over 1 GB per draw at a few
        thousand units). It is now a single tiled matrix contraction
        (100x+ faster on that step, with the per-draw gigabyte
        allocations eliminated), numerically identical to the old loop
        up to floating-point summation order; the indicator() weighting
        function is also vectorized.
    
      - Internal speedups with identical results: vectorized the
        multiplier-bootstrap post-processing (mboot()), removed a
        duplicated n x k matrix construction in the aggregation
        estimated-weight influence term (wif()), preallocated the sparse
        influence-function assembly, and removed redundant work in
        pre-processing and simulation.

Clustered and unbalanced-panel inference

  - Clustered standard errors are now available without the bootstrap.
    With clustervars set and bstrap = FALSE, att_gt() and aggte() report
    cluster-robust standard errors computed analytically from the
    cluster sums of the influence function, at every aggregation level
    (group-time, simple, group, dynamic, calendar), and the pre-test
    Wald statistic is reported under clustering.
    
      - The cluster-robust multiplier bootstrap (mboot) now follows
        Callaway & Sant'Anna (2021), Remark 10: it draws one multiplier
        per cluster and aggregates the influence function to cluster
        sums. Identical to before for equal-sized clusters; correct
        cluster-sum aggregation for unbalanced clusters and repeated
        cross sections.
    
      - Clustered inference (bootstrap and analytical) is supported for
        panel data, unbalanced panels, and repeated cross sections, and
        is identical under faster_mode = TRUE and faster_mode = FALSE.
        For repeated cross sections without an idname, the internal
        observation id is used to align the cluster identifiers with the
        influence function (idname itself is required whenever panel =
        TRUE; see below).
    
      - aggte() no longer silently ignores a clustervars request it
        cannot honor (the aggregation can only use the cluster
        information att_gt() retained). It now warns -- including when
        an override names a different variable than att_gt() clustered
        on -- and falls back to non-clustered standard errors, instead
        of silently returning the i.i.d. error or crashing in mboot().
    
      - Fixed two faster_mode = TRUE clustered-standard-error bugs on
        unbalanced panels so the fast path reproduces the slow path: (1)
        the analytical clustered SE silently fell back to the i.i.d.
        error because the stored per-unit cluster vector was
        observation-length and no longer aligned with the influence
        function; and (2) in aggte(), the estimated-weight influence
        term was added in id-sorted order while the influence function
        is in first-appearance order, misattributing it and giving a
        wrong aggregated SE (point estimates were unaffected). Balanced
        panels and repeated cross sections were unaffected.

Bug fixes

  - Fixed the conditional parallel-trends pre-test
    (conditional_did_pretest()), which had silently broken under R
    >= 4.0 and spuriously rejected almost always whenever there was more
    than one pre-treatment ATT(g,t) cell. The observed Cramér-von Mises
    statistic was left in (n_gt x nX) orientation while its bootstrap
    null distribution is (nX x n_gt), scaling the observed statistic by
    n / n_gt and driving the p-value to ~0. The root cause was
    ifelse(class(J) == "matrix", ...): class() of a matrix became
    length-2 (c("matrix","array")) in R 4.0, so ifelse() evaluated both
    branches and the no-transpose branch always won. The orientation is
    now selected with is.matrix().
    
      - aggte(type = "group", na.rm = TRUE) with a finite max_e no
        longer errors ("No valid att_gt() estimates found ...") when a
        group's only non-missing ATT(g,t) lies past max_e; the group
        filter now applies the same max_e window. The default max_e =
        Inf is unchanged.
    
      - Duplicated (idname, tname) rows (the same unit observed more
        than once in a period, a common long-format data-prep mistake)
        are now rejected with a clear error in both code paths.
        Previously only faster_mode = TRUE caught this; faster_mode =
        FALSE silently produced incorrect estimates.
    
      - A weightsname column with negative values or a non-positive mean
        is now rejected with a clear, identical error in both code
        paths, instead of silently producing NA/NaN estimates.
    
      - Fixed faster_mode = TRUE vs FALSE ATT disagreement when sampling
        weights (weightsname) vary across time: the fast path was always
        using first-period weights and now uses the same period's
        weights as the slow path.
    
      - Fixed influence-function aggregation for fix_weights = "varying"
        on balanced panels (now aggregates by unit id with rowsum()
        rather than assuming stacked order), and a length mismatch for
        fix_weights = "base_period"/"first_period" on unbalanced panels
        after weight-based unit dropping.
    
      - Fixed glance.MP() returning NULL for ngroup/ntime under
        faster_mode = TRUE.
    
      - Fixed an aggte() crash ("Error in get(gname): invalid first
        argument") when the group column is literally named gname and
        dreamerr >= 1.5.0 is installed (data.table's get() was
        intercepted; replaced with set()).
    
      - aggte() no longer modifies the data stored inside the MP object
        by reference: under faster_mode = TRUE it previously recoded the
        never-treated gname value Inf to 0 in MP$DIDparams$data as a
        side effect. The input object is now left untouched; all results
        are unchanged.
    
      - Fixed groups treated after the last observed period but within
        the anticipation window being coerced to never-treated
        (contaminating the control group with anticipation effects), and
        a data-filter inconsistency for always-treated units when
        anticipation > 0. Added an informative message clarifying that
        never-treated units are unaffected by anticipation.
    
      - When internal 2x2 estimation fails for a specific (g,t) cell
        (e.g. a singular design), att_gt() now warns and sets that
        cell's ATT to NA instead of crashing, in both faster_mode = TRUE
        and FALSE (#185, #190).
    
      - pl = TRUE on Windows now warns and falls back to sequential
        processing instead of crashing (#176).

Validation and clearer errors

  - Misspelled yname, idname, tname, gname, weightsname, or clustervars
    now produce a clear message listing the missing columns (#203).
    
      - Column names reserved for internal use by did (.w, .rowid, .G,
        .C, post, asif_never_treated, treated_first_period) are now
        rejected with a clear error when used as yname, tname, idname,
        gname, weightsname, or clustervars, or referenced in xformla.
        Previously they could silently collide with the columns did
        creates internally; rename such columns before calling att_gt().
    
      - control_group and base_period must now exactly match one of
        their documented values, in both modes. Previously faster_mode =
        TRUE accepted partial and case-insensitive abbreviations (e.g.
        control_group = "never"), and faster_mode = FALSE silently
        treated any unrecognized base_period value as "varying".
    
      - An invalid est_method (an unrecognized string or an unquoted
        variable) now errors clearly instead of silently defaulting to
        "dr" (#194).
    
      - fix_weights = "base_period"/"first_period" are blocked for
        repeated cross sections (panel = FALSE); fix_weights = "varying"
        is blocked with a custom est_method function (whose signature
        differs from the internal RC path). Both with clear messages.
    
      - anticipation must now be a non-negative number in both modes.
        Previously only faster_mode = TRUE enforced this; the
        faster_mode = FALSE path silently accepted negative values
        (shifting the base period later than the treatment period),
        which was undocumented and inconsistent across modes.
    
      - panel = TRUE (the default) without idname now errors with "Must
        provide idname when panel = TRUE. Set panel = FALSE for repeated
        cross sections." Previously this failed with a cryptic internal
        data.table error (faster_mode = TRUE) or a misleading "All
        observations dropped while converting data to balanced panel"
        message (faster_mode = FALSE).
    
      - A non-numeric outcome variable (yname) is now rejected up front
        with a clear message in both code paths (logical 0/1 outcomes
        remain allowed). Previously a character or factor outcome "ran"
        to completion with all-NA ATTs and misleading per-cell warnings,
        and a list-column outcome failed with a cryptic complete.cases()
        error.
    
      - The per-(g,t) regression-feasibility check now reports the real
        cause when it fails: "Covariate matrix for control units is
        singular or numerically ill-conditioned ... consider
        centering/rescaling covariates or removing collinear terms"
        instead of the misleading "Not enough control units ... to run
        specified regression" (which fired even with thousands of
        control units, e.g. for a quadratic in a year-scale covariate).
        The check itself is unchanged (and now uses crossprod());
        affected cells still return NA with a warning.
    
      - alp must now be a single number strictly between 0 and 1 (e.g.
        alp = 1.5 previously inverted the confidence bands silently or
        errored deep inside quantile()), and biters must be a single
        positive whole number when bstrap = TRUE (a negative value
        previously crashed inside the bootstrap's linear-algebra code
        with no hint about the cause).
    
      - Cleaner failed-cell warnings under faster_mode = FALSE: each
        failed (g,t) cell now warns exactly once with the same text as
        faster_mode = TRUE ("overlap condition violated for group g in
        time period t"). Previously the slow path warned twice per
        failed overlap/rank check -- the diagnostic plus a wrapper
        warning leaking the internal sentinel ("... : overlap. The ATT
        for this cell will be set to NA."). Genuine estimator errors are
        still surfaced by the wrapper warning. Additionally, when the
        Wald pre-test is unavailable, the warning now distinguishes
        "pre-treatment ATT(g,t) estimates exist but all have
        missing/zero variance" from "no pre-treatment cells exist at
        all" (the latter previously mis-diagnosed the former as "all
        groups are first treated early in the panel").
    
      - The documented clustervars contract -- at most one cluster
        variable beyond idname, and it must be time-invariant within
        unit -- is now enforced up front in both code paths, with one
        shared, plainly-worded error message (also used by mboot()).
        Previously faster_mode = FALSE with bstrap = FALSE (the
        analytical clustered-SE path) accepted extra cluster variables
        and silently clustered on the first one only; a time-varying
        cluster variable on that path triggered a fallback warning
        advising bstrap = TRUE, advice that then errored in mboot() for
        the very same input; and the faster_mode = TRUE error exposed
        internal argument names ("args$clustervars must be ... a
        character scalar"), contradicting the documented vector
        interface.
    
      - Per-cell empty-cell warnings under faster_mode = FALSE now name
        the period that is actually empty. Under base_period =
        "universal" (and for post-treatment cells under "varying"), the
        repeated-cross-section path warned "No units in group g in time
        period t" with the cell's current period -- a period where the
        group does have observations -- instead of the empty base
        period; faster_mode = TRUE already reported the base period
        correctly.
    
      - Aligned two pre-processing warnings across modes: the
        faster_mode = TRUE "no never-treated group" warning now also
        discloses that data from periods on/after the last cohort's
        treatment date is filtered out (both modes always dropped those
        periods; only the slow path said so), and the faster_mode =
        FALSE balanced-panel coercion warning now reports the number of
        dropped units ("k units are missing in some periods. Converting
        to balanced panel by dropping them.", same text as faster_mode =
        TRUE) instead of mislabeling the unit count as "observations"
        (an undercount of the rows actually removed).

Documentation, namespace, and internals

  - Reduced namespace pollution: replaced blanket import(stats),
    import(utils), and import(BMisc) with selective importFrom() calls.
    did no longer re-exports stats::filter/stats::lag (which previously
    masked dplyr::filter/dplyr::lag). R CMD check passes with 0
    code-related NOTEs.
    
      - Replaced fragile ifelse(cond, x <- a, x <- b) side-effect idioms
        (which relied on R's branch-evaluation order) with if/else, and
        get()/:= with set() inside data.table loops, throughout;
        behavior is unchanged.
    
      - Substantially expanded the test suite: glance(), ggdid(), error
        handling, edge cases, all aggregation types, systematic
        faster_mode consistency across dozens of parameter combinations,
        and JEL replication tests. The suite now runs with 0 warnings
        (previously 66+). Added a GitHub Action to auto-bump the dev
        version in DESCRIPTION on PR merge.
    
      - Expanded weightsname documentation (how time-varying weights are
        handled for balanced panels vs. repeated cross sections /
        unbalanced panels); grammar and typo fixes across docs,
        vignettes, and error messages; corrected the mpdta data
        documentation.
    
      - Replaced deprecated BMisc function names (getListElement,
        rhs.vars) with their snake_case equivalents (get_list_element,
        rhs_vars).

                 Changes in version 2.3.0 (2025-12-13)                  

  - Code improvements that make the package faster and more memory
    efficient
    
      - Improved automated testing and regression testing
    
      - Check if data is balanced if panel = TRUE and
        allow_unbalanced_panel = TRUE. If it is, disable
        allow_unbalanced_panel and proceed with panel data setup. This
        is different from the previous behavior, which would always
        proceed as if panel = FALSE.
    
      - Significantly reduced the number of recursive package
        dependencies, enabling faster installation times and a smaller
        build footprint.

                        Changes in version 2.2.0                        

  - Skipped, as the number of changes was large enough to warrant a
    minor version bump

                 Changes in version 2.1.2 (2022-07-20)                  

  - Added wrapper function for HonestDiD package
    
      - Fix bug for setups where gname is not contained in tname (but is
        in the tname range)
    
      - Fix bug for including too many groups with universal base period
        in pre-treatment periods
    
      - Bug fix for anticipation using notyettreated comparison group

                 Changes in version 2.1.1 (2022-01-27)                  

  - Bug fixes related to unbalanced panel and clustered standard errors
    
      - Bug fixes for conditional_did_pretest
    
      - Even faster bootstrap code (thanks Kyle Butts)
    
      - Updated version requirement for BMisc package
    
      - Bug fix for unbalanced panel and repeated cross sections in
        pre-treatment periods using universal base period

                 Changes in version 2.1.0 (2021-12-10)                  

  - Code is substantially faster/more memory efficient
    
      - Support for universal base period
    
      - Major improvements to unit testing
    
      - Completely removed mp.spatt and mp.spatt.test functions (which
        were the original names for att_gt)
    
      - Simulation/testing code now exported
    
      - Removed some slow running checks
    
      - Multiplier bootstrap code is now written in C++
    
      - Improvements to error handling, added some additional warning
        messages, removed some unnecessary warning messages
    
      - Bug fixes for NA standard errors that occur with very small
        groups

                        Changes in version 2.0.1                        

  - Improved plots
    
      - Maximum event time for event studies
    
      - Compute critical value for simultaneous confidence bands even
        when some standard error is zero (set these to NA)
    
      - Improved codes for unbalanced panel data: faster and more memory
        efficient
    
      - Correct estimates of P(G=g|Eventually treated) with unbalanced
        panel data. This affects aggte objects with unbalanced panel
        data
    
      - Bug fixes for summary aggte objects
    
      - Allow clustering for unbalanced panel data
    
      - Fixed error in calendar-type aggregation within aggte function
        (point estimates were not being weighted by group-size; now they
        are).
    
      - Additional error handling

                 Changes in version 2.0.0 (2020-12-11)                  

  - Big improvement on code base / functionality / testing
    
      - Deprecated mp.spatt function and replaced it with att_gt
        function
    
      - Calling att_gt is similar to calling mp.spatt; instead of
        formula for outcome of the form y~treat, now just pass the name
        of the outcome variable
    
      - Deprecated mp.spatt function and replaced it with
        conditional_did_pretest function
    
      - New est_method parameter. Can call any function for 2x2 DID in
        the DRDID package (default is now doubly robust estimation, but
        inverse probability weights and regression estimators are also
        supported) as well as provide custom 2x2 DID estimators
    
      - Bug fixes for including groups that are already treated in the
        first period
    
      - Allow for user to select control group -- either never treated
        or not yet treated
    
      - Add functionality for uniform confidence bands for all
        aggregated treatment effect parameters
    
      - Introduced dynamic effects in pre-treatment periods. These allow
        for users to report event study plots that are common that
        include pre-treatment periods and are common in applied work.
        The event study plots in the did package are robust to selective
        treatment timing (unlike standard regression event study plots)
    
      - Support for using repeated cross sections data instead of panel
        data is much improved
    
      - Support for using sampling weights is much improved
    
      - Big improvement to website, vignettes, and code documentation
    
      - Code for dealing with unbalanced panels
    
      - Allow for event studies to be computed over subsets of event
        times
    
      - Allow for treatment anticipation via anticipation argument

                 Changes in version 1.2.3 (2020-02-17)                  

  - Corrected check problems

                 Changes in version 1.2.2 (2019-06-21)                  

  - Improved ways to summarize aggregated treatment effect parameters
    
      - Fixed bug related to needing new version of BMisc
    
      - Fixed bug related to plotting with no pre-treatment periods
    
      - Improved ways to easily plot aggregated treatment effect
        parameters

                 Changes in version 1.2.1 (2019-06-14)                  

  - Added some error handling for some cases with small group sizes, and
    fixed some cryptic error messages
    
      - Fixes handling for data being in format besides data.frame (e.g.
        tibble)
    
      - Add warnings about small group sizes which are a very common
        source of trouble

                 Changes in version 1.2.0 (2018-10-16)                  

  - Updates for handling repeated cross sections data, both estimation
    and inference

                 Changes in version 1.1.2 (2018-09-11)                  

  - bug fixes for testing without covariates, allowed to pass NULL in
    addition to ~1

                        Changes in version 1.1.1                        

  - fixed issues between BMisc and formula.tools
    
      - added point estimates for repeated cross sections data

                 Changes in version 1.1.0 (2018-07-11)                  

  - bug fixes for the case without any covariates

                 Changes in version 1.0.0 (2018-04-03)                  

  - first version of package, functions for computing group-time average
    treatment effects, combining them into a smaller number of
    parameters, and pre-testing the common trends assumption
