Garak initializes a run by recording start time, loading base config and logging before parsing CLI arguments, then assigning a UUID4 run ID and opening a line-buffered JSONL report file (with filename controlled by report_prefix and report_dir config). When a run ends, garak writes a completion entry to the report, generates an HTML digest in the same directory, and records errors to garak.log; hint messages appear with 25% probability in CLI output but always go to the log.
garak/cli.py records _config.transient.starttime (a datetime.datetime) and _config.transient.starttime_iso (its ISO string) at the very beginning of main(), before any other initialization.[1] garak/cli.py loads the base config (_config.load_base_config()) and starts logging before parsing CLI arguments, so the logging system is available for argument-parsing errors.[1] garak/command.py's start_logging() retrieves the log filename from _config.transient.log_filename (set during config loading) rather than constructing it itself.[2] garak/command.py's start_run() assigns a UUID4 run ID — not UUID1 — to _config.transient.run_id, specifically to avoid leaking host information.[2]
When _config.reporting.report_prefix is set, start_run() names the report file <prefix>.report.jsonl; otherwise it defaults to garak.<run_id>.report.jsonl.[2] If _config.reporting.report_dir is a relative path, start_run() resolves it relative to _config.transient.data_dir; if the directory cannot be created a PermissionError is raised with a descriptive message.[2] The report file is opened with buffering=1 (line-buffered) and UTF-8 encoding, so each JSONL line is flushed immediately to disk.[2] The config snapshot written by start_run() serializes only values whose Python type is in (str, int, bool, dict, tuple, list, set, type(None)); other types — file handles, custom objects — are silently skipped.[2]
garak generates 10 responses per prompt by default; the per-probe result row shows total generations and OK generations (e.g., 840/840).[3] Hint messages printed during a run have a 25% probability of appearing in CLI output (HINT_CHANCE = 0.25 in garak/command.py); they are always sent to the log.[2] garak/command.py's start_run() shows a hint recommending --config full when _config.system.lite is active and no probes, interactive mode, or list/info commands are specified.[2] garak/command.py's deprecation_notice() always prints the deprecation message to stdout (prefixed with ✋), regardless of the HINT_CHANCE random gate used by hint().[2]
garak/command.py's end_run() writes a completion entry to the JSONL report, closes both the report file and the hit log file, then generates an HTML digest via write_report_digest(); if the digest fails, the error is logged and printed but not re-raised as fatal.[2] The HTML digest file is placed in the same directory as the .jsonl report, with the .jsonl extension replaced by .html.[2] garak logs errors to garak.log and records full run details in a .jsonl file reported at the start and end of analysis; analyse/analyse_log.py summarizes the probes and prompts with the most hits.[3]
garak/command.py's print_plugins() raises ValueError if the prefix argument is not a member of PLUGIN_TYPES, preventing display of unsupported plugin categories.[2] When selected_plugins is provided but the prefix is not found in the first element of that list, print_plugins() prints "No {prefix} match the provided filter" and returns early instead of showing all plugins.[2] print_plugins() shows a plain-text list by default (verbose=0) and renders a markdown table when verbose >= 1 and the plugin type has defined columns in _PLUGIN_TABLE_COLUMNS.[2] In plain-text output, print_plugins() uses 🌟 to mark module-level entries (those with no . in the name) and 💤 to mark inactive plugin classes.[2] The verbose markdown table for --list_probes -v includes tier (rendered as the Tier enum name) and description (truncated to 80 characters) columns; no other plugin types have extra columns defined yet.[2] garak/command.py's _tier_name() converts an integer tier value to its Tier enum name string, returning an empty string for invalid or None values rather than raising.[2] garak/command.py's _truncate() shortens a string to 80 characters maximum, appending a … character (not ...) when truncation occurs.[2] garak/command.py's plugin_info() always prints the description field first before iterating remaining fields, giving it visual priority in CLI output.[2]
Sources