use super::Observability;
pub(super) fn classify(field: &str) -> Option<Observability> {
gpu_and_model(field)
.or_else(|| release_and_namespace(field))
.or_else(|| task_and_hooks(field))
.or_else(|| storage(field))
}
fn gpu_and_model(field: &str) -> Option<Observability> {
Some(match field {
"gpu_backend" => Observability::Observed { alt: "rocm" },
"driver_version" => Observability::Observed { alt: "525" },
"compute_mode" => Observability::Observed { alt: "prohibited" },
"rocm_version" => Observability::Unmigrated(
"#403 — the rocm query reports the amdgpu kernel module version, not \
the ROCm stack version; `rocminfo`/`hipconfig --version` would",
),
"cuda_version" => Observability::Unmigrated(
"#403 — the nvidia query reports the driver version; the CUDA toolkit \
version needs `nvcc --version`, which no state query runs",
),
"checksum" => Observability::Observed {
alt: "blake3:0000000000000000000000000000000000000000000000000000000000000000",
},
"quantization" => Observability::Unmigrated(
"#403 — a GGUF file does not report the quantization it was built \
with; the content hash moves but nothing names it",
),
"format" => Observability::Unmigrated(
"#403 — the model query hashes the file and never names its format; \
reading the container magic would",
),
"cache_dir" => Observability::Unmigrated(
"#403 — the model state query stats `path`; nothing asks where the \
cache root is, so a moved cache is invisible",
),
_ => return None,
})
}
fn release_and_namespace(field: &str) -> Option<Observability> {
Some(match field {
"repo" => Observability::Observed {
alt: "paiml/forjar-alt",
},
"binary" => Observability::Observed {
alt: "forjar-alt-binary",
},
"install_dir" => Observability::Observed {
alt: "/tmp/forjar-alt-install-dir",
},
"tag" => Observability::Unobservable(
"an installed binary carries no record of which release tag produced \
it; the version it prints is a different fact",
),
"asset_pattern" => Observability::Unobservable(
"selects WHICH release asset to download; once unpacked the host \
holds a binary, not the glob that chose it",
),
"groups" => Observability::Observed {
alt: "forjar-alt-group",
},
"ssh_authorized_keys" => Observability::Unmigrated(
"#403 — user's state query reports uid/gid/groups/shell/home and not \
the key set; ~/.ssh/authorized_keys is readable and should be",
),
"chroot_dir" | "cpuset" | "overlay_lower" | "overlay_upper" | "overlay_work"
| "overlay_merged" => Observability::Unmigrated(
"#403 — a live namespace exposes these through /proc, the mount table \
and cgroupfs; pepita's state query reports only whether it runs",
),
"recipe" => Observability::Unobservable(
"names the recipe that expanded into the real resources; nothing is \
applied under this name, so no host can be asked about it",
),
_ => return None,
})
}
fn task_and_hooks(field: &str) -> Option<Observability> {
Some(match field {
"output_artifacts" => Observability::Observed {
alt: "/tmp/forjar-alt-artifact",
},
"task_inputs" => Observability::Unobservable(
"declares which files KEY the content-addressed cache; the host holds \
the files, never the fact that they were the inputs",
),
"ambient_inputs" => Observability::Unobservable(
"fingerprint commands whose stdout keys the cache — a probe forjar \
runs, not a state the host retains",
),
"working_dir" => Observability::Unobservable(
"names the directory a command ran in; nothing on the host records \
the cwd of a process that has already exited",
),
"quality_gate" => Observability::Unobservable(
"a pass/fail predicate evaluated during apply; the host keeps no \
record of which gate was applied to it",
),
"health_check" => Observability::Unobservable(
"a liveness probe run on a schedule; the host reports whether the \
service is up, never which probe decided that",
),
"gather" | "scatter" => Observability::Unobservable(
"controller-side file movement between machines; neither end records \
that the copy was declared rather than done by hand",
),
"pre_apply" | "post_apply" => Observability::Unobservable(
"hooks that run around apply; a converged host has no record that a \
hook ran, only of what it did",
),
"script" => Observability::Unobservable(
"names the build to run; the host holds the OUTPUT, and a built \
artifact carries no record of the recipe that produced it",
),
"build_machine" => Observability::Unobservable(
"names WHICH machine performs the build — controller-side routing \
that the deploy target cannot report",
),
_ => return None,
})
}
fn storage(field: &str) -> Option<Observability> {
Some(match field {
"budget_schedule" => Observability::Observed { alt: "weekly" },
"backup_remote" => Observability::Observed {
alt: "altdrive:forjar-alt",
},
"backup_remote_type" => Observability::Observed { alt: "s3" },
"backup_schedule" => Observability::Observed { alt: "weekly" },
"backup_token" => Observability::Observed {
alt: "forjar-alt-token",
},
"backup_source" => Observability::Unmigrated(
"#403 — backup_sync's query reports installed/timer/heartbeat but \
never the declared source roots; the status JSON already carries \
coverage and could carry the root set",
),
"backup_bandwidth_limit" => Observability::Unmigrated(
"#403 — the limit is baked into the sync script, and the query \
digests rclone.conf rather than that script",
),
"archive_dirs" => Observability::Observed {
alt: "forjar-alt-archive-dir",
},
"archive_destination" => Observability::Unmigrated(
"#403 — the archive query reports the SOURCE side only; nothing on \
the NAS is stat'd, so a wrong destination root is unverified",
),
"archive_schedule" => Observability::Unmigrated(
"#403 — nas_archive's query has no timer check at all, unlike \
disk_budget's, so its cadence is unobserved",
),
_ => return None,
})
}