#!/bin/bash
# bullpen-hand — carry one artifact from one pen container to another.
#
# WHY
# ---
# An artifact produced in one container has no way to reach another participant.
# It bit three times on 2026-08-09, the last one in the review itself:
#
#   "Vorab, und das ist Befund 1: Ich habe die gelieferte Datei nicht gelesen.
#    Weder /tmp/opencode/phase_b_acceptance_a2.lua noch /tmp/landed/versions.lua
#    existieren ... Alles Folgende beurteilt die im Auftrag zitierten Zeilen als
#    Zitat, nicht die Datei."                                       -- @reviewer #478
#
# docs/PROZESS.md phase 5 forbids exactly that: "Nicht kuratiert weiterreichen:
# die Artefakte selbst, nicht meine Zusammenfassung." Without a transport, every
# review is a review of a quotation, and the phase is theatre.
#
# WHY ON THE HOST AND NOT IN THE PEN
# ----------------------------------
# Only hertz can see both containers. @foreman deliberately has no shell and
# @herder is not in the roster at all; the containers hold no keys, which is
# their point. So the carrier belongs on the host -- this is the operator half of
# the @quartermaster shape.
#
# WHAT IT DELIBERATELY DOES NOT DO
# --------------------------------
# It does not read, transform, or summarise. It copies bytes and prints the
# sha256 from BOTH ends so the recipient can state, in the room, that it read
# the same file that was produced. A handover you cannot check is a rumour.
#
# WHERE IT WRITES, AND WHY THAT CHANGED
# -------------------------------------
# It used to insist on /tmp, reasoning that /tmp is opencode's project root.
# Since the pi migration on 2026-08-10 the recipients run pi, whose project root
# is ~/<projekt> -- so the rule was guarding the root of a runtime that no longer
# runs, and dropping artifacts NEXT TO the root of the one that does. The target
# directory is now asked of the recipient instead of assumed: pi-web registers
# the project in ~/.pi-web/projects.json, and that path is the answer. /tmp is
# still accepted when named explicitly, because older assignments say /tmp.
#
# AND WHO OWNS THE FILE ON ARRIVAL
# --------------------------------
# incus exec runs as root, so a delivered file arrives root-owned. The agent runs
# as a named user since the migration and cannot read it -- present and unusable,
# which is precisely the failure mode that cost 2026-08-10: a secret, a token, a
# spec directory and a trust file, all there, none readable, each reported as
# "missing". So the file is chowned to the user that actually runs the agent.
set -uo pipefail

VON=${1:?usage: bullpen-hand <from>:<path> <to>[:<path>]     (from may be "host" for hertz itself)}
NACH=${2:?usage: bullpen-hand <from>:<path> <to>[:<path>]     (from may be "host" for hertz itself)}
PEN="incus exec bullpen --"

