pub struct ExecResult {Show 15 fields
pub result_id: String,
pub request_id: String,
pub exec_id: Option<String>,
pub parent_result_id: Option<String>,
pub pc_id: String,
pub exit_code: i32,
pub skipped: Option<bool>,
pub stdout: String,
pub stderr: String,
pub started_at: DateTime<Utc>,
pub finished_at: DateTime<Utc>,
pub stdout_object: Option<String>,
pub stderr_object: Option<String>,
pub manifest_id: Option<String>,
pub collect_object: Option<String>,
}Fields§
§result_id: Stringv0.29 / Issue #19: agent-minted UUID, unique per (Command, PC)
run. Replaces request_id as the projector’s primary key so
broadcast Commands (commands.all / commands.group.X) — where N
PCs share one request_id — finally persist all N results
instead of silently dropping all but the first. Pre-v0.29
agents omit this field; it deserialises as the empty string,
and Self::stable_result_id derives a deterministic UUIDv5
from (request_id, pc_id) so legacy payloads (a) get distinct
ids across broadcast PCs (PC #2’s row stops being dropped) and
(b) get the SAME id on JetStream redelivery (the new ON CONFLICT(result_id) DO NOTHING path correctly dedupes, so
executions.success_count doesn’t double-count across retries).
request_id: StringThe NATS reply token. Still surfaced for joining back to the
kanade run request/reply path. No longer unique across rows
(broadcast Commands share it).
exec_id: Option<String>v0.29 / Issue #19: back-link to executions.exec_id. Copied
from Command.exec_id by the agent. None for ad-hoc
kanade run (no deployment) and for results emitted by
pre-v0.29 agents (decoded via serde(default)).
parent_result_id: Option<String>#955: back-link to the parent run’s result_id for a
finalize: hook’s own result row. Set by the agent to the
triggering run’s result_id so the SPA can link the
<job>__finalize row to (and from) the run whose collect it
cleaned up. None for every ordinary run and every pre-#955
payload (serde(default) keeps older results decodable).
pc_id: String§exit_code: i32§skipped: Option<bool>Whether the agent ran the script, as the agent itself reports it.
Some(true): it published this result instead of running the script because policy (or this OS) said “not now”: deadline / revoke / version-pin / staleness / unsupported-OS. Such a result says nothing about the script’s outcome, so aggregations count it asskipped, never as a failure.Some(false): authoritative “this is not a skip” — the script ran (whatever it exited, including 126 / 127, which a real script under sh returns for “not executable” / “command not found”), or the agent refused the command (EXIT_REJECTED_UNSIGNED). A refusal is deliberately not a skip: “this command was not authorised” is something the fleet’s failure counts must surface.None: the key was absent, i.e. an agent that predates the flag. SeeSelf::is_reported_skipandSelf::skips_check_projectionfor how each consumer reads it.
Agents that know the flag always send it (never null), so absence
means exactly “legacy agent”. The reserved exit codes only say why.
stdout: Stringstdout. Empty string when Self::stdout_object is set — the
agent overflowed the bytes into crate::kv::OBJECT_RESULT_OUTPUT
because the inline payload would have exceeded NATS’s default
max_payload (#227). The backend projector derefs the pointer
before inserting; SQLite still stores the full text inline so
the SPA Activity page reads unchanged.
stderr: String§started_at: DateTime<Utc>§finished_at: DateTime<Utc>§stdout_object: Option<String>Object Store key under crate::kv::OBJECT_RESULT_OUTPUT when
stdout overflowed the agent’s inline threshold (#227). Set to
Some("<request_id>/<pc_id>/stdout") by the agent’s outbox drain; the
backend projector fetches the bytes from that key and uses them
in place of the (empty) stdout field. None for the common
small-stdout case + every pre-#227 payload (serde(default)
keeps older results decodable).
stderr_object: Option<String>Sibling of stdout_object for the stderr stream. Same key
shape (<request_id>/stderr).
manifest_id: Option<String>v0.13: the manifest id that produced this result. Sourced
from Command.id (which is the YAML manifest.id, e.g.
"inventory-hw"). Distinct from the per-deploy UUID stored
in Command.exec_id. The results projector uses this to
look up the manifest’s inventory: hint and upsert
inventory_facts rows for inventory-tagged jobs.
collect_object: Option<String>#219: Object Store key under crate::kv::OBJECT_COLLECTIONS for
the bundle this run collected, when the job carried a collect:
hint and the run succeeded. Set by the agent to
Some("<pc_id>/<job_id>/<rfc3339>.zip") after it zips the
script’s listed files and uploads the archive. None for every
non-collect job + every pre-#219 payload (serde(default) keeps
older results decodable). The SPA Collect page lists / downloads
these straight from the bucket.
Implementations§
Source§impl ExecResult
impl ExecResult
Sourcepub fn stable_result_id(&self) -> String
pub fn stable_result_id(&self) -> String
Return the result_id if the agent supplied one (v0.29+
payloads always do), otherwise derive a stable UUIDv5 from
(request_id, pc_id). The projector calls this before INSERT
so legacy payloads still get a non-empty PK, AND so that
JetStream redeliveries of the same legacy payload hash to the
same id and dedupe via ON CONFLICT. Per-PC fan-out stays
distinct (different pc_id → different hash).
Sourcepub fn is_reported_skip(&self) -> bool
pub fn is_reported_skip(&self) -> bool
True when the agent reported this result as a skip. What every
result tally (and execution_results.skipped) counts. A legacy
result (None) is not one: tallies read those by exit code, exactly
as they did before the flag existed.
Sourcepub fn skips_check_projection(&self) -> bool
pub fn skips_check_projection(&self) -> bool
True when this result must be kept out of the check_status
projection because the script never ran (#909). Follows the flag
when the agent sent one. For a legacy result (None) it keeps the
rule the projector applied before the flag existed: every reserved
exit code 122..=127 is dropped. A legacy agent cannot tell its own
skip from a script that really exited 126 / 127, so those real exits
stay dropped for legacy agents too — unchanged behaviour, not a new
guess. (The signature refusal is handled separately, see
Self::is_signature_refusal.)
Sourcepub fn is_signature_refusal(&self) -> bool
pub fn is_signature_refusal(&self) -> bool
True for the agent’s signature refusal (#1165): exit
EXIT_REJECTED_UNSIGNED under the refusal’s derived result_id.
A script that merely exits 123 (xargs does, for one) carries an
ordinary random id and is not a refusal.