--- title: "ssel command-line interface" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{ssel command-line interface} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- The `ssel` command calls the installed SSEL public API and shares its version. Every public function is available by its exact R name. The aliases `model`, `chain` and `post` remain available. The command reads the installed library's API facts. | Work | Operations | | --- | --- | | Complete workflows | `modelPipeline`, `chainPipeline`, `semiSupervisedPipeline` | | Splits, fits and predictions | `buildDataset`, `trainModel`, `trainRegressionModel`, `predictModel` | | OOF diagnostics | `oofEnsemble`, `auditQuantiles`, `auditOverfit` | | Delivery | `aggregateResponses`, `postProcess` | | Chain importance and gates | `extractChainImportance`, `activeByImportance`, `activeByShadow`, `computeActiveByImportance` | | Data utilities | `detectOutliers`, `removeOutliersIQR`, `toNumeric`, `which.nonnum` | `ssel --help` lists operations. `ssel OPERATION --help` renders that function's installed R documentation. `ssel --version` prints `ssel `, the version of the package actually loaded; the command has no version of its own. API access does not imply that every learner, dataset or scientific configuration has been validated. ## Library boundary The adapter owns terminal arguments, JSON/RDS decoding, table schemas, result serialization and exit status. Its private R helpers implement those interface contracts. Modeling, iteration, gates, diagnostics and transformations stay in the installed library. The adapter resolves exported functions and invokes them with their existing arguments; it does not source implementation files or introduce a second scientific pipeline. Project data builders and manifest interpretation remain in the consuming project. A project manifest is not automatically an SSEL argument specification. AR-S2J2J, for example, assembles its domains and datasets before calling SSEL. ## Installation Use a complete source bundle containing `cli/` and `install/`. The R package alone does not install the terminal command. R must be installed. The normal macOS entry installs the package when missing and the CLI under the default prefix `/usr/local`: ```bash sudo bash install/install.sh ``` Under sudo, every R process runs as the validated invoking user; R does not request elevation. The default builds and installs the package from the bundle's `lib/` together with the CLI, even when the installed version is equal or newer; `--tarball` selects a recorded package artifact instead of building the checkout. On Windows the entry is native PowerShell without elevation, with default prefix `%LOCALAPPDATA%\Programs\ssel`: ```powershell powershell -ExecutionPolicy Bypass -File install\install.ps1 ``` Equivalent options are `-Yes`, `-Check`, `-Library`, `-Dependency`, `-Tarball` and `-Prefix`; `-NoPath` prevents a user PATH change. The bundle's `install/USAGE.md` is the installation manual and defines these separate operations. The public operation installs the package and the CLI together; there is no component selection. The examples below therefore send both to temporary destinations, so they leave an existing installation untouched. Run them from the bundle root. They do not alter shell profiles. macOS, in Bash: ```bash work="$(mktemp -d)" library="$work/library" prefix="$work/ssel" mkdir -p "$library" bash install/install.sh --yes --library "$library" --prefix "$prefix" export R_LIBS="$library${R_LIBS:+:$R_LIBS}" export PATH="$prefix/bin:$PATH" ssel --version ``` Windows, in native PowerShell: ```powershell $Work = Join-Path ([System.IO.Path]::GetTempPath()) ([guid]::NewGuid().ToString()) $Library = Join-Path $Work 'library' $Prefix = Join-Path $Work 'ssel' New-Item -ItemType Directory -Path $Library -Force | Out-Null & .\install\install.ps1 -Yes -Library $Library -Prefix $Prefix -NoPath $env:R_LIBS = (@($Library, $env:R_LIBS) | Where-Object { $_ }) -join ';' $env:PATH = (Join-Path $Prefix 'bin') + ';' + $env:PATH & (Join-Path $Prefix 'bin\ssel.ps1') --version ``` Runtime commands never install dependencies. Compatible dependencies visible to R remain available through normal library resolution. The installed payload contains platform launchers under `PREFIX/bin`, the runtime under `PREFIX/libexec/ssel`, an uninstaller and an ownership receipt. The launchers resolve R when they run; the receipt records the R that installed them as history, not as the interpreter to reuse. It works after the source bundle is removed. Existing CLI paths and linked `bin`/`libexec` directories cause a conflict. On Windows use `ssel.ps1` when the working directory is a UNC path; cmd.exe cannot preserve a UNC working directory. To remove the temporary CLI, after using it: ```bash bash "$prefix/libexec/ssel/install/uninstall.sh" --prefix "$prefix" --library "$library" ``` ```powershell & (Join-Path $Prefix 'libexec\ssel\install\uninstall.ps1') -Prefix $Prefix -Library $Library ``` A default macOS installation is removed with `sudo bash install/uninstall.sh`; after deleting the checkout, use the installed copy `sudo bash /usr/local/libexec/ssel/install/uninstall.sh`. Removal checks owned file checksums first. Modified or missing files stop removal. R packages and unrelated files remain. ## Inputs and returned objects ```text ssel OPERATION --spec FILE.json|FILE.rds [--output RESULT.rds|RESULT_DIR] [--seed N] [--root DIR] ``` A JSON specification is one object with exact R argument names. Omitted arguments retain R defaults. Scalars and declared vector arguments become R values/vectors; nested objects remain lists. A JSON `null` supplies `NULL`. Null elements within a vector become missing values. Empty character vectors and nested gate histories are supported. The exact texts `"Inf"`, `"-Inf"` and `"NaN"` decode to their numeric values only where the argument default is numeric; anywhere else they remain ordinary strings. `--seed N` seeds the R process before the operation, so independent invocations of stochastic learners are reproducible. An RDS specification is a plain named list of arguments. It preserves native R types, tables, factors, empty vectors, nested lists, `NULL`, `NA`, `NaN` and infinities. In JSON, any argument can instead be `{"$rds":"value.rds"}` to read that native object. Values are passed as data, without evaluating stored expressions. Table arguments `DT`, `.data`, `importanceDT` and `IMP` also accept `{"$csv":"table.csv","colClasses":{"column":"character"},"na.strings":["NA"]}`. Declare every CSV column and its class, and the exact missing-value convention. Use RDS when factor levels or other R attributes matter. Data-returning functions require `--output`. This includes `trainRegressionModel` in both modes and the complete semi-supervised history and promotion return. With an `.rds` path the full result, including an invisible R return, is saved as RDS. With a path without extension, a new directory is created holding `result.json` — the result's shape and types, with `NA` as null and `NaN`, `Inf` and `-Inf` as their exact texts — plus one CSV per table with column classes and factor levels recorded in the manifest. Classes without a portable form refuse the conversion; use `.rds` for them. The six file-oriented operations `buildDataset`, `trainModel`, `modelPipeline`, `chainPipeline`, `aggregateResponses` and `postProcess` allow output to be omitted. Outputs must be new paths in an existing directory; this restriction applies only to `--output`, not to files written by the library. The six-operation classification controls whether `--output` is optional; other operations also write files, as described below. `--output` saves the function's return **after the operation runs**. It does not select or redirect the operation's working directories. For example, `trainRegressionModel` requires it even with `TRAIN = TRUE`, which writes models and returns `NULL`. Changing `--output` and invoking the command again executes the operation again; it does not convert a previously saved result. `--root DIR` selects an existing project directory. Relative paths resolve from that directory: the specification itself, its CSV/RDS references, operation arguments and `--output`. Without `--root`, they resolve from the **working directory**. Moving the specification does not change that base; absolute paths remain absolute. For example, from outside a project: ```sh ssel toNumeric --root /path/to/project --spec numeric.json --output numeric-result.rds ``` The selected root is also the working directory seen by the library's existing validation of destinations that a pipeline may wipe. ## Files written by each operation The paths below are arguments inside the specification, resolved from `--root` or the working directory as described above. A name such as `.path.export` is a caller-selected directory, not a fixed `data/export` location. The `model`, `chain` and `post` aliases have the effects of `modelPipeline`, `chainPipeline` and `postProcess` respectively. ### Splits, models, diagnostics and delivery Here ``, `` and `` stand for the selected names. These functions do not recursively clear their output directories. Files they write can replace existing files at the same paths; skipped cells can leave earlier files in place. | Operation | Files read | Files written and rerun behavior | | --- | --- | --- | | `buildDataset` | Dataset CSVs in `.path.input`. | Writes `__TRAIN.csv`, `_TEST.csv` and `_TRAIN_ids.csv` in the existing `.path.train` directory. Replaces the splits it emits; it does not create that directory or remove obsolete splits. | | `trainModel` | TRAIN splits in `.path.train`, and cached models/hashes in `.path.model`. | Calls `trainRegressionModel` with `TRAIN = TRUE`: writes model/hash pairs in `.path.model`. Creates missing `.path.model`, `.path.metrics` and `.path.response` directories when their parents exist; this training step does not emit metrics or predictions. `OVERRIDE` follows the cache rule below. | | `trainRegressionModel` | With `TRAIN = TRUE`, TRAIN splits in `.path.data` and cached models/hashes. With `TRAIN = FALSE`, TRAIN/TEST splits, models and metrics. | With `TRAIN = TRUE`, saves `.path.model/__.Rds` and its `.Rds.hash` companion. With `TRAIN = FALSE`, writes `_.csv` in `.path.metrics` and `.path.response`. The needed destination directories must already exist. | | `predictModel` | Splits in `.path.train`, models in `.path.model`, metrics/response CSVs, and `.path.summary/prediction_quantiles.csv` from `auditQuantiles`. Optionally `.file.index`. | Runs `trainRegressionModel` with `TRAIN = FALSE`, then replaces `.path.summary/metrics.csv`, `response_long.csv` and nonempty `.path.export/.csv` deliveries. Creates missing summary/export directories when their parents exist; metrics/response directories must already exist. When `.file.index` exists, it also joins location data into **every CSV in summary/export containing the index key**, rewriting those files. | | `auditQuantiles` | Models in `.path.model`, TRAIN splits/ID sidecars in `.path.train`, and `.path.datasets` for ID recovery when a sidecar is absent; existing summary residuals if present. | Creates `.path.summary` as needed. Rewrites `residuals_oof.csv`, retaining previous rows for responses absent from the reconstructed batch, then recomputes `prediction_quantiles.csv` from the combined residuals. No reconstructed rows is an error before either CSV is written. | | `auditOverfit` | Models in `.path.model` and TRAIN splits in `.path.train`. | Creates `.path.summary` as needed and writes `overfit.csv` for the reconstructed audit. This replaces the table rather than merging responses. If the audit returns a column-less empty table, `fwrite` does not replace an earlier file; read the warnings before treating that file as a current result. | | `aggregateResponses` | `.path.summary/{response_long,metrics,prediction_quantiles,residuals_oof}.csv`, declared dataset CSVs in `.path.datasets`, and earlier `Yo.csv`/`Y.csv` when present. | Rewrites `.path.export/Yo.csv`, retaining responses absent from the new prediction rows. Writes `.path.export/X.csv` and `Xo.csv` from the last declared dataset whose file exists; with no dataset file, warns and leaves earlier X files in place. The export directory must exist. When `.path.iter` is supplied, creates it as needed and merges/replaces response columns in `Y.csv`. | | `postProcess` | `.path.export/Yo.csv`, `.path.summary/{response_long,residuals_oof,metrics,prediction_quantiles}.csv`, and `locationSources`. Their defaults are `data/domains/index.csv`, `.path.export/X.csv` and `.path.export/Xo.csv`. | Writes or replaces `.path.export/predictions.csv` and `audit.csv`. It reads but does not rewrite `Yo.csv` or the summaries. | In training mode, `OVERRIDE = FALSE` skips a model only when both its RDS and hash file exist and the stored hash matches the sorted predictor names. This is a **feature-name check**, not a check of training values, tuning or all model settings. A missing or different hash triggers fitting and may replace the model. `OVERRIDE = TRUE` requests fitting regardless of that cache; a failed fit is reported and does not replace the existing model. ### Workflow cleanup and iteration state `modelPipeline` (`model`) reads `.path.datasets`, rebuilds splits in `.path.train` in every mode, and uses the corresponding destinations from the table above. Its cleanup policy is: | `mode` | With `wipe = NULL` (default) or `TRUE` | Work after cleanup | | --- | --- | --- | | `full` (default) | Recursively removes `.path.train`, `.path.metrics`, `.path.response`, `.path.summary`, `.path.model` and `.path.export`. | Rebuilds splits, fits, audits, predicts and aggregates. | | `train` | Recursively removes `.path.train`, `.path.metrics`, `.path.response` and `.path.summary`; retains model/export directories. | Rebuilds splits and fits under `OVERRIDE = FALSE`. | | `predict` | Recursively removes `.path.train`, `.path.metrics`, `.path.response` and `.path.summary`; retains model/export directories. | Rebuilds splits, audits existing models, predicts and aggregates. | With `wipe = FALSE`, none of those directory trees is removed, but individual split/product files can still be replaced or merged. Training then uses `OVERRIDE = FALSE`. Missing destination directories are created recursively. An optional `.path.iter` receives `Y.csv` during aggregation; this operation does not clear that directory. `modelPipeline` does not call `postProcess`. `chainPipeline` (`chain`) uses the same dataset and output arguments plus `.path.iter`. A fresh chain (`resume = FALSE`, the default) recursively removes `.path.iter` and runs a full model pass. Each Jacobi iteration runs another full pass; each Gauss-Seidel sweep clears the six model output directories once, then accumulates its response substeps without clearing them again. The chain writes augmented `datasets_/`, `Y.csv`, `Y.csv`, `Yo.csv`, `metrics_.csv`, `summary_/` snapshots and `convergence.csv` under `.path.iter`. Summary snapshots replace the same snapshot directory; convergence rows are appended for continued iterations. `resume = TRUE` continues only when both `.path.iter/convergence.csv` and `.path.iter/Y.csv` exist. If either is absent, it takes the fresh-chain path, including deletion of `.path.iter`. If both exist but `.path.summary/metrics.csv` is missing, it stops instead. Later continuation and final assembly also consume the saved snapshots. Resume still replaces the model-stage outputs as iterations run; it is not an append-only operation. At completion, `Yo.csv` and the five summary CSVs are assembled from the selected iteration for each response. Models and `.path.iter/Y.csv` retain the last iteration, not that assembled per-response selection. `semiSupervisedPipeline` reads `.path.datasets` and writes under `.path.work/round/`. Before **every fit round**, including its initial fit and a fit restoring accepted pseudo-labels after rejection, it recursively removes that `round` directory. It recreates `datasets`, `train`, `model`, `metrics`, `response`, `summary` and `export` there, then re-emits final predictions over the original prediction pool. It does not retain a separate product tree per round; `--output` saves its returned history and promotions. When `.path.iter` is supplied, the function reads its `metrics_.csv` files and selects `datasets_/` there as its source instead of the supplied `.path.datasets`. That iteration directory is an input to this operation. The workflows validate paths before their recursive cleanup: a wipe target cannot coincide with, contain or lie inside the source datasets directory, or coincide with or contain the working directory. Repeated canonical targets are also rejected where several destinations are checked together. This guard is not a general no-overwrite policy for the lower-level writers or for `wipe = FALSE`. ### Operations without library-managed file output `oofEnsemble` reads models, TRAIN splits and ID sidecars, with dataset files as its fallback for ID recovery, and returns the reconstructed OOF table. `extractChainImportance` and `computeActiveByImportance` read models from `pathModel`. `activeByImportance`, `activeByShadow`, `detectOutliers`, `removeOutliersIQR`, `toNumeric` and `which.nonnum` operate on supplied objects. These nine operations do not write library-managed product files; the CLI writes their return to the required `--output`. This does not mean they all avoid fitting: `removeOutliersIQR` fits a model in memory. ## Executable example After installation, the following R block creates a fresh directory and complete specifications from the public chain example. It only prepares inputs; the subsequent commands run the installed CLI. The fixture seed and learners are teaching choices, not scientific recommendations. ```{r prepare-cli-example, eval = FALSE} Root <- tempfile("ssel-cli-example-") dir.create(file.path(Root, "datasets"), recursive = TRUE) setwd(Root) Data <- data.frame( SampleID = c(paste0("S", 1:18), "P1", "P2"), set = c(rep("train", 18), "predict", "predict"), x = 1:20, z = rep(c(0, 1), 10), targetA = c(1, 2.4, 1.8, 3.7, 2.9, 5.1, 4, 6.2, 5.4, 7, 6.1, 8.3, 7.2, 9.1, 8, 10.5, 9.4, 11.2, NA, NA), targetB = c(2.2, 1.3, 3.6, 2.7, 4.1, 3.8, 5.7, 4.6, 6.5, 5.9, 7.8, 6.4, 8.6, 7.5, 9.9, 8.7, 10.4, 9.6, NA, NA) ) utils::write.csv(Data, "datasets/demo.csv", row.names = FALSE, na = "") Spec <- list( .path.datasets = "datasets", .path.train = "train", .path.model = "model", .path.metrics = "metrics", .path.response = "response", .path.summary = "summary", .path.export = "export", key = "SampleID", responses = c("targetA", "targetB"), datasets = "demo", methods = "lm", seed = 2026, features = c("x", "z") ) jsonlite::write_json(Spec, "model.json", auto_unbox = TRUE, pretty = TRUE) Spec$.path.iter <- "iter" Spec$maxIter <- 1 Spec$tol <- 0 Spec$patience <- 2 Spec$sweep <- "jacobi" jsonlite::write_json(Spec, "chain.json", auto_unbox = TRUE, pretty = TRUE) jsonlite::write_json(list( .path.summary = "summary", .path.export = "export", key = "SampleID", responses = c("targetA", "targetB"), location = c("x", "z"), locationSources = "datasets/demo.csv" ), "post.json", auto_unbox = TRUE, pretty = TRUE) jsonlite::write_json(list(x = c("1,25", "1 234", "x")), "numeric.json", auto_unbox = TRUE) saveRDS(list( .path.datasets = "datasets", .path.work = "semi", key = "SampleID", responses = c("targetA", "targetB"), datasets = "demo", methods = c("lm", "glm"), seed = 2026, tau = 0.1, maxRounds = 1, epsRevert = Inf, promotionCap = 0.5 ), "semi.rds") Root ``` From that printed directory, run these commands in order. The chain replaces the model-stage products using its own documented fresh-run policy. ```sh ssel toNumeric --spec numeric.json --output numeric-result.rds ssel modelPipeline --spec model.json ssel chainPipeline --spec chain.json ssel postProcess --spec post.json ssel semiSupervisedPipeline --spec semi.rds --output semi-result.rds ssel semiSupervisedPipeline --spec semi.rds --output semi-portable ``` The last command executes the semi-supervised pipeline again, replacing `semi/round/`, and saves that run's return in portable form: a new `semi-portable/` directory with `result.json` and one CSV per table. Run it only if this second execution is intended. Back in R in the same directory, inspect the complete returned objects: ```{r read-cli-results, eval = FALSE} readRDS("numeric-result.rds") readRDS("semi-result.rds")$history jsonlite::read_json("semi-portable/result.json")$result$kind # "list" utils::read.csv("semi-portable/history.csv") utils::read.csv("export/predictions.csv") utils::read.csv("semi/round/export/Yo.csv") ``` ## Effects and failures Choose `--output` separately from the paths the operation creates or clears. The CLI checks this return destination before calling the function, then refuses to save the return if the function has created that path. An existing parent checked before execution can also be removed by workflow cleanup. The CLI has no dry-run mode, transaction or rollback: a library error can leave partial products, and a serialization error happens after the library has run. Portable serialization can also leave a partially written result directory. Reinvoking the operation to change output format repeats its library effects. Malformed specifications, unknown or duplicate names, missing required arguments, invalid output destinations and library errors exit 1 with their cause. Warnings remain visible. A successful exit does not prove that every model cell had enough data to fit. Learner dependencies and scientific validation remain the public library's contracts.