Sapling SAP
Reference

Omakase: the Sapling doctrine and the study folder

The conventions Sapling chooses for you, the study folder and the commands from protocol to dashboard.

This page is the repository's own document docs/omakase.md, shown as it is, so that it never drifts from the product.

Sapling chooses a complete, coherent stack and its conventions for you, like Ruby on Rails. You start from a protocol and a folder of SDTM data, and a few commands take you to the study dashboard. Every choice it makes is written in the open, in the SAP, where you keep it or change it.

The doctrine#

  1. The SAP says what; the specs and the code say how. sap.qmd (Markdown for Quarto, docs/markdown.md) is the statistician's plan. The ADaM and TLF specifications (adam-spec.qmd, tlf-spec.qmd) are the programmer's: made from the SAP, edited by the programmer, checked against the SAP. The programs are made from the specs and are never edited.
  2. Convention over configuration. A new study has one layout, one set of populations, one way to window the visits, one standard table set, one engine. A sapling.yml exists only when the study departs from a default.
  3. Nothing hidden. Every default is a block of the SAP, marked <!-- default: omakase (<the convention>) --> (or <!-- default: company/<template> --> when your company's standards made it), with a citation of the protocol sentence that supports it. The PDF shows it like any other block.
  4. Nobody decides for the statistician. A default is a proposal. The open decisions stay open until a person answers them (sapling decide); the AI drafts, it never approves or decides.
  5. Every step says what it did. One verdict line per step, like Rails' generators; sapling status shows each step as fresh, stale, missing, edited or failed, and no step silently uses a stale product.
  6. One bad analysis stops only its own output. An analysis no engine computes as the plan asks is an error of sapling check at its place (the method registry, docs/markdown.md "The methods and what they report"); sapling build and sapling run make every other output, and the verdict of tlf run names the output that is not made with its first cause (NOT MADE T14_3_2 (analysis T14_3_2_MMRM (sap.qmd:342): no engine computes ...)). sapling status shows tlf run as failed with that output and its cause; the steps after it (the dashboard, define, the package) say which output stopped them.
  7. Everything is editable, in the easiest place for its owner (the table "Edit anything" below).

Getting started#

sapling new cdiscpilot01 --protocol usdm.json --sdtm sdtm/   # the study folder, a first SAP, the omakase defaults
cd cdiscpilot01
sapling generate table teae-severity                          # one more conventional table, in its section
sapling ai "state how missing data are handled in the primary analysis"   # your AI drafts; you apply
sapling decide                                                # the open decisions; a person answers them
sapling build                                                 # the SAP (PDF by Quarto), the specs, the programs
sapling run                                                   # the ADaM data, the TLFs, the ARD
sapling dashboard                                             # the study dashboard on a free port of 3100-3199

sapling doctor checks the machine first (Node, R and its packages, the R engine, DataStep Studio for R, Python, Quarto, the AI provider, the editor), each line with the exact command that fixes it.

The study folder#

PathWhatWho
sap.qmdthe SAP in Markdown for Quarto (docs/markdown.md)the statistician
adam-spec.qmd, tlf-spec.qmdthe specifications, made by sapling adam spec / sapling tlf spec, then editedthe programmer
_extensions/sapling/the Quarto extension of Sapling (a Lua filter): the blocks as boxes in the PDF and in a Quarto preview; the plan names it (filters: [sapling])
protocol/usdm.json, protocol/protocol.jsonthe protocol (CDISC USDM) and its page index (what a citation resolves against)
sdtm/links to the SDTM .xpt files, and sdtm.json (their metadata)
custom/code of your own, when a spec cannot say it (docs/customize.md); Sapling never writes therethe programmer
standards/the study's own templates of the defaults (below)the study team
out/every product (never edited: change the SAP or a spec)
.sapling/what Sapling keeps for itself: the step state, the facts read from the protocol and the data, the bases of the specs, the R engine's view of the study
.Rprofilethe study's R session loads the R engine (DataStep Studio for R: dsr start here)
sapling.ymlonly when departing from a default (engine:, ai.provider:, reference.adam:, reference.tlf:, dashboard.filters:, standards:)

The commands#

Every step has its own command, its products in a folder of out/ named like it, and one verdict line. A step knows what it needs: when an input is missing or older than its source, it says which command to run first, or makes it with --with-deps. sapling build and sapling run are these steps in order (one code path).

CommandProductsNeeds
sapling new <study> --protocol F [--sdtm DIR]the study folder and sap.qmd
sapling generate <thing> ... (alias g)blocks in their section of the plan (in sap.qmd: written in Markdown and inserted at the end of the section's own text, the rest byte for byte)
sapling ai "<plain English>" [--section TITLE] / --apply FILE / --draft-alla drafted block, compiled in its place, applied when you say so (--yes); else saved in out/ai/ (below)the AI provider
sapling check [--strict] [--json]the five passes (below)
sapling review / sapling lab <block>out/review/, out/lab/
sapling decide [n "answer" --by NAME]the answer, the name and the date in the plan (in sap.qmd: the attributes answer, by, on of the .decision div)a person
sapling sap structuredout/sap/sap.md, sap.html, blocks.json
sapling sap jsonout/sap/sap.json
sapling sap pdfout/sap/sap.pdfQuarto (Typst)
sapling sap htmlout/sap/quarto/sap.html (not a step of build)Quarto
sapling sap syncthe rules of the specs written into the plan, each marked for review
sapling adam specadam-spec.qmd (made or merged), out/adam/spec/ (the JSON specification, define.md, define.csv)
sapling adam programsout/adam/programs/ (admiral, one per data set, build_order.txt)adam spec
sapling adam data [DS]out/adam/data/*.xptthe R engine
sapling adam checkout/adam/check.txt: the data against reference.adamadam data
sapling tlf spectlf-spec.qmd (made or merged), out/tlf/spec/ (the analysis contract, CDISC ARS)the R engine (its Python tools)
sapling tlf shells / tlf qc-specout/tlf/shells/, out/tlf/qc-spec/tlf spec
sapling tlf programsout/tlf/programs/tlf spec
sapling tlf run [OUTPUT]out/tlf/listings/, out/ard/ (every number, ars_results.json)tlf programs, adam data
sapling tlf checkout/tlf/check.txt: the listings against reference.tlftlf run
sapling defineout/define/define.xmladam spec, tlf spec
sapling dashboard [--check] [--port N]out/dashboard/ (the configuration, the contract, the gate: DASHBOARD PASS), then the apptlf run, adam data
sapling packageout/package/ with MANIFEST.txtthe products
sapling buildsap structured, json, pdf; adam spec, programs; tlf spec, shells, qc-spec, programs
sapling runadam data, adam check, tlf run, tlf check
sapling statuseach step: fresh, stale, missing, edited or failed (the outputs a step did not make, each with its first cause); the specs against the SAP; the next command
sapling doctorthe machine, with the fix of each problem; which layer each default comes from
sapling templates list / copy <name> [--to study|company]the defaults and their layer; a default copied to edit
sapling export adam-spec --csvout/export/adam-spec.csv (an export: the spec stays the source)
sapling openthe study in VS Code or Positron (Sapling Visual)

Options of the steps: --with-deps (make what is missing or stale first), --force (overwrite an edited product), --keep-session (leave the study's R session running).

sapling generate#

ThingWhat it adds, and where
table <template>populations, disposition (Trial population); demographics (Baseline characteristics); ancova-primary, byvisit-efficacy (Analysis methods); mmrm (Additional analyses); exposure, teae-overview, teae, sae, teae-severity (by the maximum severity), teae-grade, teae-frequent, teae-riskdiff, teae-rate (docs/markdown.md, "Adverse event tables"), labs-summary, labs-shift, vitals, km (Harms). Options: --id, --endpoint N, --dataset DS, --event COND, --population ID
population ITT|SAF|EFF|FAS|MITT|PPthe population and, when it is standard, its ADSL flag; for FAS, MITT and PP (no standard rule) a decision
estimand [ANALYSIS]the ICH E9(R1) estimand of the next endpoint
multiplicity [--procedure P]the protocol's rule for the primary hypotheses (co-primary when the protocol says both must be significant, else a hierarchy)
sensitivitya tipping-point analysis of the primary estimand
derivation DS.VAR "<rule>"a .derive div in the section of its data set (a rule is never invented: without one, only the standard derivations)

Each generator fills its defaults from the plan (its populations, data sets, variables) and from the facts sapling new read from the protocol and the data (.sapling/facts.json). The block is compiled in its place first: a generator that would add an error adds nothing. A second generate of the same block is an error that says where the first is (the line in the plan; --id adds another one). The same convention for the same endpoint (--endpoint N) is the same block. The visual editor offers the same generators in its insert palette, under "Ready-made from your protocol": one code path (src/omakase_service.ts).

The generators write Markdown directly. sapling new (src/init.ts, then src/omakase.ts), the skeleton from a protocol document (src/start.ts) and every sapling generate read the plan by its nodes (the Markdown reader) and write each block in its form of docs/markdown.md (mdCmd of src/qmd_plan.ts: a command of src/commands.ts from its arguments and options). A block a generator adds is inserted at the end of the own text of its section, with one empty line around it; an open decision it answers is removed with its citation. npm test compares the plan of sapling new and the block of each generator with their golden references (test/golden/).

sapling ai: the AI drafts, you apply#

sapling ai "state the primary estimand with its intercurrent events"      # the part of its kind: Estimands
sapling ai "a table of serious adverse events" --section Harms            # the part you name
sapling ai --apply out/ai/20261111-210550-state-the-primary-estimand.qmd  # a saved draft, applied later

Your AI provider (sapling.yml: ai.provider, src/ai.ts) writes the block from your words, the protocol's sentences, the names of the plan and the study, and the language reference. The block is compiled in its place; when it does not compile, the AI gets the compiler's messages and tries again (two times at most).

WhatHow
where the block goesthe part of the SAP of its kind, the table of parts that the skeleton and sapling generate use (an estimand in Estimands, a table of safety data in Harms, a sensitivity analysis in Additional analyses, ...); --section TITLE puts it in the part you name. The output says where it went and why
the open decision of the partthe draft takes its place (the skeleton has one per part); the output names the decision that goes. The decision is removed only when the draft is applied
a draft with an errornever written into the plan, also with --yes (an error the plan had before the draft too): the output shows the first error, and the draft is saved
without --yes (and no y)the draft is saved as out/ai/<time>-<words>.qmd; sapling ai --apply <file> applies it later (in its part, in the place of the decision if it is still open; checked again)
the namesthe AI may write only the data sets, variables and parameters the plan names, the ADaM specification (or the SDTM metadata of a plan that derives its ADaM) has, or, without a specification, the standard names of the ADaM IG. A name it makes up is sent back to it; one it keeps is listed: "names the statistician must confirm"
choicesa value the request and the protocol do not give (the number of imputations, a seed, an assumption, the degrees of freedom, the ties) is not chosen silently: the AI asks for it in its one decision, or cites where it comes from; the output lists "choices left to you"
neveran approval, the answer of a decision, or a waiver: such a draft is refused

sapling new --protocol protocol.pdf: a skeleton from the protocol document#

Without a USDM file, sapling new reads the text of the protocol (a PDF through pdftotext, or a text file) into protocol/protocol.txt and writes a skeleton sap.qmd: one part per item of Gamble et al. 2017, each an open decision that names the section of the protocol that speaks of it (by the words of its headings, the statistics chapter first for a part about the analysis; else the section whose text says it most). A part whose section is not found cites nothing, and its decision says "The protocol is silent on it, or its section was not found". sapling new prints the parts it did not find, and a note for each kind of page whose text cannot be read as it is: the pages without text (pictures or scans), the letter-spaced pages and the lines of them it rebuilt, and the pages with cipher text (docs/markdown.md, "A protocol PDF whose text is not its words").

sapling dummy: data before the first patient#

A study with a protocol and the metadata of its SDTM data (sdtm/sdtm.json: the domains, their variables, the values a variable can take), and no data yet:

sapling dummy            # sdtm/*.xpt (DUMMY data) and the report sdtm/DUMMY.md
sapling run              # the ADaM data and the tables of the plan, on the dummy data

sapling dummy [--per-arm N] [--seed S] [--effect SD] [--endpoint DOMAIN.TESTCD] [--force] (src/dummy.ts) makes SAS transport files of DM, EX, DS, SV, AE and every findings domain with a list of test codes. It writes only the variables of the metadata, only values the metadata allow, no text longer than the metadata say. The same seed gives the same bytes. Two things in the data are designed:

  1. The cases the imputation rules are for. The first subjects of every arm are one case each: complete, dropout, missed-visit, two-in-window, tie (two records equally far from the target day), late-visit (a record in the next window), no-baseline, two-baselines, partial-dates (a start date without the day in the month of the first dose, one without the month, an event without an end, one before the first dose), not-dosed, open-exposure (no end date of exposure), death, and the screen failures. sdtm/DUMMY.md lists each case with its subjects, and for each imputation or selection rule of the plan (a window, an analysis record flag, a baseline, a date imputation, LOCF, a population flag) the cases that reach it. A rule that no case reaches is named in the verdict. Look for the subjects in the ADaM data to see what the rule does with them.
  2. The effect on the primary endpoint, in standard deviations of its test (--effect, default -1; the arms between the reference and the last one have their share). --effect 0 gives data under the null hypothesis. The generator tests the effect itself (Welch's t-test of the change from baseline to the last visit, observed cases; the same numbers as R's t.test) and prints the p-value: the default is significant for every seed tried. The primary test is the one whose name shares the most words with the primary endpoint of the plan, or --endpoint.

sapling dummy check (after sapling run, or sapling adam data --with-deps) reads the analysis data (out/adam/data/*.xpt; no R) and says what each rule did with its designed subjects, against what the generator knows of them (sdtm/dummy.json: an event recorded as 2025-01 started on study day 2). The report is out/dummy/check.md; each row is

The guards: sapling dummy never replaces data it did not make (a sdtm/ with transport files and no DUMMY.md is refused); sapling package refuses dummy data. Limits: a domain without a generator (CM, MH, the trial design, the supplemental qualifiers) and a findings domain whose metadata list no test codes are not made, and the verdict says so; the visits are the scheduled ones the metadata name (Screening, Baseline, Week n).

The omakase defaults#

TopicThe defaultWhere it comes from
populationsITT (randomized: ADSL.ITTFL), SAF (randomized and dosed: SAFFL), EFF (dosed, with a value of each primary endpoint after baseline: EFFFL) when the protocol names an efficacy population, the completers of Week n (COMPnnFL) when it names them; PP as an open decisionthe protocol's sentences (sapc init)
treatment columnsefficacy and the subject tables by the planned treatment (TRT01P, ICH E9 5.2.1); safety by the actual one (TRT01A)
TRT01PNthe planned dose of the arm when each arm has one (the highest EX.EXDOSE of its subjects), else 1, 2, ... in the protocol's orderthe data
estimandper objective (ICH E9(R1)): the population, the treatments, the variable, the protocol's intercurrent events and a permanent discontinuation under treatment policy; it names the primary analysisthe USDM estimand
first and last dosethe first and the last exposure record (EX)
baselinethe last non-missing value on or before the day of the first dose
visit windowsby study day, from the protocol's schedule (7 days a week after the first dose): Baseline day 1 or before; each visit to the midpoint between its target day and the next one; the last open-ended. ANL01FL: in a window, the value closest to the target day (the earlier of two). For an endpoint, the visits at which it is measured (the data)the USDM encounters
change from baselineCHG and PCHG after baseline
missing dataLOCF only when the protocol says so (a .impute div with method="LOCF")the protocol's words
primary modelANCOVA of the change with the baseline (ANOVA of the value for a score without baseline), the pooled site when the protocol's model has the investigator, the treatment as its number (the test of linear dose response with doses; the treatment test with two arms)the protocol
pooled sitesa site with fewer than n patients in a group pooled, n from the protocolthe protocol
age groups<65, 65-<75, >=75 (ICH E7)
TEAEon or after the first dose; the first occurrence flags
tests on baseline and safetyonly those the protocol names (ICH E9: baseline tests are not informative after randomization)the protocol
the standard table set14.1.1 populations, 14.1.2 disposition, 14.2.1 demographics, 14.3.x the primary analysis of each primary endpoint, 14.4.1 exposure, 14.5.1 TEAE overview, 14.5.2 TEAE by SOC and PT, 14.5.3 SAE, 14.6.1 labs by visit, 14.6.2 lab shifts, 14.7.1 vital signs (ICH E3 section 14, numbered as the CDISC pilot's study report)
ADaMADSL, ADAE (OCCDS), one BDS per primary endpoint, ADVS, ADLBC and ADLBH, derived in the SAP from SDTM with the ADaM IG's namesthe data
codelistsCDISC Controlled Terminology decodes (SEX)
the stackR, admiral, CDISC ARS, the study dashboard (r-agentic, used in place), Sapling Visual as the editor, the knowledge rules on
the dashboardfilters SEX, AGEGR1, RACE; a data cut on the first dose; menus Efficacy, Safety, Subjects; the roles of the exploratory toolssapling.yml dashboard.filters, or your dashboard.md

sapling check: a compiler with passes#

PassWhat it checks
1 syntaxthe parse of the plan (Markdown) and the Sapling subset: the commands, their arguments and options
2 structurethe sections in the order of the SAP guideline (Gamble et al. 2017); the required sections (objectives, estimands, populations, analysis methods, missing data, multiplicity); unique ids; every reference resolves; every output has an analysis (and so a shell); every analysis a population, data and a method; a primary estimand names its analysis; every citation resolves; no orphan block; the specs against the SAP (a rule of a spec that differs from the SAP; merge conflicts)
3 datathe variables against the derivations, the ADaM IG and SDTM (used before made, made twice), codelist values
4 knowledgethe rules of the knowledge base (ICH E9(R1), Gamble, FDA, EMA findings)
5 chainwhat the chain needs for sapling adam spec and sapling tlf spec

Each pass has its verdict line (PASS, WARN, FAIL) and file:line:col diagnostics with their fix. Exit code 0 (pass), 1 (--strict: warnings are errors, for CI and before approval), 2 (errors). --json for tools. Every step command runs the check first and refuses on errors ("fix these first").

How to change what Sapling made#

THE SAP SAYS WHAT, THE SPEC SAYS HOW. Change a thing at its source:

You want to changeWhereWhoWhat happens
a population, an analysis, a test, a table's rows, an estimand, a derivation rulesap.qmd (Sapling Visual's forms, or the Markdown)the statisticiansapling status marks the steps stale; sapling adam spec / tlf spec merge the change into the specs
a variable's label, length, type; the order of rows; a title; a footnoteadam-spec.qmd, tlf-spec.qmdthe programmerallowed: the products follow the spec; sapling check notes it
a derivation rule, a condition, a method, in the specadam-spec.qmd, tlf-spec.qmdthe programmerthe products follow the spec; sapling check reports "the spec's rule differs from the SAP's" with the exact SAP line; sapling sap sync writes it into the SAP, marked for a person to review; --strict refuses until the SAP and the spec agree
what a rule language cannot saycustom/ (docs/customize.md), declared in the SAP with a .derive div with custom="..."the programmerthe hook is how; the SAP's words are what
a default of the whole company or studystandards/ (below)the standards teamnew blocks and specs use it; the block says <!-- default: company/<template> -->
a generated program or product in out/neverthe next run refuses to overwrite it ("you edited out/...: make the change in the spec")

When the SAP changes after you edited a spec, sapling adam spec (or tlf spec) makes the SAP's new spec and merges it with yours, 3-way (the spec the SAP gave before, yours, the new one): your edits are kept, the SAP's change comes in, and a line both changed is marked as a conflict (<<<<<<< yours (the spec) ... >>>>>>> new (the SAP now)), never resolved silently. With no edit, the products are byte for byte what the SAP alone makes.

Edit anything#

What you want to changeWhereWho
the plansap.qmd: Sapling Visual (forms) or the Markdownstatistician
the ADaM variables (labels, lengths, rules, R)adam-spec.qmdprogrammer
the tables (order of rows, titles, footnotes)tlf-spec.qmdprogrammer
a default for every study of the company~/.sapling/standards/ (or sapling.yml standards:, SAPLING_STANDARDS): defaults.yml, templates/table-<name>.qmdstandards team
a default for one study<study>/standards/ (study > company > omakase)study team
the dashboard (filters, menus)sapling.yml dashboard.filters, or dashboard.md next to the planprogrammer
the engine, the AI provider, the references to compare withsapling.ymlanyone
a step's productnowhere: it is made again; change its source

sapling templates list shows every default and its layer; sapling templates copy defaults or copy table-<name> starts a file to edit. defaults.yml keys: age_groups, tables (the standard set of sapling new), lab_categories.

A table template is Markdown (docs/markdown.md), templates/table-<name>.qmd: the table as the generator writes it, with placeholders {{id}} (the id of the table), {{n}} (its number, 14.5.3), {{itt}}, {{saf}}, {{eff}} (the populations of the plan) and {{row:X}} (the id <id>_X of a row). The comment at its top is left out; the block gets the mark of its layer (<!-- default: study/table-sae -->):

<!-- The template of sapling generate table sae (study), Markdown (docs/markdown.md): {{id}}, {{n}}, ... -->
:::: {.tlf #{{id}} population="{{saf}}"}
**Table {{n}} Incidence of Serious Treatment-Emergent Adverse Events**

::: {.incidence #{{row:SER}} condition='ADAE.TRTEMFL = "Y" and ADAE.AESER = "Y"' rows="soc;pt"}
Serious treatment-emergent adverse events
:::
::::

A table template of the earlier LaTeX form (table-<name>.tex) is not taken. sapling generate refuses the table with a message that says how to replace the template: sapling templates copy table-<name> --force [--to company] writes the built-in table as table-<name>.qmd in the same folder; make the changes of the old template in it again, then remove the old file. sapling new leaves the table out with that note, and sapling templates list marks the file.

The R engine#

The R engine is r-agentic (its R library, its Python tools, its standards, the study dashboard), used in place from its installed location: no copy, and nothing written there. sapling doctor finds it (in this order): SAPLING_ENGINE, sapling.yml engine:, ~/.sapling/engine, r-agentic next to Sapling.

The study folder holds the engine's view of the study in .sapling/: links to the engine's R/, tools/ and standards/, the app's files linked in a real folder .sapling/apps/dashboard/, and the study as the engine reads it (.sapling/studies/<id>/: study.json, data/sdtm linked to sdtm/, the protocol's index, the structured SAP, the specifications, the programs, the engine's output). The study's .Rprofile loads the engine's R packages and library with options(sma.root = ".sapling"); r-agentic's harness finds its root by that option, and its Python tools run with their root set to .sapling (tools/engine_tool.py). R runs only through DataStep Studio for R (dsr start in the study folder, dsr batch, dsr eval), as r-agentic's AGENTS.md asks. r-agentic's own files and proofs are unchanged.

The proofs#

ProofCommandResult
the pilot with the plan in Markdown, against the stored digest of a proven run (tools/pilot_digest.json)zsh tools/pilot_qmd.shnew, check, DEMO decisions, build, run, adam conform, status, dashboard gate, define, package, sap pdf and html by Quarto; DASHBOARD PASS (18,662 numbers); against the digest: the same 7 ADaM data sets (3,678,731 values), 12 listings (5,023 lines), ARD (485,536 CSV values, 1,475 ARS results) and contract; PILOT QMD PASS
the MVP, replayable for a recording (timed transcript steps.tsv, --pause before each step, relative paths)zsh tools/mvp_demo.sh [--pause]9 steps (doctor, new, generate, a real AI request, DEMO decisions, build, status, run, dashboard) in 221 s; DASHBOARD PASS (24,875 numbers); MVP DEMO PASS
the Rails demo: a fresh folder to the dashboardzsh tools/omakase_demo.sh8 sapling commands in 212 s (the AI request about 20 s); 7 ADaM programs and 13 TLF programs BATCH: CLEAN; DASHBOARD PASS, 13 pages, 24,875 numbers equal to the ARD; OMAKASE DEMO PASS
the omakase result against the hand-tuned pilot (pilot-adam.qmd)python3 tools/omakase_compare.py <study>/out/tlf/listings examples/cdiscpilot01/out/adam/results/tlfthe primary analyses equal: ADAS-Cog (11) N 79/81/74, dose-response p = 0.245; CIBIC+ p = 0.960; demographics N 86/84/84/254, all 71 of its numbers; no listing line-equal (other titles, ids, rows); 30-95% of each pilot listing's numbers, every difference with its cause (the age groups, the first dose from EX instead of the Visit 3 date, windows by study day for labs and vital signs, the rows of the overview, no pairwise LS means)
a second protocol (synthetic, examples/synthetic-htn/)sapling new synhtn01 --protocol examples/synthetic-htn/usdm.json --sdtm examples/synthetic-htn/sdtm/sdtm.json, then sapling buildSAPLING CHECK PASS, SAPLING BUILD PASS; then sapling dummy and sapling run: 3 ADaM and 9 TLF programs BATCH: CLEAN, SAPLING RUN PASS; Table 14.3.1 (the ANCOVA of the change in systolic blood pressure at Week 12) with its p-value, the same as lm() on the ADaM records
the command linenode --test test/sapling.test.ts (in npm test)new, generate (once), the passes and the structure rules, the steps (fresh, stale, missing, edited), the specs (compare, sync, merge, byte for byte), the templates (Markdown; one of the earlier LaTeX form refused with how to replace it), decide
the writer of the generatorsnode --test test/golden.test.ts test/qmd_study.test.ts test/init.test.ts (in npm test)sapling new, each generate thing and sapc init make what their golden references (test/golden/) hold

The MVP, and after it#

The MVP is the smallest end-to-end product a statistician and a programmer can use: sapling new (the study folder, the omakase SAP, Sapling Visual opens), the SAP edited in Sapling Visual or with sapling ai, sapling generate, the step commands with sapling status and sapling doctor, adam-spec.qmd made from the SAP with a basic grid in the editor (Sapling: Open ADaM Spec Grid: filter, sort, inline edit of the label, the length and the rule, the mark of a rule that differs from the SAP with the SAP line to write), the programs made from it, and the pilot from an empty folder to DASHBOARD PASS in 8 commands.

Built beyond the MVP and kept (tested in test/sapling.test.ts, small and self-contained): tlf-spec.qmd, sapling sap sync, the 3-way merge of a spec after an SAP change (conflicts marked), the company and study template layers (standards/, sapling templates), sapling export adam-spec --csv.

After the MVP (not built):