Skip to contents

Every static-analysis limitation, manifest validation issue, and inventory-resolution edge case is reported under a stable warning code. Codes never change wording in a way that breaks downstream tooling, never get renumbered, and retired codes are never reused. This vignette is the canonical reference.

Warnings surface through svt_warnings():

svt_warnings(graph)        # graph-level (SVT-W001..010)
svt_warnings(features)     # manifest issues (SVT-W101..105)
svt_warnings(inventory)    # inventory issues (SVT-W201..206)

Each warning row carries code, file, line, col, and message. Inventory warnings additionally carry the owning feature name.

Graph warnings (SVT-W001..099)

Static-analysis limits surfaced during graph construction.

Code Name Triggered by
SVT-W001 Dynamic input ID input[[expr]] with a non-literal key
SVT-W002 renderUI / insertUI source for input definition inputs created at runtime in UI rendering
SVT-W003 Named access into reactiveValues() resolved by name only every rv$name read (best-effort)
SVT-W004 Conditional source() or box::use() inclusion calls inside if/for/while/tryCatch
SVT-W005 Whole-namespace box import (pkg[…]) box::use(pkg[...]) clause
SVT-W006 library / box::use overlap with non-imported reference a file with box::use(pkg[fn]) references pkg::other
SVT-W007 Duplicate file inclusion via source() and box::use() the same file reached by both
SVT-W008 library() inside a function body library() call inside any function
SVT-W009 Metaprogramming construct blocks resolution do.call, eval(parse(...)), etc.
SVT-W010 Output reassigned second and subsequent output$x <- ... for the same x

Manifest warnings (SVT-W101..199)

Issues with features.yml against the graph. By default (lenient = FALSE) any of these aborts the slice; with lenient = TRUE they become warnings and slicing proceeds best-effort.

Code Name Triggered by
SVT-W101 Manifest references unknown node a roots: entry doesn’t resolve to any graph node
SVT-W102 Root claimed by multiple features two manifest features claim the same output/observer
SVT-W103 Unclaimed output a top-level output/observer is not the root of any manifest feature
SVT-W104 Orphan module instance a module_instance node has no matching definition
SVT-W105 risk_classification = not_required without rationale risk_classification: not_required is set without a rationale field

A name collision across features+modules is fatal regardless of lenient (emitted as a raw issue row with code manifest_name_collision).

A note on SVT-W103

The unclaimed-output check fires only when an explicit manifest is provided. The default slicing rule trivially claims every root, so the warning is meaningless without a user-authored manifest. This is the validation-completeness gate: an app with zero SVT-W103 warnings under a manifest has every user-visible behavior claimed by some feature.

Inventory warnings (SVT-W201..299)

Issues with function-origin resolution and category declarations. These appear in per-feature inventories; aggregate them via svt_warnings(inventory).

Code Name Triggered by
SVT-W201 Internal function access (pkg:::fn) a pkg:::fn call site
SVT-W202 Ambiguous function origin multiple loaded namespaces export the same name
SVT-W203 Unresolved function call no resolution rule applies; package recorded as <unknown>
SVT-W204 Transitive function call a function from a transitive dependency is called directly
SVT-W205 Category unset for non-trivially used package a non-trivially used package has no package_categories declaration
SVT-W206 Package version unresolved no renv.lock and no installed copy of the package

Testing warnings (SVT-W301..399)

Issues raised by the testing layer (spec 06). Coverage in this layer means exercised, never correct.

The whole range is live. SVT-W306 is the one exception: it is retired and never emitted.

Code Name Triggered by
SVT-W301 Feature or module has no mapped test no test maps to the subgraph (verification gap)
SVT-W302 Partial coverage some observables or stimuli are unexercised
SVT-W303 Scaffold present but not filled in a generated scaffold still carries its skip() marker
SVT-W304 Orphan test a test maps to no feature, module, or helper
SVT-W305 Unknown @covers target an annotation or manifest tests: entry names something not in the graph
SVT-W306 (retired, never emitted) allocated when the design still generated shinytest2 scaffolds; replaced by SVT-W312
SVT-W307 Test surface changed since covering test surface_hash moved while every covering test file’s md5 did not
SVT-W308 Test surface incomplete a static-analysis blocker (W001/W002/W003/W009) sits in the subgraph
SVT-W309 Scaffold target already exists target = "app" would overwrite an existing test file; skipped instead
SVT-W310 Mapped test failed or absent from results an ingested CI report reports a failure, or has no entry for a mapped test
SVT-W311 verification = not_required without rationale a waiver with no rationale_verification
SVT-W312 Observable is opaque under testServer() a root’s def_call is renderPlot, renderUI, downloadHandler, …

SVT-W301, W302, W303, W304, W305, W307 and W310 need the coverage layer, which runs when svt_validate() finds a test tree (tests = "coverage", the default in that case). SVT-W311 is raised by manifest validation and is fatal unless lenient = TRUE, exactly like its risk-classification twin SVT-W105.

What the discovery layer reads

testthat blocks under the app’s test tree, and nothing else. The harness of a discovered test is testserver or unit — the same two values the scaffold generator emits. There is no browser-harness value and no JS scan: a Cypress spec or a shinytest2 suite attaches through the manifest’s tests: block, where the credit is recorded as a human declaration (link: manifest) rather than a derived fact.

A note on SVT-W301 and the dual gate

SVT-W301 is the verification counterpart of SVT-W103. Neither number means much alone:

SVT-W103  behaviour a user can reach that no feature claims   -> validation gap
SVT-W301  a claimed feature that no test exercises            -> verification gap

Zero of both is a validated surface that is fully claimed and fully exercised.

A note on SVT-W312

opaque is an annotation, never a harness switch. A renderPlot output is readable under testServer() but its value is a display list — not a useful comparison target. The honest assertion is structural; the semantic one belongs in the helper that computed the data, which is exactly where the generated unit stubs point.

Stability

Codes are stable identifiers. They:

  • Never get renumbered.
  • Never get reused (a retired code stays retired forever).
  • Are diffable across runs — auditors can build expectations against the warning surface (e.g., “this feature historically fires SVT-W003 twice; alert if the count changes”).

New codes append to the relevant range. The ranges are deliberately spaced (W001–W099 graph, W101–W199 manifest, W201–W299 inventory, W301–W399 testing) to leave room for additions in each layer without renumbering the others.