Some checks failed
Merges the Godot port into the reverse-engineering repository, preserving both
histories -- 1019 commits of corpus plus the port's 31, brought in by subtree
merge and then moved into place so git can follow each file across the rename.
The reason is not tidiness. The two-repo split forced the exporter to depend on
the decoders by pinned revision, and that created a whole class of failure that
now disappears: a sha reachable only from a topic branch, orphaned by a
squash-merge, breaking a fresh checkout silently at build time. It also forced a
live read-only mount of one agent's working tree into another's container, which
is why a contract file could move mid-iteration. With a path dependency, a
decoder change and the exporter change it requires land in the same commit or
not at all.
Canary stays separate: it is a fork tracking upstream.
New structure for the long term:
docs/game/ how the game is NAVIGATED -- menus, modals, prompts, alerts,
and in-game flight. Written so nobody rediscovers it. Mostly
open questions on purpose; the in-game tutorials are the
resource for the flight half.
docs/port/MODDING.md
modding as a constraint on the exporter TODAY, not a later
feature: one logical asset in one file (the disc splits nearly
everything, and resolving that is the exporter's job), names a
person recognises, PNG/OGG/OGV/JSON only, base-and-overrides so
re-exporting is always safe, provenance in every file.
data/base + data/mods
generated tree and drop-in overrides, both gitignored
exchange/ transient inter-agent files, deliberately outside history
docs/agents/ the team protocol
Both the README and the navigation doc lead with the correction that cost the
most: the oracle is the real game under Xenia Canary. Reborn's renderer is a
hypothesis under test, it has been wrong, and treating it as ground truth
propagated into three documents and both agents before a human caught it.
Scripted modding stays possible without being built: no screen name is hardcoded
in GDScript and there is no native code in port/, which is what Godot Mod Loader
needs to be able to substitute behaviour later.
73 lines
3.4 KiB
Bash
Executable File
73 lines
3.4 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Bring up the headless display, then hand over.
|
|
#
|
|
# Xvfb and openbox are started as children of PID 1 (tini), NOT of the agent's
|
|
# shell, so they outlive any single command. The RE container learned this the
|
|
# hard way: a display owned by a shell gets reaped when that shell exits, which
|
|
# reads as "Xvfb dies on its own every few minutes".
|
|
set -euo pipefail
|
|
|
|
: "${DISPLAY:=:97}"
|
|
: "${SCREEN_GEOMETRY:=1280x720x24}"
|
|
|
|
if ! xdpyinfo -display "$DISPLAY" >/dev/null 2>&1; then
|
|
Xvfb "$DISPLAY" -screen 0 "$SCREEN_GEOMETRY" -nolisten tcp &
|
|
for _ in $(seq 50); do
|
|
xdpyinfo -display "$DISPLAY" >/dev/null 2>&1 && break
|
|
sleep 0.1
|
|
done
|
|
openbox >/dev/null 2>&1 &
|
|
fi
|
|
echo "[entrypoint] display $DISPLAY ready ($SCREEN_GEOMETRY)"
|
|
|
|
if [ -d /reborn ]; then
|
|
echo "[entrypoint] /reborn mounted read-only — HANDOFF.md is the contract"
|
|
fi
|
|
|
|
# Seed ~/.claude.json from the host's read-only copy, then stamp onboarding as
|
|
# complete. Claude Code re-runs its first-run wizard whenever
|
|
# lastOnboardingVersion differs from the installed version, so a container with a
|
|
# newer Claude than the host stops on the theme picker -- no error, no log line,
|
|
# and an unattended agent sits there forever.
|
|
if [ -f "$HOME/.claude.host.json" ] && [ ! -s "$HOME/.claude.json" ]; then
|
|
cp "$HOME/.claude.host.json" "$HOME/.claude.json" 2>/dev/null || true
|
|
fi
|
|
# Same reason as .claude.json above: `credential.helper=store` rewrites this
|
|
# file by rename-over-target, which fails with EBUSY on a bind mount. Copy it to
|
|
# a writable path; nothing is ever written back to the host's file.
|
|
if [ -f "$HOME/.git-credentials.host" ]; then
|
|
cp "$HOME/.git-credentials.host" "$HOME/.git-credentials" 2>/dev/null || true
|
|
chmod 600 "$HOME/.git-credentials" 2>/dev/null || true
|
|
fi
|
|
|
|
CLAUDE_VER=$(claude --version 2>/dev/null | grep -oE '^[0-9][0-9.]*' || echo 0.0.0)
|
|
python3 /usr/local/bin/seed-claude-config.py "$HOME/.claude.json" "$CLAUDE_VER" \
|
|
"$PWD" "${PROJECT_DIR:-/work}" "$HOME" || true
|
|
chmod 600 "$HOME/.claude.json" 2>/dev/null || true
|
|
|
|
# ── Claude Code ──────────────────────────────────────────────────────────────
|
|
# Without this the loop prompt is handed to `exec` as a command, and the whole
|
|
# markdown file is tried as a filename: exit 126, "File name too long".
|
|
if [ "${SYLPH_AUTONOMOUS:-0}" = "1" ]; then
|
|
# Drop the image's default CMD first, or `claude` is handed the literal string
|
|
# "bash" as its prompt and answers a question nobody asked.
|
|
if [ "$#" -eq 1 ] && [ "$1" = "bash" ]; then
|
|
set --
|
|
fi
|
|
# Remote Control registers the session with the account so the agent can be
|
|
# reached from claude.ai -- the point of a detached run being that nobody is
|
|
# sitting in front of it. The name is passed EXPLICITLY: the flag's value is
|
|
# optional, so a bare --remote-control swallows the /loop prompt after it.
|
|
if [ "${SYLPH_REMOTE:-1}" != "0" ]; then
|
|
set -- --remote-control "${SYLPH_REMOTE_NAME:-sylpheed-port}" "$@"
|
|
echo "[entrypoint] Remote Control as '${SYLPH_REMOTE_NAME:-sylpheed-port}'"
|
|
fi
|
|
# claude-autonomous wraps `claude --dangerously-skip-permissions` in a pty and
|
|
# answers the one-time first-run gates. The Bypass Permissions disclaimer has
|
|
# no config key that skips it, so unattended it hangs forever.
|
|
set -- claude-autonomous "$@"
|
|
echo "[entrypoint] starting Claude Code in $(pwd)"
|
|
fi
|
|
|
|
exec "$@"
|