Files
kernel-agent/bin/ka-status
T
claude-noether 18da673ccc phase 1: promote vb2_dma_resv RFC v2 + add ka-status + ampere as 2nd aarch64 host
Three changes that together flip kernel-agent from spec'd to operational
in the manual-orchestrated form. Real ka-* CLI verbs come in later phases;
this commit gets a first iteration through the pipeline and proves the
flow at the artifact level.

1. Promote vb2_dma_resv RFC v2 series into the scope-tagged tree

Markus iterated v2 locally on boltzmann (kernel-agent-bootstrap dir,
reaching linux-fresnel-fourier pkgrel=14). v2 attaches the producer
fence at device_run in slept-OK context per Dufresne's v1 review on
linux-media. The three patches land under
patches/subsystem/media/videobuf2/dma-resv-release-fence/:

  - 0004 (helper) — opt-in vb2 dma_resv producer-fence helper
  - 0005 (driver opt-in) — hantro device_run attach
  - 0006 (driver opt-in) — rockchip-rga device_run attach

Numbered 4/5/6 because the fresnel build PKGBUILD applies them after
the three 0001/0002/0003 PBP DTS patches; this directory's numbering
follows that apply-order, not the upstream lore series numbering.

README at the scope dir documents fleet eligibility, decision history,
and the v1 → v2 design pivot.

2. Update fleet/fresnel.yaml to include the v2 series

Pre-v2 manifest had a comment block 'Explicitly NOT included … vb2
dma-resv-release-fence … defer until v2 lands'. v2 has landed. Move
those three lines from 'excluded' to 'includes', annotate the decision
inline.

3. README updates

- Build hosts table: add ampere (CoolPi GenBook, RK3588 32GB) as
  secondary aarch64 host. Same uarch as boltzmann, on-demand wake via
  His. Gives the fleet a second native build target for when boltzmann
  is busy (e.g. carrying a firefox-fourier 4h build).
- 'Out of scope this round' bootstrap section: mark vb2_dma_resv as
  resolved 2026-05-15, keep panfrost IOMMU_CACHE deferred.

4. First ka-* CLI verb implemented: bin/ka-status

