#!/bin/sh # Dev genome bootstrap (compose git-init). Runs to completion at every up. set -eu rm -f /tmp/git-init-ready ORIGIN=/will-dev/origin/will.git WORK=/will-dev/work RESOURCES=/will-dev/resources # Agent sees this directory read-only and waits for its marker. Rebuild it so # local project Pi settings never enter will's resource tree. mkdir -p "$RESOURCES" find "$RESOURCES" -mindepth 1 -maxdepth 1 -exec rm -rf {} + mkdir -p "$RESOURCES/.pi/skills" "$RESOURCES/persona" # 1. origin: mirror the committed state of the real workspace if [ ! -d "$ORIGIN/objects" ]; then git clone --bare --quiet /workspace "$ORIGIN" else git -C "$ORIGIN" fetch --prune --quiet /workspace '+refs/heads/*:refs/heads/*' fi # 2. dev workspace: clone once, persists across boots if [ ! -d "$WORK/.git" ]; then git clone --quiet "$ORIGIN" "$WORK" fi # 3. node_modules: one-time copy (293M) if [ ! -d "$WORK/node_modules" ]; then echo 'dev: copying node_modules (one-time)' cp -a /workspace/node_modules "$WORK/node_modules" fi # 4. overlay the dirty workspace state, committed as dev-sync. # Skipped when will left uncommitted work, so nothing is clobbered. if [ -z "$(git -C "$WORK" status --porcelain)" ]; then # Mirror the workspace minus gitignored files: .gitignore is the single # source of truth, so secrets like .env never enter will's genome. rsync # excludes are also delete-protected, keeping dest node_modules/.dev intact. rsync -a --delete --filter=':- .gitignore' --filter='- /.git' \ /workspace/ "$WORK/" git -C "$WORK" add -A git -C "$WORK" -c user.name=dev-sync -c user.email=dev-sync@local \ commit -m 'dev-sync: overlay workspace state at boot' --quiet || true else echo 'dev: work tree dirty, skipping overlay sync' fi git -C "$WORK" log --oneline -2 # Runtime resources come from committed HEAD, never from uncommitted worktree # changes. This mirrors immutable production resources while preserving dirt. git -C "$WORK" archive HEAD persona .pi/skills AGENTS.md | tar -x -C "$RESOURCES" touch "$RESOURCES/.ready" /tmp/git-init-ready # Compose keeps the initializer alive so its healthy state is an unambiguous # dependency barrier. One-shot invocations still exit normally. if [ "${WILL_INIT_STAY_ALIVE:-}" = 1 ]; then exec sleep infinity; fi