# Architecture invariants These are stable constraints and standing decisions that are easy to violate across module boundaries and are not fully recoverable from any one implementation. The current architecture remains provisional, and none of these is permanent: each holds until its stated **reconsider when** condition is met, and a change to one must be intentional, documented, and backed by evidence. When a condition is met, update the implementation, its independent evidence, and this document in the same change. Superseded rationale belongs in Git history. ## Validation and evidence Validate each derivation independently. A comparison against a canonical implementation is acceptance evidence for the numerical and repeated-sampling claims it covers. The evidence pages mark a study that names a reference as comparator-backed. Each such study declares its comparison kind in the study registry. A comparison does not replace an independent check of a changed term. Implementations descended from one source share transcription errors. Derivative, exact-law, remainder, mutation, and score checks fail against distinct error classes. So each changed mathematical term keeps its independent check, whether or not a comparison covers it. The [evidence manifest](technical-reference/evidence.md) records which instrument covers which estimand, in both directions. The `LTMLE` fixture is an example of a comparison that reaches a blind spot. It pins cumulative-bound placement and a nonzero finite-sample targeting path that exact laws at `epsilon=0` cannot see. *Reconsider when* the project owner changes the acceptance policy in [Theory and comparators](#theory-and-comparators). Keep feature selection separate from statistical certification. Evaluating a configuration on the draws that selected it makes the reported result selection-dependent, so a study that selects must certify on draws that did not do the selecting. *Reconsider when* a study performs no data-dependent selection, or uses disjoint selection and certification cohorts. ### Theory and comparators This rule is the repository standard for every method, extension, validation study, and release decision. Other documents link here. Assess theory and comparator availability separately. | question | rule | | --- | --- | | No comparator exists, exact or related | This does not block implementation, study registration, or release. Use established theory or a documented natural extension, and validate each supported claim independently | | A comparator exists | Use it where it is informative. Its agreement is acceptance evidence for the claims it covers. Label each such claim as comparator-backed, and state what the comparison establishes and what differences remain. Each changed term keeps its independent check | | Fundamental theory is missing | This blocks the affected claim when the gap needs a new identification, regularity, or asymptotic argument. Scope the block to that method, configuration, or inference claim | | A comparator and missing theory | A comparator cannot replace missing fundamental theory. The absence of a comparator does not create a theoretical gap | | A planned comparator study has not run | This does not block a merge or a release when the method's Python studies pass with their expected properties. Label the evidence "comparator runs pending" beside its property evidence. When the comparator runs, register its results and act on its findings | A study without a comparator records that absence and names the independent evidence that replaces it. Do not invent a reference implementation, and do not claim parity to fill an evidence table. The independent evidence still checks the target, each changed mathematical term, and each claimed statistical property. The [roadmap's eligibility rules](roadmap.md#eligibility) apply this standard to method entry. *Reconsider when* the project owner changes the acceptance policy. ### Option defaults keep the shipped code path A targeting or fitting option defaults to the value that runs the code that existed before the option. The solver tests the option at its top, and the `else` branch holds the shipped body unchanged. `stopping="relative"`, `step_bound=None`, `iterate="last"`, `outcome_by_arm=False`, `treatment_adjustment=None` and `patience=None` all follow this rule. A default must not call a new function that gives the same answer. A rounding difference in the last place would move recorded results. Prove a default with the A/B replay. 1. Set `OMP_NUM_THREADS=1 OPENBLAS_NUM_THREADS=1 MKL_NUM_THREADS=1`. 2. Run `python -m tests.canonical.replay_committed replay --all --replicates 20 --jobs 4 --write ` at the base commit. 3. Run the same command at the branch. 4. Run `compare --rtol 0`. `compare` exits 2 when both sides name one source tree, and it exits 1 on any difference. The committed rows are not the gate, because they drift across platforms in the last bits. `tests/unit/test_replay_committed.py` tests the tool. Plumbing tests such as `tests/unit/test_stopping_rule.py::TestPlumbing` assert that a written-out default gives the same fit as an omitted option. *Reconsider when* a change moves a default on purpose. Then regenerate the studies it moves, in the same change. ### A declared stopping rule changes what the solver and the score check read A rule other than `"relative"` needs a stop context. The context holds the unit of each row and the outcome range. `TMLE._retarget_detailed` builds it once, so every caller of the targeting step gets it. A solver that receives a declared rule and no context raises `ValueError` at once. It never invents a threshold. A fold solve and a C-TMLE candidate solve pass `stop.restrict(rows)`, so $n_u$ counts the units of the rows each solve receives. A rule ratio is valid only at the pair of fit and score where it was computed. An outer loop that moves the regression or the mechanism after the outcome solve must restate the ratio at the pair it judges, through `rule_ratio_at`. It must not read the ratio the solver stored. `tests/unit/test_stopping_rule_engines.py` recomputes the ratio by hand at every round. `score_check` reads the declared rule. Under a rule other than `"relative"`, a fluctuation row is held to `Fluctuation.thresholds`, and an estimand row is a `"diagnostic"` row that cannot fail. The diagnostic row reports the residual in standard errors and the coverage a Wald interval keeps at it. The default rule keeps its rows and its bar. *Reconsider when* a stopping rule gets a derivation that bounds the residual of a contrast. Then the estimand rows can carry that bound as a bar. ### Comparator fixtures ship pending A cloud session cannot run R. So an R comparator ships with the change in an explicit pending state. `tests/canonical/option_fixtures/registry.py` holds one entry for each fixture with its script hash and its state. A pending entry has no JSON file, a stated reason and a matching hash. A present entry has a JSON file with the same hash, and its parity test runs. A self-test writes a payload in the comparator's schema, requires the comparison to pass, and requires each mutated copy to fail. A reader-facing table row that names a pending fixture carries the label "exact tests, with comparator runs pending". `tests/unit/test_option_fixtures.py` ties the label to the registry. A row that carries the label while its fixture is present fails, and so does an unlabelled row of a pending fixture. This applies the rule of [Theory and comparators](#theory-and-comparators): the missing comparator blocks nothing, and the label states the gap. *Reconsider when* the local Study queue has run the images and every entry is present. ## Dataframes and labels User-facing frames go through narwhals and results return in the caller's dataframe library. Keep backend names, not whole input frames, in data and report objects so results remain serializable and do not pin input memory. Cast numeric roles through narwhals before conversion; preserve the treatment's logical type until treatment encoding. Detect null labels through the logical column type, never by branching on pandas or polars. Per-arm arrays are keyed by treatment level. Use shared arm-mapping helpers and carry original arm labels into parameter names, tables, and errors. Do not parse user-visible names to recover arms; compose the known names forward and retain a structured index. The binary path is a bit-for-bit regression surface for generalized treatment support. A multi-arm change must leave binary results unchanged unless a documented compatibility change is intended. A `K`-level arm enters a design matrix as `K - 1` drop-first indicators, through the shared `data.validate.arm_indicators`. At `K = 2` that rule is the single 0/1 code column itself, which is what delivers the bit-for-bit guarantee above rather than a separate compatibility branch. Both `CausalData.treatment_block` and `LongitudinalData.history_design` call it, so a design that conditions on an arm is coded the same way wherever it is built, and a longitudinal node's block is sized by *that node's* level count rather than by a panel-wide one. One ordinal column is not an acceptable simplification: it constrains any learner linear in its design to an ordered response in the arm, which is a restriction on `Q` or on `g` that the estimand does not ask for. *Reconsider when* an estimand is added whose treatment is genuinely ordered and whose derivation uses that ordering. An ordinal coding would then be a modelling choice to declare, not a default to inherit. Note that no exact-law test can see this choice, since a saturated learner partitions by distinct design row and the two encodings are a bijection; the witness is `tests/unit/test_sequential_design.py::TestAThreeLevelArmEntersAsIndicators`, a `glm` mechanism on a non-monotone truth. Internal tabular arithmetic stays in NumPy. The dataframe boundary is a negligible share of a fit, supported learners consume NumPy arrays, and the public dataframe contract is already isolated through narwhals, so a columnar engine has no share here to win. *Reconsider when* a supported workload becomes dominated by joins, grouping, IO, or conversion rather than estimation. ## Public causal workflow The beginner-facing computational path is `CausalStudy -> identify -> estimate`. `StudyProtocol` owns the study context and assumption rationale. A design owns observed column roles. A typed estimand owns the mathematical contrast and intervention metadata. An `IdentifiedEffect` owns the observed-data functional and assumptions. A typed method owns learning and runtime configuration. The protocol can describe strategies in study language, but it cannot override another object's contract. Do not reintroduce root engine constructors or a parallel string-driven convenience path. One public question must normalize to one evidenced engine request. *Reconsider when* a distinct audience has a workflow that cannot use these contracts without losing information. The alternative must still converge to the same structured identification and result records. An estimation method is named, never selected from the data. `estimate(method=...)` carries a fixed default preset, which is a declaration rather than a choice. What is excluded is picking an estimator by scanning `available_methods()`, or by comparing fits on the rows being estimated. The temptation grows with the catalog. `riesz_tmle` and `ep` already appear there as unavailable-with-reason, so a "use the best available method" convenience is one short function away. It would report an interval whose post-selection coverage nothing certified and for which no selection contribution, common-influence-curve reduction, or stable-oracle reduction had been derived. *Reconsider when* a published selector supplies selection-aware inference and certifies on draws that did not do the selecting. Identification is complete before nuisance fitting. Unsupported estimand/design/provider/method combinations fail at that boundary with a capability reason; a placeholder may not produce an estimate for graph, Riesz, EP, front-door, IV, mediation, or transport behavior that has not been implemented and evidenced. *Reconsider when* the corresponding work package supplies its functional, method artifacts, persistence, and independent evidence. A design and a prepared data container must agree on every role before either is used. Both `CausalStudy` and the containers accept the same roles, and neither module alone can see a disagreement: the container holds arrays that no longer name their source, while the design is what `IdentifiedEffect.functional` records, `summary()` prints, and persistence writes. Adopting a container without reconciling it therefore reports an adjustment set no estimate came from, and saves it. *Reconsider when* a container carries its own identification record, so the design has nothing left to contradict. Reported inference always belongs to the reported subset. [Reporting a subset of a family](technical-reference/inference.md#reporting-a-subset-of-a-family) states the rule and what is recomputed. The decision recorded here is that the public layer recomputes rather than reusing the wider family's critical value. *Reconsider when* an engine can be asked for the narrowed family directly, and the public layer stops selecting after the fact. A point-treatment estimator stamps the inference status in `TMLE._retarget_detailed`, from its `_inference_status(data)` hook. The hook reads the estimator configuration and the prepared `CausalData`, and nothing fitted. The status can be determined before learner runs, though `_retarget_detailed` stamps estimates after nuisance fitting. `fit`, `retarget`, and each sensitivity sweep get their estimates from that method, so no sweep builds an interval that the fit refuses. The same method stamps both fold-level reports, and `CVTargeting.inference` reads the status from those reports rather than store it. The longitudinal estimator decides its fit status in `_inference_status(data, folds)`, in `cleverly.longitudinal.estimator`. That function calls `cluster_inference_status` on the prepared cluster labels and weights, and it reads nothing fitted. `LTMLE.fit` and the truncation-curve replay `_refit_bound` pass the fit status to `_estimates` and `_msm_estimates`. Those two builders stamp it on each estimate. The replay must equal the fit field for field, so a stamp in `fit` alone would make `truncation_curve()` refuse. A parameter status sits beside the fit status. `PARAMETER_STATUSES` in `src/cleverly/_inference_status.py` names the statuses that one parameter takes, and today it holds `constant_node_plugin` only. `_estimates` stamps it through `_parameter_status`, in the fit and in `_refit_bound` alike. `_parameter_status` reads one fitted quantity, `constant_nodes(fit)`: the nodes whose regression read a constant 0 or 1 over the whole sample. A level takes the status when its own fit has such a node, and a contrast when its fit or its reference fit has one. A fit status that withholds inference comes first in `STATUS_RECORDS`, so it stays. | path | status it sets | | --- | --- | | `smooth_contrast` | the status of its inputs, through `inference_status`. A mix of two fit statuses raises `ValueError`. A mix of a fit status and a parameter status takes the earlier one in `STATUS_RECORDS` | | `median_estimates` | the status of its repeats. Any mix raises `ValueError`, a parameter status included, because a median would report one draw's refusal under another draw's name | | `variable_importance` | none. It raises the fold-policy refusal first. On each candidate's prepared data it raises the outcome-scale refusal, then asks the hook. It refuses before the first fit. A cross-fitted run of a continuous outcome with no `q_bounds` meets the scale refusal first in two cases. With `delta=`, its fit would raise the arm-indexed refusal first. With a `CTMLE` template, the hook would give its collaborative status | | a longitudinal estimator | `cluster_inference_status` on the prepared cluster labels and weights. The fit and the truncation-curve replay pass it through `_estimates` and `_msm_estimates` to each `make_estimate` call. The replay computes the status again from the data and folds of the result, so the replay at the fitted bound equals the fit in every field that `_fitted_replay_matches` compares | | a longitudinal parameter | `_parameter_status` in `_estimates`: `constant_node_plugin` when `constant_nodes` of its fit, or of its reference fit for a contrast, is not empty. `_msm_estimates` never meets it, because `msm=` refuses a constant cell in the backward pass | | `incidence_total()` | each total's own status, through `inference_status` over the incidences it sums | | a band | none. `LTMLE._bands` and `CausalStudy`'s `_narrow_bands` build the band over the estimates that supply inference, and below two of them they build none | The same stamp sets the Student $t$ reference of a clustered fit. A builder records the rows an estimate reads, and not its degrees of freedom: `_stratum_estimates` returns the stratum code of each stratum estimate. `stamp_inference` turns that record into `reference_df`, and it does so only when the fit's status supplies inference. A fit at `"no_cluster_variance_plugin"` therefore keeps `reference_df=None` on every estimate, and no stratum count below 3 reaches `cluster_reference_df`. A fit at `"few_cluster_plugin"` shows its interval, so each estimate takes the degrees of freedom of its own rows. A derived estimate takes the smallest `reference_df` of its inputs at its own `make_estimate` call. Every interval, p-value and sensitivity limit reads the reference through `wald_ci`, `wald_statistic`, `reference_quantile` or `one_sided_quantile` in `cleverly.inference.delta`, and `tests/unit/test_few_cluster_reference.py` scans the package for a second normal quantile. A future fold-evaluated report with baseline strata must carry each stratum's row set into `_average_over_folds`, or its stratum estimates read the fit's cluster count. *Reconsider when* a source gives a reference that is not a function of the cluster count. One fit has one fit status. When more than one non-inferential fit status applies, the fit takes the first one in `STATUS_RECORDS` (`src/cleverly/_inference_status.py`). An override that finds more than one status passes them to `precedent_status`, so the order lives in the table. A parameter status can sit beside it on some estimates. The result's `inference_status`, through `reported_status`, is the fit status: it leaves parameter statuses out unless every estimate holds one. A reader of `std_error`, `ci`, or `pvalue` must branch on each estimate's `supplies_inference`. The `inference_status` of a result is the fit status, so it alone does not say that every estimate supplies inference. A reader that decides columns from it must also handle the estimates that do not: `summary()` stars them, `curve()` and `incidence_total()` add an `inference` column and the plug-in columns, and `to_frame()` holds the union of the columns. A frame or label that publishes a spread must take its names from `spread_columns()` or `spread_name`, which raises `KeyError` for an inferential name with no diagnostic entry. A text that names a status must read it from `STATUS_RECORDS`. The estimator that stamps a parameter status names it through `CONSTANT_NODE_STATUS`. [Inference status](technical-reference/inference.md#inference-status) gives the public contract. *Reconsider when* [F18](roadmap.md#f18-selector-path-c-tmle-inference) supplies the selector paths' influence curve, when a second parameter status is added, or when [X54](roadmap.md#x54-small-sample-evidence-for-clustered-intervals-and-boundary-nodes) part (i) supplies an interval at a boundary node, which would remove the one status that reads a fitted quantity. Where a configuration group serves more than one engine, a default that differs between them is a sentinel resolved per engine, never a literal that silently picks one engine's answer for the other. `g_bounds="auto"` and `n_multiplier="auto"` are the two current cases. A restated engine default is pinned against that engine's signature by a test, because a restatement that nothing compares is free to drift. *Reconsider when* the engines agree, at which point the sentinel should be deleted rather than kept. Configuration groups are immutable and normalized before engine construction. Convenience keywords may map into `ModelSpec`, `CrossFitting`, `Targeting`, `Inference`, and `Runtime`, but may not bypass them or reassign design roles. A shortcut whose name is a configuration field sets that field; in particular `alpha` is the interval significance level and `submodel_alpha` is the logistic-submodel bound. A supplied `SplitPlan` fixes outer validation assignments by input row position, and a plan read off a result binds to that fit's data fingerprint. It does not fix inner learner or collaborative-selection folds. Validate every repeat against that fingerprint before nuisance fitting, including cluster integrity. Results derive the public plan from retained folds, and provenance fingerprints those same assignments. The full-refit bootstrap, longitudinal fits, and any refutation that changes the row set refuse a supplied plan until they define their own row-mapping and validation contracts. A fit accepts a supplied plan only when the plan records how `random_partition` drew each repeat. Draw each repeat again from that record before nuisance fitting, and refuse labels the record does not produce. Labels no recorded draw produces could have been chosen by reading the outcome, which is the leak cross-fitting prevents. A result carries the record of every retained draw, and a refit keeps the record and drops the fingerprint binding. `SplitPlan` stays constructible from labels alone, and every layer that accepts a plan refuses one with no record. *Reconsider when* another generator records enough identity to be drawn again from its record. An outer split reads the row count, the cluster labels and a seed, and it reads no treatment, outcome or covariate value. No fit draws a partition that balances the data it then conditions on. The declaration layer refuses a balancing policy before any split exists, and the fit layer refuses it again, because a copied or modified estimator can carry a policy that the package does not draw under. A selector-based collaborative fit draws selection folds without cross-fitting, so the refusal reaches it at every setting. Outcome-adaptive C-TMLE draws no selection folds, so its in-sample fit accepts an unused policy. *Reconsider when* a reviewed result covers a partition read off the analysed data for a shipped estimator. A cross-fitted fit of a continuous outcome works on a scale the caller declared, and never on one the sample supplied. Refuse `q_bounds=None` before nuisance fitting, in every engine that cross-fits. No package or study code derives an outcome support from a realized sample. The package checks only that a declared interval contains the observed outcomes. *Reconsider when* a reviewed result covers a cross-fitted transform whose endpoints the held-out rows helped choose. What stratification used to guarantee is now checked on the realized draw, before the first learner. Each treatment arm, and each class of a binary outcome, must reach two independent units. Each training complement must carry every arm, a row whose outcome was observed, and every class a learner's inner split needs. The independent unit is the row, and it is the cluster where `id=` declares one. A refusal raised after a draw names no redraw, no seed and no fold count, because a split that happens to fit was chosen by reading the values the draw must not read. *Reconsider when* a split law that reads the treatment acquires a reviewed result. A composition the package cannot support is refused by name before it costs a learner. Collaborative TMLE refuses declared clusters at every setting, because no reviewed result covers a grouped draw of its selection splits. *Reconsider when* a cluster-level result covers the selection path. Every outer split of a clustered fit, point treatment or longitudinal, is whole-cluster. `make_folds` checks it for point treatment, and `LTMLE.fit` checks it with `check_integrity` after the draw. An MSM projection weight, an MSM design, and a `Stochastic` density are user-supplied functions that the reported influence curve treats as fixed. Each carries a declaration with `"known"`, `"estimated"`, or `None`, and only `"known"` fits. A callable can close over an estimate, and code cannot inspect that closure. `MSM.linear` leaves `design_kind` as `None`, because the design it builds is known by construction. An MSM with no design declaration reads as `"known"` only when its design has the exact type that `MSM.linear` builds. The MSM and `Stochastic` objects refuse undeclared and estimated functions at construction. Their estimator paths check again before a fit or a result recomputation, because a copied or modified object can carry an invalid declaration. The `TMLE` fit checks before each refusal of the fit configuration, because no remedy that those refusals name lets an undeclared function fit. The public evaluators `MSMSet.evaluate`, `evaluate_regimen_msm`, `RegimeSet.evaluate`, and `Stochastic.density` check before they run a user function. The simulated-confounding replay evaluates through them. Its replay model reads each MSM declaration from the source model, under the same rules as the fit. A treatment rule and the density of a user-written `Intervention` carry the same three-state declaration. The table gives where each one refuses `None` and `"estimated"`. Each refusal comes before any learner and before the function runs. | function | declaration | refused at | checked again by | | --- | --- | --- | --- | | `Rule.rule` | `rule_kind` | construction, and the `TMLE` fit | each retarget, `Rule.density`, and `RegimeSet.evaluate` | | the callable nodes of a `DynamicRegimen` | one `rule_kind` for the plan | construction, and `LTMLE.fit` on the raw `regimens=` | `DynamicRegimen.assignment` and `longitudinal_truncation_curve` | | the `Stochastic` nodes of a `DynamicRegimen` | each node's `density_kind` | construction, and `LTMLE.fit` on the raw `regimens=` | `resolve_plans` and `longitudinal_truncation_curve` | | `density` of a user-written `Intervention` | a `density_kind` attribute, `None` when absent | the `TMLE` fit | each retarget and `RegimeSet.evaluate` | A plan of labels alone needs no declaration. A callable written inline in `regimens=` carries none, so `LTMLE.fit` refuses it. `cleverly._declarations.FunctionDeclaration` shares the check and the refusal texts of every declaration. `Rule` and `DynamicRegimen` share one rule declaration. The simulated-confounding replay freezes each regime with the `density_kind` of its source, and never with a literal `"known"`. A longitudinal MSM keeps evaluated arrays and a `functions_kind` marker. Its evaluator writes `"known"` only after the source design and weight declarations pass. A projection built by hand has no marker, and it refuses truncation replay. [RM28](https://github.com/esbraun/cleverly-tmle/blob/4ce96cda2bda93ba9233026977e3ff63ea3e0003/docs/roadmap.md#rm28-declared-densities-of-user-written-interventions) records the rule. A learned rule is not a declared rule. `TMLE(learned_rule=LearnedRule())` and `LearnedRuleValue` estimate its fold-average value on a parameter axis of their own, `"learned_rule"`, which every axis-keyed consumer decides explicitly. The table gives the rules the fit keeps. | rule | how the fit keeps it | | --- | --- | | the rule of a row comes from a fit that never saw the row | the rule reads the out-of-fold outcome regression of the row's fold | | one pooled fluctuation | `cv_evaluation=True` with `targeting_scheme="pooled"`; every other scheme refuses before any learner | | the estimate is the `1/V` average of the fold plug-ins | the fold-evaluated report, with each fold's curve centred at its own estimate | | no row is refitted | `refute`, the full-refit bootstrap and `repeats` above 1 refuse, because a refit relearns the rules. Every sensitivity analysis refuses too, because no derivation for this target was reviewed | [Learned rules](technical-reference/point-treatment-tmle.md#learned-rules) states the contract, and [RM30](https://github.com/esbraun/cleverly-tmle/blob/4ce96cda2bda93ba9233026977e3ff63ea3e0003/docs/roadmap.md#rm30-learned-policy-value-evaluation) records its delivery. *Reconsider when* the package adds another learned-policy target ([X11](roadmap.md#x11-learned-policy-follow-ups)) or a population-law-dependent intervention function. A normalized method declaration either changes the selected engine request or fails before that engine is constructed. Shared configuration groups do not imply shared implementation: every non-default point-only setting is refused on a longitudinal design, while supported semantic translations such as `cross_fit=False` to `n_folds=1` remain explicit. Method-configuration failures derive from `CleverlyError`, so callers never have to catch implementation-language `TypeError` or `ValueError` separately. *Reconsider when* an independently evidenced longitudinal derivation makes a currently point-only setting operational. Every causal result carries its `IdentifiedEffect`, normalized method, and structured `ParameterKey` mapping. Persistence round-trips the complete result graph with cloudpickle inside a joblib-readable gzip stream, including the fitted arrays, estimator configuration, analysis data, nuisance-estimator templates, and the rules and policy densities of each regimen. Importable objects are saved by reference. Lambdas, closures, and objects defined in `__main__` are saved by value, and they load only under the Python minor version that saved them. Loading therefore has pickle's arbitrary-code-execution risk and is restricted to trusted artifacts in compatible dependency environments. *Reconsider when* a safe, estimator-agnostic format can represent arbitrary third-party sklearn-compatible models without weakening replay. Only the version that wrote a saved artifact supports it. `result.save()` writes one gzip stream that holds a header pickle and then the result pickle. The header records `cleverly.__version__`. On a different version, `cleverly.load()` reads the header and warns with `VersionMismatchWarning` before it unpickles the result. It then loads the artifact as saved, and it runs no migration, backfill, or re-stamp. The load reads the stream to its end, so gzip checks the CRC and the length. The load refuses a stream with data after the result. `result.save()` writes a temporary file beside the destination and renames it, so a failed save keeps an earlier artifact at that path. Development snapshots between two releases share one version string, so the check does not see a change between them. `cleverly._saved_version` holds the rule, and `tests/unit/test_serialization.py` checks it. *Reconsider when* the project leaves alpha. A saved object round-trips the records it holds, and never a value it derived from them. Both `TMLEResult` and each assessment facade drop every memoized value before joblib writes the artifact. The shared filter `without_memos` reads the `cached_property` descriptors on the owning class, and each `__getstate__` applies it. A stored memo records a conclusion and not the question that produced it, so no reader can check the memo after a load. *Reconsider when* a derived value costs more to recompute than a load may spend. Scalar result algebra is composed once in `inference.results`: sole-estimate selection, ordered name validation, influence-curve extraction, joint covariance, and smooth delta-method contrasts. Point and longitudinal result types delegate those operations and retain only method-specific artifacts and reports. Scientific formulas that differ by method stay separate; identical result algebra must not be copied into another result class. Assessment is routed by declared fitted artifacts, not result-class names or parsed parameter aliases. Every public result family has an explicit fit-wide supported, `deferred`, `not_applicable`, or `unavailable` answer for every public diagnostic. A `deferred` capability reports `available: False` and names the argument that makes the operation run. A cost opt-in stays request state, because it changes no capability answer. Sensitivity selects parameters through `ParameterKey`. `validate()` summarizes only stored state and never refits, while refutation and benchmarking are explicit expensive operations. Assessment caching is keyed by operation plus normalized arguments, is persisted separately from estimates, and may not mutate the headline estimate or its summary. A facade drops its memoized capability verdicts before it enters an artifact. The persistence invariant above states that rule for every saved object. *Reconsider when* an assessment needs stochastic state that cannot be normalized or serialized; that operation must then declare itself non-deterministic from a saved result rather than entering the persistent cache silently. Both assessment facades route through one base. Lookup, refusal, and the combined report are written once. A refusal therefore always carries the reason its own capability row declares, and a combined report reads that declaration the same way on both facades. Sensitivity implementations are reached through `SENSITIVITY_ROUTES`, which also declares whether the target takes an estimand as its second positional argument; that table and the declared capabilities are checked against each other in both directions. Whether an operation takes an estimand at all is read from the routed signature, not from that flag, because an operation can take the same ambiguous default by keyword. A facade may not fill in an estimand a fit leaves ambiguous: substitution is for the case where exactly one reported parameter fits, and otherwise the analysis refuses by name. `run_all` sorts each included capability row into one of three execution classes. A `summarize` row reads stored state and always runs. A `refit` row refits nuisances and runs only under `include_refits`. A `retarget` row retargets cached nuisances and runs under `include_retargets`, unless its own row declares `cost` as `cheap`. A cheap retarget runs by default, which is how an eligible ordinary TMLE fit reports its derived E-value without refitting a nuisance model. The two flags stay separate because the two costs are disjoint. Refutation and benchmarking refit nuisances. Point-treatment truncation curves usually retarget cached nuisances. Longitudinal truncation curves refit the complete backward recursion while holding the mechanism predictions fixed. One flag made whichever class it did not name run under the other's permission. A row's execution class is a fact about the fitted result, not only about the operation. `assessment_capabilities` resolves it per result. Guarded DR-TMLE refits the reduced regressions inside its targeting alternation. Longitudinal TMLE refits every bound-dependent outcome and pseudo-outcome regression and every targeting update. Both truncation curves therefore belong in the `refit` class. A family still declares each operation exactly once, and the contract test enforces that. A longitudinal fit builds its truncation replay recipe before the first backward pass. The recipe stores resolved plans, unfitted outcome and pseudo-outcome learner clones, and recursive solver settings. It stores no fitted outcome model. The fitted result supplies the realized folds, scaler, raw mechanism predictions, data, weights, clusters, and parameter structure. Replay availability requires cloneable learners, and an explicit integer `random_state` wherever a learner or a nested library member declares that parameter. [Replay-only unavailability](technical-reference/scope-and-refusals.md#replay-only-unavailability) states that rule and lists every omission code. Every requested longitudinal grid starts with an exact fitted-bound replay. It compares all retained estimates, regimen fits, and MSM fits, even when the grid omits the fitted pair. `run_all` applies its gates in one order: caller deferral, then availability, then required arguments, then cost. Every gate above the cost gate refuses for a reason no flag pays off. A report that named the cost first told the caller to pass `include_refits=True` for a row that also needs explicit `covariates`. A combined run injects its top-level seed before those gates, not after them. A deferred row is a request the caller can replay. A non-deterministic operation replays only with the seed the run would have used. A row this fit refuses outright records no arguments, and that is the one exception. It describes no invocation, so a seed on it names a draw that nothing ever took. A row that the request refuses differs from the row that the fit alone gives. It records the request's arguments with the signature defaults bound. It carries no next step, because its sentence names the refused value. A missing required argument or cost opt-in is `deferred`, because the caller can make the operation run. A missing method, derivation, replay artifact, or supported requested variant is `unavailable`. An invoked operation that raises a capability refusal is also unavailable. The deferral gate reads the capability status, not the supplied argument names. An argument that is present with the value `None` defers the same operation as an absent argument. The E-value defers on `estimand=None`, which is its public default. On the four rows of the request rule below, a value that the call refuses reads `unavailable`. An ambiguous default estimand is a deferral on both facades. Several operations default to `estimand="ate"` and answer for one parameter. A fit that reports several eligible parameters and no bare `ate` leaves that choice to the caller. A fit that reports no eligible parameter stays `unavailable`, because no argument makes a missing derivation run. One eligible parameter is the case each facade answers for itself. A facade that substitutes that name runs the row under it. A facade that substitutes nothing defers the row, because the operation would otherwise run on the ambiguous default and refuse. One predicate decides that ambiguity. Both the request-level capability resolution and the facade's own parameter substitution read it. Written twice, the two disagreed: the substitution declined to guess between two contrasts while the row beside it still advertised the analysis as runnable. The combined report then invoked the operation and published the refusal as `unavailable`, under a next step that named no argument. A row whose call refuses some values of one argument resolves each request from the predicate that the call raises from. The facade's `_gated_map` holds each row after the method and replay gates, before any request. Its `_request_gated` then resolves one request on top of that map, through the gate method that the class table `_request_gates` names for the operation. The bare row, a combined report, and a direct call each resolve their own arguments. The bare row and a combined report apply the estimand gate first. A direct call does not, because it raises its own refusal of the default estimand. | request | row | | --- | --- | | the argument is omitted, and the default runs | unchanged | | the argument is omitted, the default is refused, and another value runs | `deferred` on that argument, with the call's sentence | | the argument is omitted, and no value runs | `unavailable`, with the default's sentence | | the argument has a value that the call refuses | `unavailable`, with the call's sentence | The helper `_argument_resolved` applies this rule to four rows: `truncation_curve` on `mechanism`, `refute` on `tests`, `benchmark` on `covariates`, and `simulated_confounding` on `estimand`. The `tipping_gamma` gate refuses `use_ci=True` on a fit that supplies no inference, and it leaves every other request unchanged. A fit-wide refusal stays in one ordered table, which the row and every entry point read. The sweep in `tests/unit/test_capability_row_sweep.py` asks every row of each kind of fit in `KINDS`. Each row that a request resolves as available must run, and no row may decline or stay deferred. Each committed mutation in `MUTATIONS` makes the sweep fail on the kinds that it names, and on no other kind. On each named kind, one problem matches the signature of the mutation. An unchanged control, M0, passes. Availability is authoritative before execution. Each capability row names the `Replayability` field it needs in `requires_replay`, and the shared base applies that gate to every row. A facade may not patch one row by name. `refute` read its slot only while running, and so reported `available=True` on a result that carries no estimator. A replay slot reads the checks that its call runs before any learner. The slot `refit_nuisances` reads `TMLE._refit_configuration_refusal`. That method runs `_configured_for_refit` and then `_preflight_fit_configuration`, which includes `_resolve_estimands_for_data` with every subclass override and the remaining configuration guards. The chain fits nothing, so the method reads each `ValueError` of the chain as a refusal. `CapabilityError` and `DataError` are `ValueError`, and so is a malformed setting that a copied estimator can carry. Any other exception is a defect and propagates. A result whose estimator holds a configuration that `refit()` refuses keeps `retarget_cached_nuisances` and loses `refit_nuisances`, with the code `point_replay_refit_configuration`. Every refit asks `_configured_for_refit` for the estimator that it fits, and an override adapts it for an added covariate. The override returns a copy, so a refit never changes the fitted estimator. Repeated-sampling studies retain one structured `ReplicationRecord` per estimand and a `ReplicationFailure` with replicate index, seed, exception type, and message for every failed fit. `CoverageStudy` records only the errors that `cleverly.validation._failures.recordable` accepts, and any other error stops the study. An error in the data-generating process always stops it. Summaries are derived from those records through `summarize_replications`; evidence adapters must not reimplement bias, root-n bias, coverage, or standard-error calibration. The study alpha comes from the returned `ParameterEstimate`, and all successful records must agree on it. ## Targets, interventions, and variants A new point-treatment estimand is a `Target` registered through `targets.register`. If it needs a new score group, register the fluctuation submodel first. Put its influence curve in the shared inference layer so covariance, bands, delta methods, and score diagnostics remain reusable. A regime is a density over arms and is a parameter axis distinct from arms, shifts, incremental interventions, and MSM coefficients. Keep those axes explicit; a fit must not mix incompatible definitions of its counterfactual under one result namespace. A modified treatment policy is a map of the natural dose, and `policies=` is its axis. Every policy class resolves to deterministic branches with known probabilities, through `policy_branches` for a continuous dose and `discrete_assignments` for a categorical one. The ratio, the support report and the longitudinal node read those branches and nothing else. A new class adds a branch type there, not a new axis. `RiskRatioTilt` differs in its report only. It reads `g`, so it reports on the `rr_tilt` axis. `TMLE` refuses a tilt beside other policies. An incremental intervention is an odds tilt with one factor for each level of the treatment. A scalar names the second level of two. A mapping names levels by label. The `Incremental.factors` method resolves a declaration against one treatment or one node, and it is the only reader of the declaration. A new consumer calls it with the levels it holds and a subject for the refusal text. It does not read `.delta`. The binary surface rule holds at every consumer: with two levels, the code runs the arithmetic of Kennedy (2019) with $\delta=\delta_1/\delta_0$, and with more levels it runs the general formulas. The two branches meet in the mechanism fluctuation too, which is logistic for two levels and a softmax for more. A two-level estimate, influence curve, standard error and fluctuation are bit-identical to the ones before the categorical change. Two diagnostics changed: `IncrementalSupport.min_propensity` is the minimum over both columns of the mechanism, and `IncrementalSupport.delta` holds label and factor pairs instead of one float. `tests/unit/test_incremental_binary_surface.py` and `tests/unit/test_ipsi_binary_surface.py` replay the committed studies to check it. A three-level fit on a configuration of the rows `T35` and `G43` carries the label of the registered study [categorical incremental policies](technical-reference/method-evidence/categorical-incremental.md). Any other three-level fit is `experimental`, and no binary support row covers it. `tests/unit/test_categorical_ipsi_declaration.py` holds that check for nodes. An estimator variant that only changes which nuisance estimate is targeted should override the nuisance hook, return a replaced `NuisanceEstimates` with diagnostics, and inherit targeting and result behavior. `CTMLE` is the reference pattern. A method with different data ordering or recursion, such as `LTMLE`, should use a separate container and result type and must explicitly reuse or refuse each shared subsystem. A selector-based `CTMLE` fit chooses one shared categorical treatment mechanism. At `K` arms its selection target is one nonredundant vector: all `K` means for `ey`, or the `K - 1` contrasts against the declared reference for `ate`, `rr`, and `or`. Do not fit or select a separate mechanism per contrast under one result; that is a collection of estimators with different nuisance states, not one joint fit. `TMLE.retarget` operates on cached point-treatment nuisances. Do not assume this contract for a variant whose derived equations require nuisance refits at the targeted state; document and test the cost and persistence behavior instead. ## Longitudinal, survival, and competing risks Resolve dynamic rules and policy densities once, before fitting, into the plan's assignment matrix and policy arrays. A rule receives only the history available at its node, and a policy density receives that history and the earlier treatments. Downstream mechanism, support-mask, outcome-regression and clever-covariate logic must read the same resolved plan. Outcome designs contain covariate history and the arms of earlier policy nodes, not past treatment columns that are deterministic under the resolved plan. At a policy node the design also holds the current arm, and the node carries the policy-weighted mean of its per-arm predictions. A plan without a policy node runs the arrays of a deterministic plan, and `tests/unit/test_influence_gateaux_longitudinal_policy.py` pins a one-hot policy to its rule bit for bit. A modified treatment policy node reuses the per-arm carry of a policy node. At a categorical node its columns are the levels. At a continuous node its columns are the randomizer branches, and the node's treatment factor in the cumulative product is the density ratio. `g_bounds` therefore bounds only the censoring and categorical factors. The fit attaches the ratio to the plan after the mechanism fit. A frozen plan carries it, so a replay reads the same ratio. A cross-fitted per-regimen fit targets after its folds, not inside them. Each fold runs an untargeted backward regression sequence on its training rows. One pooled fluctuation per node then targets the out-of-fold predictions over every follower, with the out-of-fold cumulative mechanism in its loss weight. This is Section 5.2, Steps 1 to 4, of Díaz, Williams, Hoffman and Schenck (2023). The proof of their Theorem 3 needs each fold's fit to be fixed given its training rows. So no pooled coefficient returns to a fold regression, and the per-regimen fit divides by the out-of-fold mechanism alone. `n_folds=1` keeps the canonical single-fold recursion, which carries each targeted prediction back. `tests/unit/test_pooled_longitudinal_targeting.py` holds the longhand checks and the mutations. *Reconsider when* a published result certifies a fold-local update for this plug-in estimator. The sequentially doubly robust estimator `SDR` shares plan resolution, masks, designs and the mechanism fit with LTMLE. Its node regressions fit the Equation (4) transforms of Díaz, Williams, Hoffman and Schenck (2023), composed with the node's event. It fits no fluctuation and clips no regression. `g_bounds` bounds each partial path probability, and the estimate is the mean of $\varphi_1$. Both estimators use fold-measurable nuisances. A cross-fitted SDR fold builds its training transforms from its own regressions and its own mechanism fit at its training rows. The estimate is the mean of $\varphi_1$ over every row. `tests/unit/test_sdr_construction.py` holds the longhand and the two rejected constructions. *Reconsider when* a published result certifies the stitched transform of Section 5.3, Step 2, or the recombined ratio that `lmtp` reads. The sequential g-computation and IPW estimators share plan resolution, masks, designs and the mechanism fit with LTMLE. `SequentialGComputation` reads `untargeted_recursion`, the recursion that LTMLE fits before its fluctuation, on every row. `SequentialIPW` reads the cumulative mechanism product and the clever covariate of LTMLE. Neither fits a fluctuation, and both fit in sample only. `tests/unit/test_sequential_baselines.py` checks that the g-computation estimate is the first-node mean of the shared recursion (W8). The incremental estimator `IPSI` shares plan resolution, masks, designs and the mechanism fit with LTMLE. An odds-tilt node's factor is computed in the cancelled form $\delta_{A}/D$, with $D=\sum_b\delta_b\pi(b)$. With two levels it is $(\delta A + 1 - A)/(\delta\pi + 1 - \pi)$. It is never divided by a bounded mechanism, and it is never written as $q/\pi$. The mechanism term at a node is $W_{t-1}(\delta_{A_t}/D_t)\{m_t(H_t,A_t)-\bar m_t(H_t)\}$ for any number of levels. A node reads its own factors, and a node with two levels branches to Kennedy's arithmetic. `g_bounds` bounds only the cumulative censoring, label, rule and policy product, and the tilt product multiplies in after the bound. The influence function holds a mechanism term exactly at each tilt node whose treatment mechanism is estimated. `IPSI` fits no fluctuation. Its estimate is the pooled mean of $\varphi$ over every row. A cross-fitted fold runs the training-fold recursion that SDR runs. `tests/unit/test_ipsi_exact_laws.py` and `tests/unit/test_ipsi_construction.py` hold the checks. Longitudinal MSMs are projections over regimen/horizon cells. Their fluctuation is pooled and the backward recursion proceeds in lockstep over nodes; the horizon belongs in the design and each cause receives its own projection while sharing nuisance fits. Under cross-fitting the fold recursions run per cell and stay untargeted, and only the pooled update is lockstep. No pooled coefficient and no working-model coefficient returns to a fold regression. A policy cell moves its per-arm predictions in the same stacked fluctuation, along its own block of the design, and carries their policy-weighted mean. For survival, a unit experiencing the event at node `t` belongs in node `t`'s event regression. The event-free state is part of history, not an intervened mechanism factor. Reports store risk; survival is a derived scale, with contrasts transformed by sign rather than by `1 - estimate`. For competing risks, use a cause-specific event numerator with the all-cause survival factor. Competing events affect the at-risk population but do not add a denominator factor unless a new estimand explicitly intervenes on them. Cause-specific estimates need not be renormalized to a simplex. A repeated outcome is not an event. It has no risk set and no survival complement. Each horizon $h$ seeds its own observed $Y_h$, so horizon $h$ is the end-of-study fit of the panel truncated after $h$. The container puts $Y_s$ first in block $s + 1$, so $Y_s$ precedes $L_{s+1}$ in every history. A caller that keys or groups parameters by horizon reads `reports_curve`. A caller that uses the absorbing structure reads `is_survival`. `tests/unit/test_repeated_outcome.py` checks the reduction, and a permuted `outcome` field changes no estimate of a repeated fit. A held design has one treatment decision, at node 1, held over every node. Each later node is an identity node. Its plan value is the observed node-1 value, its factor is exactly one in the ratio numerator and denominator, and it fits no model. It is never a policy node or a modified treatment policy node, and it adds no block to a mechanism or outcome design: there is one block per decision. Regimens resolve on the decisions, so a rule or a policy is evaluated once, on the node-1 history. `tests/unit/test_point_policy_mtp.py` makes the held nodes policy nodes and sees the Gateaux check fail. `tests/unit/test_point_survival_mutations.py` re-evaluates a rule at every node and sees the estimate move. A censoring node at which no eligible unit is censored has the fixed factor one and fits no learner. The eligible rows are the rows at risk before the node with positive weight. A cross-fitted training fold with no censored unit predicts one, which is its empirical rate. The nuisance report shows an omission in place of a model row. `survtmle` sets `G_dC = 1` at `t = 1` for the same case. `tests/unit/test_no_censoring_node.py` removes the guard and sees the solver error return. Hazard targeting (`survival_model="hazard"`) requires a held baseline decision over a categorical treatment and no covariate measured after baseline. It refuses any other design before any learner (H1 to H3, H7 and H11 of [the hazard refusals](technical-reference/scope-and-refusals.md#hazard-and-isotonic-survival-refusals)). The hazard model conditions on the baseline treatment and the baseline covariates only. A later treatment node or covariate needs its own model and fluctuation. *Reconsider when* a read source gives the hazard construction for a treatment at several nodes or for a later covariate. A one-step hazard fit's levels are non-decreasing in floating point for every regimen and cause. Every horizon's plug-in mean reduces its column in one order that does not depend on the values. The method `HazardState.levels` holds that reduction. Rounding to nearest is monotone, so this order keeps each curve non-decreasing. `tests/unit/test_survival_monotone.py` pins the monotonicity with no tolerance. *Reconsider when* the plug-in moves to a reduction whose order changes between horizons. A hazard cell with followers at risk and no event of its cause is held at exactly zero. The fluctuation never moves it, and each parameter that reads it takes the `constant_node_plugin` status. A cell whose followers all have an event is held at an all-cause hazard of exactly one, under the same rule. A clipped cell stops the one-step walk short of the joint score equations. `tests/unit/test_hazard_empty_cells.py` and `tests/unit/test_hazard_full_cells.py` show this by mutation. *Reconsider when* an interval at such a cell ships under [F31](roadmap.md#f31-inference-at-a-boundary-node-estimate). The isotonic projection (`isotonic=True`) moves the levels and the curve band limits only. The critical value and every standard error stay as fitted. Contrasts are rebuilt from the projected levels and keep the band built at the unprojected levels. Westling, van der Laan and Carone (2020), Theorem 1 (iv) and (v), supports the projected limits. The mutations M10 and M13 in `tests/unit/test_isotonic_projection.py` move the influence curve or centre the band, and each one fails. *Reconsider when* a registered study shows the pointwise interval at a projected level fails where the true curve strictly increases. ## Weights, bounds, and sensitivity Observation weights define the target population and must flow through nuisance loss, score equations, influence functions, covariance, and effective-sample-size calculations. Estimand or MSM weights answer a different question; do not merge the two. Choose mechanism bounds from the clever covariate's algebra. Conditional-effect groups use propensity odds and need their corresponding bound regardless of arm count. A binary-only method must declare and validate that restriction rather than indexing two arms by accident. Sensitivity analyses operate on one named parameter or contrast. Resolve its arms from structured parameter metadata, not string splitting, and preserve the distinction between assumptions shared across arms and explicitly arm-specific assumptions. ## Parallelism and performance Nuisance fits are single-threaded by default so parallelism occurs across folds and learner candidates. Callers can change the process-level limit through the public learner controls. Do not add nested native threading without an end-to-end measurement and an oversubscription plan. Nested model parallelism oversubscribes small fits, and repeatedly constructing the thread-pool controller can itself be a material cost. *Reconsider when* a measured workload benefits from giving one model the machine; callers can already opt out with `set_thread_limit(None)`. Concurrency is `outer × inner × threads-per-fit`; the third factor is pinned to one, and the split of the first two belongs to the test tier. The fast tier consists of thousands of short tests, so xdist balances it and inner `n_jobs` remains one. A standalone regeneration script is not a test tier and does not inherit that split. It owns the machine, so it sizes its inner pool from `tests.parallel.available_cores()` rather than from the measured `STUDY_JOBS` floor. It must still keep its phases *sequential*. `tests/canonical/tmle3/` generates every sample and fits the Python side to completion before it hands the same samples to the R container. The two are the same work on the same cores, so overlapping them would leave both contending for a machine neither can have. The fast suite leaves inner parallelism alone. Documentation examples are not statistical evidence. A unit, integration, or end-to-end test in the fast tier must cover the behavior a guide shows, or a registered validation study must cover it. A documented example never supplies the evidence for an estimate, an interval, or a diagnostic verdict, whatever a runtime gate reads off that example. Evidence manifests such as `docs/technical-reference/evidence.md` remain test-enforced source registries. A reader-facing example must nonetheless *run*. `tests/unit/test_documentation_runtime.py` executes the registered documents' fences and the tutorial notebooks' code cells. It runs three gates, and the table states what each one covers. | gate | documents | sample size | what it asserts | | --- | --- | --- | --- | | `test_every_nonsemantic_example_runs` | entries in `PRELUDES`, none of them tutorials | shrunk to `SMALL_N` | nothing raises | | `test_tutorial_semantics_at_documented_size` | every tutorial under `docs/examples/`, as Markdown or as a notebook, offline | the size the page prints | nothing raises, then one reviewed callback checks that page | | `test_every_narrated_decimal_matches_a_stored_output` | every tutorial notebook | none, it reads stored outputs | each decimal in the prose rounds from a stored output | The smoke gate asserts no number. It exists because compiling a fence cannot see a name the package does not have. Six shipped examples were broken that way at once: two on a renamed attribute, two calling `.summary()` on reports that expose `to_frame()`, one passing a float where an assignment density is required, and one stratifying on a column the design does not adjust for. Every one of them rendered as ordinary, copyable code. *Reconsider when* the check stops paying for its runtime. The semantic gate does assert numbers. It stays inside the rule above because of what it asserts them about: each callback checks that a page reports its own output correctly. A callback reads identification metadata and summary strings, exact display identities such as `survival = 1 - risk`, and the direction, ordering, and coverage that the page narrates at the page's own seed. An identity holds in every sample. A seeded relation holds in the one sample the reader sees, so the callback detects a page that contradicts itself and certifies no method. A method claim still needs an ordinary fast test or a registered study. The gate is bounded by review rather than by a heuristic. Each callback lives in its own module under `tests/unit/tutorial_semantics/` and not in the document, so a rewritten page cannot grant itself a numeric gate. One module per tutorial lets several tutorials change at once without an edit to a shared registry. `test_every_tutorial_has_one_semantic_assertion` discovers the tutorials and pins the modules to them. The documented-size gate is their only runtime pass, which avoids repeating each fit at the smoke size. *Reconsider when* a callback needs a looser tolerance to keep passing. A relation that moves under a supported change is a sampling claim, and it belongs in a registered study. A reader-facing notebook stores outputs, so its fast check is necessarily narrower. The stamp detects a code, output, or stamp-field edit after stamping. It does not prove that the code produced the output, because the hashes and payload share one mutable file. The executor disables skipped cells and tolerated errors. A result-determining change requires its manual `--check` re-execution. All other documentation checks are static and belong in the ordinary fast tier. Links resolve, including links that name a repository path. Every `python` fence parses. There is deliberately no manual documentation job. These properties are cheap enough to run on every change, and a dispatch that re-ran them would read as a gate while adding no coverage. That was the removed job's failure mode: its `ruff check README.md docs` validated nothing at all, because the linter does not read Markdown. Note the division: the ruff *formatter* does reach inside `python` fences and is covered by the whole-tree `ruff format --check .`, but it skips any block it cannot parse, so syntax is a test's job and not the formatter's. **The warning-as-error build runs on every pull request, and that is a different job.** `ci.yml`'s `docs` job builds the site with `-W` on Python 3.12, which is the interpreter `pages.yml` deploys from. It is not the dispatch ruled out above, because it checks what no fast test reaches: numpydoc validation of each rendered docstring, a document that no toctree references, and a cross-reference Sphinx cannot resolve. Before it existed, `pages.yml` gave the first `-W` run *after* the merge, so a rejected docstring took the published site down instead of failing a request. A docstring must therefore validate on every supported interpreter and not on one: `AssessmentStatus` documented the synthetic `enum.StrEnum` constructor that 3.12 exposes, which left `PR01` and `PR02` firing on a 3.11 build of the same tree. *Reconsider when* the build stops paying for its runtime on a request. **Prose is reported, never gated.** `tests/prose.py` reports on reader-facing writing and `tests/unit/test_documentation_prose.py` fails on a finding carrying no recorded judgment, not on the writing. Recording `accepted: ` in `tests/prose-report.md` is a passing outcome, so the decision stays with the writer. *Reconsider when* a rule is found that is exact enough to have no defensible exception. This is a standing decision rather than a preference, and the reason is measured. A Vale rule that failed the build on an em dash produced a sweep that stripped dashes mechanically, leaving six sentences without a predicate, two enumerations broken mid-list, five altered technical claims and four deleted evidence clauses. The rule was correct and the enforcement mode did the damage. Development-tool versions have one declaration: exact Ruff and mypy pins in the `dev` extra, resolved by `uv.lock`. Nox and CI install that extra; they do not restate versions in session or workflow files. Size every layer from `tests/parallel.available_cores()`, which reads a container's CPU quota and affinity mask through joblib. Neither `os.cpu_count()` nor xdist's `-n auto` does. Nesting pools is sometimes right and is never assumed: record the configuration used and the measured alternative where defaults are set. Before adding compiled code, compare against a competent NumPy implementation and include the real learner workload. Track compile time, memory, core count, numerical equivalence, and the kernel's share of a fit. Machine-specific output is exploratory evidence, not durable documentation; only the resulting cross-module decision and the condition that would reopen it belong here. Production code stays pure Python. Prior measurements found nuisance fitting dominant in representative workloads and found no material full-workload advantage from compiling the clearest package-owned numerical kernels. *Reconsider when* a competent compiled implementation wins materially in a full supported workload, including compilation, memory, data movement, packaging, and maintenance cost. Choose the algorithm before choosing the compiler. Newton targeting is the default because the universal least-favourable one-step walk can dominate a cheap GLM fit, and the Gaussian multiplier option avoids the Rademacher resampling matrix where its approximation is appropriate. Neither choice removes the need to inspect overlap and influence-curve behavior. Scale is constrained by statistical learning and memory before it is constrained by targeting arithmetic. The conditional-density learner's long design is the remaining known superlinear allocation. Benchmark with the intended learner and data shape before changing a numerical kernel.