VQ=${VON%%:*}; VP=${VON#*:}
NQ=${NACH%%:*}
NP=${NACH#*:}
[ "$NP" = "$NACH" ] && NP=""            # Ziel darf den Pfad weglassen

# --- wer laeuft im Ziel, und wo liegt sein Projekt? ------------------------
# Beides wird GEFRAGT, nicht angenommen. Die Vorgabe "agent" spiegelt
# bullpen-pi-migrate; faellt auch die aus, bleibt root.
ziel_nutzer=$($PEN incus exec "$NQ" -- sh -c \
    'grep -h "^User=" /etc/systemd/system/bullpen-lurker.service /etc/systemd/system/pi-web.service 2>/dev/null | head -1 | cut -d= -f2' 2>/dev/null)
ziel_nutzer=${ziel_nutzer:-root}

ziel_heim=$($PEN incus exec "$NQ" -- sh -c \
    "getent passwd $ziel_nutzer | cut -d: -f6" 2>/dev/null)
ziel_heim=${ziel_heim:-/root}

# pi-web REGISTRIERT Projekte, es entdeckt sie nicht -- also steht der Pfad dort
# und muss nicht aus dem Nick geraten werden. Das ist wichtig: @deus' Projekt
# heisst `harvest`, nicht `deus`. Wer aus dem Nick ableitet, liegt bei ihm falsch.
ziel_wurzel=$($PEN incus exec "$NQ" -- sh -c \
    "python3 -c \"
import json, sys
try:
    d = json.load(open('$ziel_heim/.pi-web/projects.json'))
except Exception:
    sys.exit(0)
p = (d.get('projects') or [{}])[0].get('path')
print(p or '')
\"" 2>/dev/null)
# KEIN stiller Rueckfall auf das Heimatverzeichnis. Am 2026-08-12 landete so eine
# Fixture in grinds /root: dort registriert kein pi-web ein Projekt, also war das
# Heim des aufgeloesten Nutzers uebrig -- und das ist root mit 0700. bullpen-coder
# haette sie lesen koennen, der von ihm erzeugte Code nicht: der laeuft unter
# `su nobody`. Beidseitig gleiche Pruefsumme, Meldung "lesbar fuer root", und
# trotzdem unerreichbar fuer den einzigen Prozess, der sie braucht.
if [ -z "$NP" ] && [ -z "$ziel_wurzel" ]; then
    echo "bullpen-hand: $NQ hat keine registrierte Projektwurzel -- Ziel bitte nennen." >&2
    echo "  Kandidaten in $NQ:" >&2
    for k in /var/lib/bullpen/coder /var/lib/bullpen /tmp; do
        $PEN incus exec "$NQ" -- test -d "$k" 2>/dev/null && \
            echo "    $k   ($($PEN incus exec "$NQ" -- stat -c '%U %a' "$k" 2>/dev/null))" >&2
    done
    echo "  Fuer @coder ist es /var/lib/bullpen/coder -- dort liest auch der" >&2
    echo "  Sandkasten-Nutzer nobody, unter dem der erzeugte Code laeuft." >&2
    exit 2
fi
[ -z "$NP" ] && NP="$ziel_wurzel/$(basename "$VP")"

case "$NP" in
    "$ziel_wurzel"/*|/tmp/*|/var/lib/bullpen/*) : ;;
    *) echo "bullpen-hand: Ziel liegt weder in der Projektwurzel des Empfaengers" >&2
       echo "  ($ziel_wurzel) noch unter /tmp -- dort findet $ziel_nutzer es nicht." >&2
       exit 2 ;;
esac

# --- lesen: aus einem Container ODER vom Wirt -----------------------------
# "host"/"hertz"/"-" heisst: die Datei liegt hier. Ohne das musste der Operator
# am 2026-08-11 an bullpen-hand vorbei arbeiten, um ein Review-Paket aus einem
# Repo auf hertz in einen Container zu legen -- also genau die improvisierte
# Bruecke, gegen die dieses Programm geschrieben wurde.
lies_quelle() {
    case "$VQ" in
        host|hertz|-) cat "$VP" ;;
        *)            $PEN incus exec "$VQ" -- cat "$VP" ;;
    esac
}
quell_summe() {
    case "$VQ" in
        host|hertz|-) sha256sum "$VP" 2>/dev/null | cut -c1-16 ;;
        *)            $PEN incus exec "$VQ" -- sha256sum "$VP" 2>/dev/null | cut -c1-16 ;;
    esac
}

TMP=$(mktemp); trap 'rm -f "$TMP"' EXIT
if ! lies_quelle > "$TMP" 2>/dev/null; then
    echo "bullpen-hand: $VQ:$VP nicht lesbar" >&2; exit 1
fi
[ -s "$TMP" ] || { echo "bullpen-hand: $VQ:$VP ist leer" >&2; exit 1; }

$PEN incus exec "$NQ" -- mkdir -p "$(dirname "$NP")" 2>/dev/null
$PEN incus exec "$NQ" -- tee "$NP" < "$TMP" > /dev/null || {
    echo "bullpen-hand: konnte $NQ:$NP nicht schreiben" >&2; exit 1; }
$PEN incus exec "$NQ" -- chown "$ziel_nutzer" "$NP" 2>/dev/null

# Beide Enden nachrechnen. "kopiert" ist keine Zusicherung, "gleiche Pruefsumme"
# schon -- und der Empfaenger kann sie im Raum nennen.
h_von=$(quell_summe)
h_nach=$($PEN incus exec "$NQ" -- sha256sum "$NP" 2>/dev/null | cut -c1-16)
groesse=$(stat -c %s "$TMP")

# Lesbar fuer den, der es lesen soll? Eigentuemer allein genuegt nicht -- ein
# Elternverzeichnis ohne Durchgangsrecht kippt es trotzdem.
if $PEN incus exec "$NQ" -- su - "$ziel_nutzer" -c "cat '$NP'" >/dev/null 2>&1; then
    lesbar="lesbar fuer $ziel_nutzer"
else
    lesbar="NICHT lesbar fuer $ziel_nutzer -- Durchgangsrecht auf dem Elternverzeichnis pruefen"
fi

if [ "$h_von" = "$h_nach" ] && [ -n "$h_von" ]; then
    echo "$VQ:$VP -> $NQ:$NP"
    echo "  $groesse Bytes, sha256 $h_von (beidseitig geprueft)"
    echo "  $lesbar"
    echo "  im Auftrag nennen als: $NP"
else
    echo "bullpen-hand: Pruefsummen weichen ab ($h_von != $h_nach)" >&2
    exit 1
fi