bash, ~120 lines. Reads fleet/*.yaml manifests, queries Gitea for
open [ka:*] issues, probes each reachable host for the installed
kernel-package version. Read-only — no sudo, no host writes. Picks
GITEA_TOKEN from /opt/herding/etc/claude-identities/<host>.creds or
env override.

Proves the agent's Gitea-API + manifest-parsing skeleton works
end-to-end without committing to a full ka-promote/build/install
implementation. Smoke-tested locally:

  $ bin/ka-status
  kernel-agent status (repo: marfrit/kernel-agent)
  open [ka:*] issues total: 1
  ══ fresnel ══
    manifest: arch=arm64 soc=rockchip/rk3399 board=pinebook-pro
    package:  linux-fresnel-fourier
    installed: host-down            # (fresnel is currently powered off)
    open ka-issues: (none for this host)

No PKGBUILD update in this PR — that lives in marfrit-packages and
ships as a sibling PR (the actual linux-fresnel-fourier-7.0-14 publish).
2026-05-15 15:32:00 +00:00

137 lines
4.6 KiB
Bash
Executable File

#!/usr/bin/env bash
#
# ka-status — per-host kernel-agent state summary.
#
# Reads fleet/*.yaml manifests + queries Gitea for open [ka:*] issues +
# probes each host (where reachable) for the installed kernel-package
# version. Designed to give a first-look "what's the state" before any
# ka-promote / ka-install action.
#
# Usage:
# ka-status # summary across all manifests
# ka-status <host> # detail for one host
#
# Read-only. Never mutates state. No sudo. No SSH-into-host writes.
#
# Phase 1 deliverable. Future ka-* CLI verbs (ka-promote / ka-close /
# ka-install) build on the same Gitea-API + manifest-parsing skeleton.
set -euo pipefail
GITEA_URL="${GITEA_URL:-https://git.reauktion.de}"
REPO="${KERNEL_AGENT_REPO:-marfrit/kernel-agent}"
TOKEN_FILE="${KERNEL_AGENT_TOKEN_FILE:-/opt/herding/etc/claude-identities/noether.creds}"
# Resolve token from per-host claude-identity creds, or env override.
token=""
if [ -n "${GITEA_TOKEN:-}" ]; then
token="$GITEA_TOKEN"
elif [ -r "$TOKEN_FILE" ]; then
token="$(grep -E '^GITEA_TOKEN=' "$TOKEN_FILE" | head -1 | cut -d= -f2)"
fi
# Locate fleet/ — script lives in bin/ next to fleet/.
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
fleet_dir="${script_dir}/../fleet"
[ -d "$fleet_dir" ] || { echo "fleet/ not found relative to $script_dir" >&2; exit 2; }
api_get() {
local path="$1"
local args=(--silent --show-error --max-time 15)
[ -n "$token" ] && args+=(-H "Authorization: token $token")
curl "${args[@]}" "${GITEA_URL}/api/v1/${path}"
}
# Open ka-prefixed issues, JSON array on stdout.
fetch_ka_issues() {
api_get "repos/${REPO}/issues?state=open&type=issues&limit=50" \
| python3 -c '
import json, sys
issues = json.load(sys.stdin)
def kind(t):
if not t.startswith("["): return None
p = t.find("]")
return t[1:p] if p > 0 else None
out = [{
"number": i["number"],
"title": i["title"],
"kind": kind(i["title"]),
} for i in issues if kind(i["title"]) and kind(i["title"]).startswith("ka:")]
print(json.dumps(out))
' 2>/dev/null || echo "[]"
}
# Parse a fleet/<host>.yaml manifest (very-narrow YAML subset).
manifest_field() {
local file="$1" key="$2"
grep -E "^[[:space:]]*${key}:" "$file" | head -1 | sed -E "s/^[^:]*:[[:space:]]*//; s/[[:space:]]+#.*//; s/^[\"']//; s/[\"']$//"
}
manifest_pkgname() {
local file="$1"
awk '/^package:/{p=1; next} p && /^[a-z]/{p=0} p && /^[[:space:]]+name:/{sub(/^[[:space:]]+name:[[:space:]]*/,""); print; exit}' "$file"
}
# Probe a host for installed kernel-package version (best-effort, non-blocking).
probe_installed() {
local host="$1" pkg="$2"
ssh -o ConnectTimeout=3 -o BatchMode=yes -o StrictHostKeyChecking=accept-new \
"${host}.fritz.box" "pacman -Q '$pkg' 2>/dev/null || dpkg-query -W -f='\${Package} \${Version}\n' '$pkg' 2>/dev/null || echo 'host-up:not-installed'" 2>/dev/null \
|| echo "host-down"
}
issues_json="$(fetch_ka_issues)"
# Per-host issue grouping — match on title containing the host name (cheap heuristic;
# proper kernel-agent will tag issues with a host label).
issues_for_host() {
local host="$1"
echo "$issues_json" | python3 -c "
import json, sys
host = '$host'
issues = json.load(sys.stdin)
hits = [i for i in issues if host in i['title'].lower()]
for h in hits:
print(f\" #{h['number']} [{h['kind']}] {h['title']}\")
" 2>/dev/null
}
print_host() {
local file="$1"
local host pkg
host="$(basename "$file" .yaml)"
pkg="$(manifest_pkgname "$file")"
local arch="$(manifest_field "$file" arch)"
local soc="$(manifest_field "$file" soc)"
local board="$(manifest_field "$file" board)"
printf '\n══ %s ══\n' "$host"
printf ' manifest: arch=%s soc=%s board=%s\n' "$arch" "$soc" "$board"
printf ' package: %s\n' "$pkg"
if [ -n "$pkg" ]; then
printf ' installed: %s\n' "$(probe_installed "$host" "$pkg")"
fi
local n=0
while IFS= read -r line; do
[ -z "$line" ] && continue
if [ $n -eq 0 ]; then printf ' open ka-issues:\n'; fi
printf '%s\n' "$line"
n=$((n+1))
done < <(issues_for_host "$host")
[ $n -eq 0 ] && printf ' open ka-issues: (none for this host)\n'
}
if [ $# -ge 1 ]; then
f="${fleet_dir}/${1}.yaml"
[ -r "$f" ] || { echo "no manifest for host '$1'" >&2; exit 2; }
print_host "$f"
else
printf 'kernel-agent status (repo: %s)\n' "$REPO"
printf 'open [ka:*] issues total: %s\n' "$(echo "$issues_json" | python3 -c 'import json,sys; print(len(json.load(sys.stdin)))')"
for f in "$fleet_dir"/*.yaml; do
[ -e "$f" ] || continue
print_host "$f"
done
fi