Protocol v0.4.1 and Phase 1 tasks 1.0 and 1.3 - #12
Merged
Merged
Conversation
The 'peto' option returned weights unchanged while being described as a correction, and was the default. It is replaced by three explicit methods: 'none' (no correction; the data carry a mortality_correction attribute and an estimand note saying the results describe respondents who survived to be surveyed), 'mport' (primary, stops as not implemented, task 1.7b), and 'peto' (sensitivity, stops as not implemented). A configured correction that leaves every weight unchanged now stops the pipeline (assert_correction_applied), so a stub can no longer pass as a correction. README, workflow and reference docs, the rate-table schema, and config comments now state that no correction is applied yet and that outputs are estimates among survivors (protocol section 3.4.5). Tests cover all four paths and the guard.
Operational specification implementing protocol v0.4.0 section 3.4.1 and adjudication A1: state definitions with their CCHS variables, the two modelled transitions, event-time conventions (one-year steps, one spell per person, cessation risk from first cigarette, two-year durability, recent quitters current and censored), target population and immigration entry, and the output contract with the smoking-history generator. Records one open decision for the PI: the same-age initiation/cessation rule (proposed: one-year spell as primary, exclusion as sensitivity). Names the code consequence that the cessation model must move from the ever-daily universe and time_quit_smoking_daily to all ever-smokers and time_quit_smoking_complete.
…ensitivity); add count diagnostic
…-op guard; fix stale docs Review findings on PR #4. fit_apc_model() now copies the mortality_correction and estimand_note attributes from the APC dataset onto the fitted model, with a test, so Stage 8 outputs cannot pass as corrected. assert_correction_applied() checks that a correction changed only the weights: same row count, identical age/period/cohort/event in the same order, finite positive weights, and at least one weight changed; tests cover dropped rows, reordering, and non-finite weights. apc-plan.md is marked superseded in part and its two stale 'peto' passages updated. The Stage 7 workflow page now lists the real columns (event, weight), notes that spline columns are built in Stage 8, and documents the two attributes.
…interval conventions, recent-quitter semantics Revisions from external review of PR #6. The 100-cigarette gate (Manuel et al. 2020) now defines membership; experimental smokers are Never; age at first cigarette dates entry only for those who pass the gate (smoked_100_lifetime; SMK_01A for 2022). Delayed entry for immigrants is stated per transition (arriving Never, Current, or Former). The annual interval is defined (age row = year from the a-th birthday; event row in the risk set; initiation before cessation within a year; survey year included). Recent quitters are separated into observed status (Former), modelled state (Current up to survey), and risk-set contribution (censored at quit age), so each person has one modelled state at each age. The same-age sensitivity removes the person from both transition models. The dependency on PR #4 for the 'none' correction value is declared. The PI's 2026-08-27 decision on 2001 (complete-cessation timing is NA(c), imputed) is recorded.
Wording and plain-language edits throughout; no methodological change. Ethics statement rewritten (secondary analysis of de-identified data; Statistics Canada Open Licence). Study team updated. NHIS typo fixed. Edits were made in Word and merged back by hand: the docstyle harvest remaps citekeys when field-codes.json entries lack a Zotero URI or share one, so the harvested file was used only as a source of text changes. The edited .docx is kept under docs/protocol/source/ for provenance.
The protocol's Word configuration was lost when docs/_quarto.yml was removed in the documentation restructure (4c96354); _docstyle/page-config.json was a fossil of it. Restore it as docs/protocol/_quarto.yml, mirroring the manuscript subproject, with the original header, footer, TOC and version-history settings and the popcorn-base/pop-draft-manuscript CSS. The docstyle tooling resolves _extensions/ and _docstyle/ relative to the project directory only, so the subproject has an _extensions symlink and its own _docstyle/ sidecar. A nested _quarto.yml does not remove the protocol pages from the root website build (checked with quarto inspect and a root render). Output goes to docs/protocol/output/ (gitignored).
…ring; specification fully decided
Phase 1, task 1.7a: mortality method 'none' by default; no-op corrections fail
Phase 1, task 1.0: smoking-state and transition analysis specification
Text edits from the second review of the Word render merged into the QMD (same 0.4.1 entry; history description updated). The edited .docx is kept under docs/protocol/source/. The "STROBE: Potential bias" placeholder sat between two items of a nested list, so Pandoc turned the remaining items into an indented code block (literal ** and hard-wrapped text in Word). The placeholder now follows the list, combined with the STRESS tag. New docs/protocol/protocol.css layers spacing over the base theme: more space above level-1 and level-2 headings, space before and after the reporting-guideline blockquotes, and a Dateline paragraph style (custom-style div) for the date and version line under the title.
PUMF initiation-age floor stays at 13 (the 5-11 category, midpoint 8, is too coarse to date entry; Master uses exact ages with a floor of 8); the first CCHS cycle is assigned survey year 2001 for cohort assignment. Recorded in config comments and protocol sections 3.2 and 3.3 with a version-history entry. No change to methods or code.
The body section already starts with a nextPage section break; the nested ::: page-break div added a second break in an otherwise empty paragraph, producing a blank page 2 in the Word render.
…ields Enable the docstyle author plate (authors and affiliations from _quarto.yml; list and order TBA, more authors to come) with the ::: author-plate ::: placeholder under the date line. Holford_SM_2006, Rao_SM_1992 and StatCan_CCHS_UserGuide_2022 were absent from field-codes.json, so Pandoc citeproc rendered them as plain text and appended its own bibliography after the version history. Added their CSL data (converted from references.bib) so all 26 citations are Zotero fields and the stray reference list disappears.
Author list and order are still to be decided; the authors block stays in _quarto.yml (not rendered) for when the plate is re-enabled.
…ts below 13; cessation entry still uses the midpoint; ratifies open decisions
Record study decisions from public issue #5 (protocol 0.4.1)
The cessation model now follows the established-smoking estimand. Universe: established smokers (100 or more cigarettes, smoked_100_lifetime, via a config-driven yes_code) across all ever-smoker statuses, replacing the ever-daily subset; the same gate applies to the initiation numerator, so experimental smokers are Never in both models. Event: stopping smoking completely, dated by time_quit_smoking_complete (new config key years_since_quit_complete; years_since_quit keeps the daily variable for intensity and sensitivity work). Clock: each person's risk starts at their own age at first whole cigarette (expand_denominator gains a per-person age_denom_min), replacing the floor of zero that started the clock at birth. Durability: quits under cfg$apc$cessation_durability_years (2) are censored at the quit age with no event. A quit at the entry age is one trial with the event. Missing entry age, entry after survey, quit before entry, and missing quit timing (all of 2001, where the stopped-completely questions were not asked) are excluded and counted per cycle, unweighted and weighted, in a cessation_diagnostics attribute pending imputation (task 1.8c). Worksheet roles updated. Tests cover the universe, the gate in both models, no person-year before entry, durable and recent quitters, the same-age spell, and each exclusion class. Docs updated.
…odes) SMKDSTY_original groupings (ever, current, former, never) and the sex codes now live under config.yml survey.* as *_codes / *_code entries, read through survey_code(); R/apc-model.R no longer contains literal status or sex codes (prepare_apc_data, build_initiation_data, build_cessation_data, get_period_constraint). A test changes the former-smoker codes in config and checks the universe classification follows. First step of remediation task 3.1 (value-code semantics layer), scoped to the APC stage.
Public issue #3, items 4 and 7. fit_binomial_apc() now stops when there are no person-year rows, when the numerator has no events or zero weighted events (a model that would encode 'nobody ever made this transition'), or when a cell has non-positive person-years or more events than person-years; after glm() it stops on non-convergence and warns on a boundary fit. Tests cover the empty and zero-event cases and a converging fit.
PI style review. 'Established-smoker criterion' replaces 'gate'; 'the model includes' replaces 'universe'; 'time at risk begins' replaces 'clock starts'; 'a person who started and stopped at the same age' replaces 'one-year spell'; the diagnostics group is same_age_quits. Error and guard comments say what happens instead of 'fail loudly' or 'plausible-looking'. Applied to the workflow page, the estimand specification, CLAUDE.md, config comments, the worksheet purpose text, and R comments and messages.
… names; last 'spell'
…osure at Stage 1 The first CI pipeline run failed after 56 minutes at the imputation stage: time_quit_smoking_complete was never derived because two of its cchsflow inputs, SMK_10_gate and SMK_10A_cont, were not in the study variables sheet, and rec_with_table() skips a derivation with missing inputs without an error. Both are added as intermediate rows. A new check_feeder_closure() reads every DerivedVar rule for a study variable from the variable-details sheet and stops in the coverage_check target if any input is absent from the variables sheet, so this class of gap fails in seconds at Stage 1. Tests: the project worksheets pass the check; a synthetic sheet with a missing feeder fails with a named message. Verified locally: harmonizing the 2013-14 and 2001 fixtures yields time_quit_smoking_complete (present for 2013-14, absent for 2001 as expected).
…ounds External review of PR #7 (three blocking, four further findings). Cessation risk now begins at each person's own age at first cigarette; the reporting floor is no longer applied to the start age. Quit timing is validated: it must be finite, within the configured bounds for the data source (survey.years_since_quit_complete min/max), and place the quit no later than the survey; otherwise the person is excluded and counted (excluded_quit_timing_invalid). The durability setting must be present in config; the hard-coded fallback is gone. The initiation builder is rewritten with the same classification discipline: never smokers and experimental smokers are at risk from the floor age to the survey with no event; established smokers have one event at their entry age; established smokers who started below the floor were already smoking when observation begins and contribute nothing here (they stay in the cessation model); missing status, missing 100-cigarette answer, missing entry age, and entry after the survey or outside the configured bounds are excluded and counted, never reclassified as Never. Per-cycle counts are the initiation_diagnostics attribute (shared summarise_groups helper). expand_denominator() coerces cohort and ages to integers again. The worksheet row for time_quit_smoking_complete gains apc-denominator and documents that the variable is absent from the 2022 PUMF as well as 2001 (both NA(c), imputed); the specification, config comment, and workflow page say the same. Stage 7 docs use event and weight. The test that hard-coded status and criterion codes reads them from config. New respondent-level invariant tests: risk from an entry age below the floor; negative and out-of-bounds quit durations excluded; no person-year after the survey age or before entry; missing or invalid initiation records excluded rather than treated as Never; never smokers at risk from the floor to the survey; early initiators contribute no initiation rows.
PI direction: the variable-details worksheet is the reference for minimum and maximum values; config.yml refers to it instead of holding copies. New details_range() reads a variable's valid range for one database from the worksheet rules -- [lo, hi] copy rules, the set of category-to-value midpoints, and, for derived variables, the union of their feeders' ranges -- so ranges follow each cycle's grouping and top-codes. config.yml survey entries for continuous variables now say `range: variable_details`; the 28 literal min/max lines are removed. survey_range() resolves the pointer; the APC builders receive the variable-details sheet from the pipeline and validate entry ages and quit durations per respondent against the range for that respondent's cycle. The initiation floor (PUMF 13, Master 8; public issue #5) is an analysis decision, not a range, and moves to apc.initiation_floor_age. Finding made by the change: the PUMF groups quit duration as under 1, 1-2, 2-3, and 3 or more years from 2003 onward, so the largest midpoint is 5, not the 15 that config comments, worksheet notes, and the specification claimed; only the 2001 cycle reaches 15. All those claims are corrected, and the coarsening is flagged for task 1.10. Tests: details_range on synthetic rules; survey_range on the real worksheets (0.5-5 for 2013-14, 15 for 2001 daily); out-of-range quit durations excluded; synthetic test histories now use in-range values.
…ot a top-code of 15
- The entry age in the cessation model is checked against the worksheet range for age_first_cigarette in the respondent's database, as the initiation model does; an entry age outside it is excluded and counted (excluded_entry_invalid). - Missing smoking status and a missing 100-cigarette answer are counted before the established-smoker filter (excluded_status_missing, excluded_criterion_missing), with experimental smokers reported as not_established_ever_smokers. - Range resolution fails closed: a cycle code not listed in cchs_cycles stops, and a non-missing value from a database with no worksheet range stops rather than passing. Test fixtures now carry NA(c) where the real PUMF does (2001/2022 quit timing, 2022 age at first cigarette). - config/statscan.yml.example uses the worksheet database names (cchs2001_m, ...). - check_feeder_closure() is database-specific: a feeder rule in another cycle does not close the chain. A feeder absent from cshm-variables.csv stops; a feeder with no rule in a study database warns (the 2019-20 PUMF has no SMKG040, which the cchsflow rules for SMKG203_cont, SMKG207_cont and age_start_smoking assume). - Manuscript, variable reference, estimand specification and Stage 7 workflow no longer describe a fixed cessation floor or survey_bound(); they describe the respondent-specific entry age and the worksheet range check.
Phase 1, task 1.3: cessation risk set per the estimand specification
Contributor
There was a problem hiding this comment.
Pull request overview
This pull request updates the prespecified study protocol to v0.4.1 and implements Phase 1 pipeline remediation tasks (1.0 and 1.3) to align APC initiation/cessation datasets with the ratified estimand specification, using unified cchsflow v3 smoking variables and worksheet-derived validation ranges.
Changes:
- Protocol v0.4.1: ratifies key study decisions (PUMF initiation floor; cycle-1 survey year), incorporates PI editorial revisions, and restores Word rendering via a docstyle subproject under
docs/protocol/. - Pipeline: rebuilds cessation risk set around established smokers (100+ cigarettes) with durable-quit event logic, adds diagnostics attributes and fit guards, and standardizes value-code access via config helpers.
- Validation & tests: adds feeder-chain closure checking at Stage 0, introduces worksheet-driven range utilities, and expands test coverage for APC dataset construction and survival-correction guards.
Reviewed changes
Copilot reviewed 31 out of 36 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
tests/testthat/test-validate-coverage.R |
Adds tests for check_feeder_closure() behavior (warnings vs stops; cycle specificity). |
tests/testthat/test-apc-data.R |
Expands APC tests for new cessation estimand, range checks, and mortality-correction guards. |
tests/testthat/setup.R |
Loads combined variable-details sheet once for all tests (TEST_DETAILS). |
tests/testthat/helper-apc.R |
Updates APC test data generator to include established-smoker and complete-cessation timing fields. |
schemas/cshm-rate-tables.yaml |
Updates schema documentation for mortality_correction including "none" semantics. |
README.md |
Clarifies that mortality corrections are not implemented; documents "none" behavior and guardrails. |
R/variable-details-sheet-utils.R |
Adds details_range() to derive per-database min/max from variable-details recoding rules. |
R/validate-coverage.R |
Adds check_feeder_closure() to fail early when derivation feeders are missing from worksheets. |
R/config-utils.R |
Introduces survey_code(), cycle_database(), survey_range(), and initiation_floor() helpers. |
R/apc-model.R |
Refactors APC data construction to use established-smoker criterion, durable cessation logic, per-cycle range validation, diagnostics, and mortality-correction labeling/guards. |
manuscript/manuscript.qmd |
Updates manuscript text to reflect initiation floor logic and revised cessation risk-set definition. |
docs/workflow/8-apc-model.qmd |
Updates workflow docs to reflect mortality_method: "none" and new guard behavior. |
docs/workflow/7-apc-data-preparation.qmd |
Updates Stage 7 documentation for new variables, event column naming, and worksheet-derived ranges. |
docs/reference/variables.qmd |
Updates reference docs for weight/mortality-correction semantics and initiation/cessation definitions. |
docs/protocol/protocol.css |
Adds protocol-specific spacing styles for docstyle Word output. |
docs/protocol/full-protocol.qmd |
Bumps protocol to v0.4.1; records ratified decisions and incorporates PI editorial revisions and docstyle layout updates. |
docs/protocol/.gitignore |
Ignores protocol-local Quarto artifacts under the protocol subproject. |
docs/protocol/_quarto.yml |
Adds nested docstyle subproject configuration for Word rendering of protocol documents. |
docs/protocol/_docstyle/section-map.json |
Adds docstyle-generated section mapping for Word round-tripping. |
docs/protocol/_docstyle/reference.docx.hash |
Tracks docstyle reference-doc hash for reproducible Word rendering. |
docs/protocol/_docstyle/page-config.json |
Adds docstyle-generated header/footer and table style configuration. |
docs/protocol/_docstyle/field-codes.json |
Adds Zotero field-code store for Word rendering/round-tripping of citations. |
docs/development/protocol-todo.md |
Updates mortality-method status notes to reflect "none" default and no-op guard. |
docs/development/pipeline-progress.md |
Updates pipeline progress notes to reflect "none" survival correction default and guards. |
docs/development/estimand-specification.md |
Adds/records the Phase 1 estimand specification and conventions (states, transitions, timing rules). |
docs/development/apc-plan.md |
Marks parts of the prior APC plan as superseded and updates mortality-method defaults/notes. |
config/statscan.yml.example |
Renames Master cycle IDs to _m database naming convention. |
config.yml |
Moves ranges to worksheet-derived variable_details, adds value-code groupings and thresholds, sets initiation floor and cessation durability, and defaults mortality method to "none". |
CLAUDE.md |
Updates developer guidance for protocol rendering and new unified-variable/code/range conventions. |
.gitignore |
Ignores protocol Word-render output directory (docs/protocol/output/). |
_targets.R |
Runs check_feeder_closure() in Stage 0 and passes variable_details_sheet into prepare_apc_data(). |
Suppressed comments (1)
docs/protocol/full-protocol.qmd:302
- Spelling/style: “Statistics Canada Open Licence” uses Canadian spelling (“Licence”), consistent with the rest of the repo and earlier references in this document.
This study involves the secondary analysis of de-identified survey data. Analysis of CCHS Master Files will be conducted within the secure environment of Statistics Canada’s Regional Data Centres, following all protocols for data privacy and disclosure control. No individual-level data that could identify respondents will be released. The use of PUMF data for model development is subject to the Statistics Canada Open License agreement.
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| var: age_first_cigarette # Age smoked first whole cigarette (midpoint-estimated) | ||
| min: 13 # Study floor — NOTE: SMKG01C_cont has a 5-11 category (midpoint 8); | ||
| range: variable_details | ||
| # 13 excludes it. Lowering to 8 is an open study decision. |
|
|
||
| 1. **Estimate historical and current smoking parameters:** Reconstruct rates of smoking initiation, cessation, and intensity (cigarettes per day) by birth cohort (1890--2030) for each province and territory. | ||
| 2. **Examine regional and temporal variations:** Analyse how smoking patterns have evolved across Canadian jurisdictions in response to differing policy environments and social trends. | ||
| 2. **Examine regional and temporal variations:** Analyze how smoking patterns have evolved across Canadian jurisdictions in response to differing policy environments and social trends. |
| 4. **Develop an open-access model:** Create a publicly-accessible, reproducible R-based model that can be used and updated by researchers, policy analysts, and health system planners. | ||
|
|
||
| The model will provide demographic and geographic estimates (by single year of age and birth cohort) to interface with existing population health models. Historical reconstruction will cover the period 1965--2023, with projections extending from 2024 to 2050. Analysis will be conducted at the provincial level, with national estimates derived from aggregated provincial data. | ||
| The model will provide demographic and geographic estimates (by single year of age and birth cohort) to interface with existing population health models and support smoking projections and the evaluation of smoking preventive interventions, amongst other uses Historical reconstruction will cover the period 1965--2023, with projections extending from 2024 to 2050. Analysis will be conducted at the provincial level, with national estimates derived from aggregated provincial data. |
| ## Resource and access considerations | ||
|
|
||
| Model development uses publicly available PUMF data in an open R environment hosted on GitHub. The codebase is designed to run on both PUMF and Master File data without modification, controlled by a configuration profile. Production estimates using CCHS Master Files will be produced within a Statistics Canada RDC. RDC analysis requires institutional affiliation, an approved project proposal, and compliance with Statistics Canada disclosure control protocols. The lead analyst will apply for RDC access upon protocol approval. | ||
| This study involves the secondary analysis of de-identified survey data. Analysis of CCHS Master Files will be conducted within the secure environment of Statistics Canada's Regional Data Centres, following all protocols for data privacy and disclosure control. No individual-level data that could identify respondents will be released. The use of PUMF data for model development is subject to the Statistics Canada Open License agreement. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Protocol v0.4.1 and Phase 1 remediation, tasks 1.0 and 1.3.
Protocol (docs/protocol, v0.4.0 → v0.4.1). Two previously open study decisions ratified (issue #5): the PUMF initiation-age floor stays at 13 (the 5–11 category is too coarse to date initiation events below that age; cessation follow-up still starts from the category midpoint of 8; Master files use exact ages with a floor of 8), and the first CCHS cycle is assigned survey year 2001 for cohort assignment. Editorial revisions from PI review of the Word render; Word rendering restored as a docstyle subproject; ethics statement and study team updated (supersedes #11, whose commits are included here).
Pipeline.
age_first_cigarette,smoked_100_lifetime,time_quit_smoking_complete); feeder rows added so the derivation chain closes;check_feeder_closure()is database-specific.apc.cessation_durability_years(2); same-age start and stop is one trial with the event; missing status, missing 100-cigarette answer, missing entry age and missing quit timing (2001 and 2022 PUMF, NA(c)) are excluded and counted incessation_diagnosticspending imputation. Fixes Cessation model: exclusion of former occasional smokers #1.config.yml; variable ranges are derived from the variable-details worksheet (survey_range()/details_range()), and range checks fail closed when a non-missing value has no worksheet range.assert_correction_applied().Version bump per document is enforced by
.github/workflows/protocol-version.yml.