One row per operation and entry path
The same site can be compliant on one path and defective on another. A shared
formatter that receives a value already rounded half-away is a no-op; the same
formatter receiving an unrounded value is itself the rounding step, and
formatC() and sprintf() are not half-away. Give each
(operation, entry path) pair its own row. A single collapsed verdict for a
shared helper is wrong in one direction or the other.
Split by distinct behavior, not by syntax. Two calls to the same operation on
the same path with the same precision -- both bounds of a confidence interval,
say -- share one row; note that it covers two call sites. Splitting them inflates
the inventory without adding a verdict. Conversely, quantization and display
formatting in the same function are distinct behaviors and get separate rows:
(x * 10) %/% 1 / 10 (Tie) vs paste0(n, "%") (Display) is two rows, not one.
Every in-scope operation gets a row, including compliant and allowlisted ones.
Give the allowlisted helper its own definition-site row (e.g.
R/rounding-helpers.R:10 trunc(abs(x) * scale + 0.5) as a reviewed PASS
with tie-vector evidence), in addition to the per-call-site rows that use it.
Recording it only in the allowlist table drops it out of the count a reader
uses to check your coverage, which is the opposite of what an allowlist is for.
When early rounding and its downstream display are paired (BR-002), mark
Stage FAIL on both rows and state they are one finding -- the early site is
the primary defect, the downstream row carries the same Stage verdict so the
pair stays together when sorted or filtered.
In Coverage, quote the scanner's files_scanned and total_hits verbatim;
do not recount R/ files by hand.
Signed zero is part of BR-001, not a fourth rule. A site that renders -0
fails the tie rule; do not open a separate verdict column for it, and do not
fail a downstream no-op formatter for a signed zero its caller produced --
the finding belongs to the operation that created the value.
Two causes, never one
When a formatting call diverges from policy, say which of these is acting --
usually both, and they can push in opposite directions:
- Tie mode.
base::round() is half-to-even at a true tie.
- Binary representation. Most decimal ties are not stored exactly, so the
result follows the stored value rather than the printed one.
Do not compress this into "the function uses banker's rounding". That claim is
false for formatC() and sprintf(), and it predicts the wrong direction for
half the inputs. At zero decimals every x.5 tie is stored exactly, so cause 1
acts alone; decimals bring in cause 2. This is why the probe, not source
reading, is the evidence.
Also check that no displayed value is a signed zero: a small negative value
formatted at the display precision can render as -0 or -0.0. Probe this
only with in-domain inputs for that entry path. An out-of-domain negative
probe (e.g. negative counts for a rate that takes counts and durations) is
recorded as not scored -- input unreachable -- never as a FAIL. Score FAIL
only when a reachable input can render a signed zero.
Allowlist
An allowlist entry is a classification the rule owner already made, not a
reason to stop looking. An entry needs a reason, a source version, and an
approver. Report the site as a reviewed PASS, keep it visible in Coverage,
and return it to full review if the named source has changed since approval --
otherwise the allowlist quietly becomes a blind spot. Never add, edit, or infer
an entry yourself; proposing one to the rule owner is the most you may do.
First close gaps from the source: NAMESPACE and @export tags settle entry
points, while literal digits selects a probe precision. They do not establish
Display compliance: code describing its own decimals is circular evidence.
Audit every unaffected rule. Infer Display only from non-circular reporting
context; otherwise mark that cell NOT ASSESSABLE, name the missing item, and
deliver the partial report.
Stop and escalate
Stop the whole review only when no trustworthy evidence is obtainable at all:
- the source cannot be read or located;
- no report entry point can be identified, even from
NAMESPACE;
probe-tie-behavior.R reports an unexpected result -- this environment does
not behave as the rule references describe, so no witness from it can be
trusted;
- the target documents a convention that conflicts with the stated policy, so
which rule applies is a question for the rule owner, not for you.
Record the blocker and stop. Everything else is a NOT ASSESSABLE cell in a
report you still deliver.
Target-issue mode
Use this mode only for a user-authorized, evidence-backed FAIL. The issue is
a handoff to an implementer, not another audit: give it one defect cluster,
source anchors, the exact observed/policy values, a non-prescriptive fix
boundary, and executable acceptance tests. Never open an issue merely to
repeat scanner candidates, layout/encoding exclusions, or missing policy.
When NOT to use this skill
Do not use for non-numeric reports, statistical-method validation,
or independent QC replacement. If the user only wants "why do
R and SAS differ in rounding" explained, answer directly. If stage or precision
are out of scope, name the unchecked rules instead of narrowing silently.