From 96fc1d3879fe4d3d30606ef744fdaa27950aa901 Mon Sep 17 00:00:00 2001 From: Sam & Claude Date: Mon, 15 Jun 2026 16:47:38 +0200 Subject: [PATCH 1/3] =?UTF-8?q?feat(build):=20enforce=20clean=20repos=20on?= =?UTF-8?q?=20release=20builds=20=E2=80=94=20gate=20now=20covers=20iso/col?= =?UTF-8?q?ibri/zot?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.sh | 86 ++++++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 78 insertions(+), 8 deletions(-) diff --git a/build.sh b/build.sh index 57b6d5b4..079a96f1 100755 --- a/build.sh +++ b/build.sh @@ -102,14 +102,7 @@ if [ -n "${SSH_PUBLIC_KEY:-}" ]; then fi if [ "${BUILD_CHANNEL}" = "release" ]; then - case "${CLAWDIE_REF}" in - v[0-9]*.[0-9]*.[0-9]*) ;; - *) - echo "ERROR: release builds must pin a Clawdie-AI tag with --clawdie-version X.Y.Z" - echo " Current Clawdie ref: ${CLAWDIE_REF}" - exit 1 - ;; - esac + check_release_gate fi # The ISO carries its own product version (ISO_VERSION in build.cfg). It does not @@ -467,6 +460,69 @@ is_pinned_clawdie_ref() { printf '%s' "$_ref" | grep -Eq '^[0-9a-fA-F]{40}$|^v[0-9]+\.[0-9]+\.[0-9]+$' } +# Release builds require clean, committed sources across the whole stack. +# A release ISO must be a reproducible artifact — no uncommitted local patches. +check_release_gate() { + case "${CLAWDIE_REF}" in + v[0-9]*.[0-9]*.[0-9]*) ;; + *) + echo "ERROR: release builds must pin a Clawdie-AI tag with --clawdie-version X.Y.Z" + echo " Current Clawdie ref: ${CLAWDIE_REF}" + exit 1 + ;; + esac + + _release_errors=0 + + # Clawdie-AI local checkout + resolve_clawdie_ai_repo + if command -v git >/dev/null 2>&1 && git -C "${_resolved_clawdie_ai_repo}" rev-parse --git-dir >/dev/null 2>&1; then + if ! git -C "${_resolved_clawdie_ai_repo}" diff --quiet 2>/dev/null || ! git -C "${_resolved_clawdie_ai_repo}" diff --cached --quiet 2>/dev/null; then + echo "ERROR: release builds require a clean clawdie-ai repo" + echo " Uncommitted changes detected in: ${_resolved_clawdie_ai_repo}" + _release_errors=$((_release_errors + 1)) + fi + fi + + # ISO repo itself + if command -v git >/dev/null 2>&1 && git -C "${SCRIPT_DIR}" rev-parse --git-dir >/dev/null 2>&1; then + if ! git -C "${SCRIPT_DIR}" diff --quiet 2>/dev/null || ! git -C "${SCRIPT_DIR}" diff --cached --quiet 2>/dev/null; then + echo "ERROR: release builds require a clean clawdie-iso repo" + echo " Uncommitted changes detected in: ${SCRIPT_DIR}" + _release_errors=$((_release_errors + 1)) + fi + fi + + # Colibri + if [ "${FEATURE_COLIBRI:-NO}" = "YES" ]; then + resolve_colibri_paths + if command -v git >/dev/null 2>&1 && git -C "${_resolved_colibri_repo}" rev-parse --git-dir >/dev/null 2>&1; then + if ! git -C "${_resolved_colibri_repo}" diff --quiet 2>/dev/null || ! git -C "${_resolved_colibri_repo}" diff --cached --quiet 2>/dev/null; then + echo "ERROR: release builds require a clean colibri repo" + echo " Uncommitted changes detected in: ${_resolved_colibri_repo}" + _release_errors=$((_release_errors + 1)) + fi + fi + + # Zot (agent binary) + if [ "${COLIBRI_STAGE_AGENT:-YES}" = "YES" ]; then + resolve_zot_paths + if command -v git >/dev/null 2>&1 && git -C "${_resolved_zot_repo}" rev-parse --git-dir >/dev/null 2>&1; then + if ! git -C "${_resolved_zot_repo}" diff --quiet 2>/dev/null || ! git -C "${_resolved_zot_repo}" diff --cached --quiet 2>/dev/null; then + echo "ERROR: release builds require a clean zot repo" + echo " Uncommitted changes detected in: ${_resolved_zot_repo}" + _release_errors=$((_release_errors + 1)) + fi + fi + fi + fi + + if [ "${_release_errors}" -gt 0 ]; then + echo "ERROR: release build aborted — ${_release_errors} dirty repo(s). Use BUILD_CHANNEL=dev for iteration builds." + exit 1 + fi +} + write_build_manifest() { _manifest_path="$1" _iso_repo_commit="unknown" @@ -500,6 +556,19 @@ write_build_manifest() { fi fi fi + # Zot provenance (agent binary built from source — record if repo is dirty). + _zot_dirty="null" + if [ "${FEATURE_COLIBRI:-NO}" = "YES" ] && [ "${COLIBRI_STAGE_AGENT:-YES}" = "YES" ]; then + resolve_zot_paths + if command -v git >/dev/null 2>&1 && git -C "${_resolved_zot_repo}" rev-parse --git-dir >/dev/null 2>&1; then + if git -C "${_resolved_zot_repo}" diff --quiet 2>/dev/null && \ + git -C "${_resolved_zot_repo}" diff --cached --quiet 2>/dev/null; then + _zot_dirty="false" + else + _zot_dirty="true" + fi + fi + fi mkdir -p "$(dirname "$_manifest_path")" cat > "$_manifest_path" < Date: Mon, 15 Jun 2026 16:56:55 +0200 Subject: [PATCH 2/3] fix(build): repair + harden the release gate (Sam & Claude) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Codex's release gate (96fc1d3) had the right idea but two issues that the verified gates (sh -n / prettier / git diff) structurally could not catch, because none exercise the BUILD_CHANNEL=release path: 1. Blocker: check_release_gate was *called* at line ~105 but *defined* (and its resolve_* dependencies defined) far below. In POSIX sh that's a call before definition — with `set -e`, a release build aborted at exit 127 ("check_release_gate: not found") before the gate ran. Moved the invocation into the preflight section, after all helpers are defined. 2. Unsatisfiable + asymmetric: the gate required clawdie-ai to be on a vX.Y.Z tag, but clawdie-ai has no v-tag and is being pruned — so release was impossible. Replaced with reproducibility-by-record: every staged source (clawdie-iso, clawdie-ai, colibri, zot) must be a clean, committed tree; the manifest's recorded commits then fully describe the artifact. A recorded SHA is as pinned as a tag. Dropped the tag requirement. Also: - "clean" now uses `git status --porcelain`, so untracked files (which a diff-only check misses but which still change the build) fail the gate. - Factored the repeated resolve+dirty boilerplate into assert_clean_repo. - New scripts/test-release-gate.sh smoke test: asserts the porcelain semantics and that the gate is invoked after its definition (guards the exit-127 regression). A 5-line test that the three "verified gates" could not provide. Checks: sh -n build.sh; sh -n + run scripts/test-release-gate.sh (PASS); git diff --check. Co-Authored-By: Claude Opus 4.8 --- build.sh | 77 ++++++++++++++---------------------- scripts/test-release-gate.sh | 50 +++++++++++++++++++++++ 2 files changed, 80 insertions(+), 47 deletions(-) create mode 100755 scripts/test-release-gate.sh diff --git a/build.sh b/build.sh index 079a96f1..453da3db 100755 --- a/build.sh +++ b/build.sh @@ -101,9 +101,8 @@ if [ -n "${SSH_PUBLIC_KEY:-}" ]; then fi fi -if [ "${BUILD_CHANNEL}" = "release" ]; then - check_release_gate -fi +# The release gate runs in the preflight section below — it depends on the +# resolve_* helpers, which are defined later in this script. # The ISO carries its own product version (ISO_VERSION in build.cfg). It does not # track any single component. Component versions are resolved below purely for @@ -460,60 +459,41 @@ is_pinned_clawdie_ref() { printf '%s' "$_ref" | grep -Eq '^[0-9a-fA-F]{40}$|^v[0-9]+\.[0-9]+\.[0-9]+$' } -# Release builds require clean, committed sources across the whole stack. -# A release ISO must be a reproducible artifact — no uncommitted local patches. -check_release_gate() { - case "${CLAWDIE_REF}" in - v[0-9]*.[0-9]*.[0-9]*) ;; - *) - echo "ERROR: release builds must pin a Clawdie-AI tag with --clawdie-version X.Y.Z" - echo " Current Clawdie ref: ${CLAWDIE_REF}" - exit 1 - ;; - esac +# Assert a repo has no uncommitted *or untracked* changes. `git status --porcelain` +# is used (not `git diff`) so stray untracked files — which would change the build +# yet pass a diff-only check — also fail the gate. Increments _release_errors on a +# dirty/untracked tree; a non-git path is skipped (nothing to assert). +assert_clean_repo() { + _acr_name="$1" + _acr_path="$2" + command -v git >/dev/null 2>&1 || return 0 + git -C "${_acr_path}" rev-parse --git-dir >/dev/null 2>&1 || return 0 + if [ -n "$(git -C "${_acr_path}" status --porcelain 2>/dev/null)" ]; then + echo "ERROR: release builds require a clean ${_acr_name} repo (no uncommitted or untracked files)" + echo " Dirty tree at: ${_acr_path}" + _release_errors=$((_release_errors + 1)) + fi +} +# Release builds must be reproducible from the recorded provenance. We do not +# require components to be on a named tag — a recorded commit is just as pinned — +# but every staged source must be a clean, committed tree so the manifest's +# commits fully describe the artifact. (build-manifest.json records each commit.) +check_release_gate() { _release_errors=0 - # Clawdie-AI local checkout + assert_clean_repo "clawdie-iso" "${SCRIPT_DIR}" + resolve_clawdie_ai_repo - if command -v git >/dev/null 2>&1 && git -C "${_resolved_clawdie_ai_repo}" rev-parse --git-dir >/dev/null 2>&1; then - if ! git -C "${_resolved_clawdie_ai_repo}" diff --quiet 2>/dev/null || ! git -C "${_resolved_clawdie_ai_repo}" diff --cached --quiet 2>/dev/null; then - echo "ERROR: release builds require a clean clawdie-ai repo" - echo " Uncommitted changes detected in: ${_resolved_clawdie_ai_repo}" - _release_errors=$((_release_errors + 1)) - fi - fi + assert_clean_repo "clawdie-ai" "${_resolved_clawdie_ai_repo}" - # ISO repo itself - if command -v git >/dev/null 2>&1 && git -C "${SCRIPT_DIR}" rev-parse --git-dir >/dev/null 2>&1; then - if ! git -C "${SCRIPT_DIR}" diff --quiet 2>/dev/null || ! git -C "${SCRIPT_DIR}" diff --cached --quiet 2>/dev/null; then - echo "ERROR: release builds require a clean clawdie-iso repo" - echo " Uncommitted changes detected in: ${SCRIPT_DIR}" - _release_errors=$((_release_errors + 1)) - fi - fi - - # Colibri if [ "${FEATURE_COLIBRI:-NO}" = "YES" ]; then resolve_colibri_paths - if command -v git >/dev/null 2>&1 && git -C "${_resolved_colibri_repo}" rev-parse --git-dir >/dev/null 2>&1; then - if ! git -C "${_resolved_colibri_repo}" diff --quiet 2>/dev/null || ! git -C "${_resolved_colibri_repo}" diff --cached --quiet 2>/dev/null; then - echo "ERROR: release builds require a clean colibri repo" - echo " Uncommitted changes detected in: ${_resolved_colibri_repo}" - _release_errors=$((_release_errors + 1)) - fi - fi + assert_clean_repo "colibri" "${_resolved_colibri_repo}" - # Zot (agent binary) if [ "${COLIBRI_STAGE_AGENT:-YES}" = "YES" ]; then resolve_zot_paths - if command -v git >/dev/null 2>&1 && git -C "${_resolved_zot_repo}" rev-parse --git-dir >/dev/null 2>&1; then - if ! git -C "${_resolved_zot_repo}" diff --quiet 2>/dev/null || ! git -C "${_resolved_zot_repo}" diff --cached --quiet 2>/dev/null; then - echo "ERROR: release builds require a clean zot repo" - echo " Uncommitted changes detected in: ${_resolved_zot_repo}" - _release_errors=$((_release_errors + 1)) - fi - fi + assert_clean_repo "zot" "${_resolved_zot_repo}" fi fi @@ -1928,6 +1908,9 @@ EOF chroot "${MOUNT_POINT}" chown -h clawdie:clawdie /home/clawdie/.cache } +if [ "${BUILD_CHANNEL}" = "release" ]; then + check_release_gate +fi preflight_colibri_artifacts preflight_zot_artifacts diff --git a/scripts/test-release-gate.sh b/scripts/test-release-gate.sh new file mode 100755 index 00000000..e76909f3 --- /dev/null +++ b/scripts/test-release-gate.sh @@ -0,0 +1,50 @@ +#!/bin/sh +# Smoke test for the release-channel clean-repo gate in build.sh. +# +# Why this exists: the gate only runs on BUILD_CHANNEL=release, so `sh -n`, +# prettier, and dev builds never exercise it — a broken gate ships green. This +# test pins the two properties we depend on: +# 1. assert_clean_repo fails a tree with untracked OR modified files, and +# passes a clean tree (the porcelain check, not diff-only). +# 2. check_release_gate is invoked AFTER it is defined in build.sh (guards the +# call-before-definition regression that aborts release builds with exit 127). +# +# Run: sh scripts/test-release-gate.sh +set -eu + +SCRIPT_DIR=$(cd "$(dirname "$0")/.." && pwd) +BUILD_SH="${SCRIPT_DIR}/build.sh" +TMP=$(mktemp -d "${TMPDIR:-/tmp}/release-gate-test.XXXXXX") +trap 'rm -rf "${TMP}"' EXIT +fail=0 +check() { if [ "$1" = "$2" ]; then echo "ok - $3"; else echo "FAIL - $3 (want '$2', got '$1')"; fail=1; fi; } + +# --- property 2: definition precedes invocation ------------------------------- +_def=$(grep -n '^check_release_gate() {' "${BUILD_SH}" | head -1 | cut -d: -f1) +_call=$(grep -n '^[[:space:]]*check_release_gate$' "${BUILD_SH}" | head -1 | cut -d: -f1) +if [ -n "${_def}" ] && [ -n "${_call}" ] && [ "${_call}" -gt "${_def}" ]; then + echo "ok - check_release_gate invoked after its definition (def ${_def}, call ${_call})" +else + echo "FAIL - check_release_gate call/definition order (def '${_def}', call '${_call}')"; fail=1 +fi + +# --- property 1: assert_clean_repo semantics ---------------------------------- +# Extract the helper from build.sh and exercise it against real temp git repos. +sed -n '/^assert_clean_repo() {/,/^}/p' "${BUILD_SH}" > "${TMP}/fn.sh" +. "${TMP}/fn.sh" + +mkrepo() { d="${TMP}/$1"; mkdir -p "$d"; git -C "$d" init -q; git -C "$d" config user.email t@t; git -C "$d" config user.name t; + echo base > "$d/file"; git -C "$d" add file; git -C "$d" commit -qm init; printf '%s' "$d"; } + +clean=$(mkrepo clean) +_release_errors=0; assert_clean_repo clean "${clean}"; check "${_release_errors}" 0 "clean repo passes" + +untracked=$(mkrepo untracked); : > "${untracked}/stray.txt" +_release_errors=0; assert_clean_repo untracked "${untracked}"; check "${_release_errors}" 1 "untracked file fails (porcelain catches it)" + +modified=$(mkrepo modified); echo changed > "${modified}/file" +_release_errors=0; assert_clean_repo modified "${modified}"; check "${_release_errors}" 1 "modified tracked file fails" + +_release_errors=0; assert_clean_repo nogit "${TMP}/does-not-exist"; check "${_release_errors}" 0 "non-git path is skipped" + +[ "${fail}" -eq 0 ] && echo "PASS: release gate smoke test" || { echo "FAILED"; exit 1; } -- 2.45.3 From b959b64d21886c22fc5a6d11aab67ae2fded57c9 Mon Sep 17 00:00:00 2001 From: Sam & Claude Date: Mon, 15 Jun 2026 17:06:17 +0200 Subject: [PATCH 3/3] =?UTF-8?q?chore(iso):=20rename=20provenance=20"dirty"?= =?UTF-8?q?=20=E2=86=92=20"modified"=20(Sam=20&=20Claude)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Drop the "dirty" terminology in favor of "modified" (same boolean sense: true = working tree has uncommitted or untracked changes). Pure rename — no logic change. Safe now: nothing consumes these keys yet (checked colibri too). - build-manifest.json keys: zot_dirty/colibri_dirty/iso_repo_dirty → zot_modified/colibri_modified/iso_repo_modified - .clawdie-source.json: dirty_at_build → modified_at_build - iso-publish manifest (write-artifact-manifest.sh): repo_dirty → repo_modified - gate messages, comments, shell vars, and docs (BUILD/CHANGELOG/ISO-MANIFESTS/ PLAN) reworded. Checks: sh -n on all three scripts; release-gate smoke test PASS; prettier clean on changed docs. Co-Authored-By: Claude Opus 4.8 --- BUILD.md | 2 +- CHANGELOG.md | 2 +- PLAN-OPERATOR-USB-NEXT.md | 2 +- build.sh | 40 +++++++++++++++--------------- docs/ISO-MANIFESTS.md | 2 +- scripts/write-artifact-manifest.sh | 8 +++--- 6 files changed, 28 insertions(+), 28 deletions(-) diff --git a/BUILD.md b/BUILD.md index f3c4af6b..a7e63d4b 100644 --- a/BUILD.md +++ b/BUILD.md @@ -414,7 +414,7 @@ The installed system receives the same manifest. It records: - ISO version and build channel - FreeBSD version/arch - bundled Clawdie-AI ref and commit -- ISO repo commit and dirty state +- ISO repo commit and modified state - UTC build timestamp The final size output distinguishes: diff --git a/CHANGELOG.md b/CHANGELOG.md index 7c973bb8..8a85f912 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,7 +21,7 @@ image is meant to work out of the box. Work continues from here toward `1.0.0`. ### Versioning - The ISO now carries its **own product version** and no longer borrows zot's number. `ISO_VERSION` is explicit (set in `build.cfg`); `auto`/zot-tracking is gone and a build with no version fails fast. Component versions (zot, colibri, clawdie-ai, clawdie-iso) are recorded as provenance in `build-manifest.json`. -- `build-manifest.json` now records `colibri_commit`/`colibri_dirty` — the image stages adjacent colibri binaries, so the commit that produced them is captured for reproducibility. +- `build-manifest.json` now records `colibri_commit`/`colibri_modified` — the image stages adjacent colibri binaries, so the commit that produced them is captured for reproducibility. ### Added - Live rebuild lane now covers the **whole agent stack**: `go` added to the live-operator package list and the `zot` source seeded at `/home/clawdie/ai/zot`, so a booted USB can rebuild zot (Go) as well as Colibri (Rust). See `docs/LIVE-COLIBRI-REBUILD.md`. diff --git a/PLAN-OPERATOR-USB-NEXT.md b/PLAN-OPERATOR-USB-NEXT.md index 00e6e4da..e53ef479 100644 --- a/PLAN-OPERATOR-USB-NEXT.md +++ b/PLAN-OPERATOR-USB-NEXT.md @@ -642,7 +642,7 @@ folder so `$HOME` stays uncluttered: - Keep provider authentication manual. The image may include code, but it must not bake provider credentials. - If full `.git` history is too large, use shallow clones or archive snapshots - plus a small manifest that records remote URL, branch, commit, and dirty + plus a small manifest that records remote URL, branch, commit, and modified state. - This is a live-debug aid, not a build-host replacement: Linux agents still do not build the ISO, and real hardware proof remains operator/Codex-owned. diff --git a/build.sh b/build.sh index 453da3db..2ed51d2a 100755 --- a/build.sh +++ b/build.sh @@ -462,7 +462,7 @@ is_pinned_clawdie_ref() { # Assert a repo has no uncommitted *or untracked* changes. `git status --porcelain` # is used (not `git diff`) so stray untracked files — which would change the build # yet pass a diff-only check — also fail the gate. Increments _release_errors on a -# dirty/untracked tree; a non-git path is skipped (nothing to assert). +# modified/untracked tree; a non-git path is skipped (nothing to assert). assert_clean_repo() { _acr_name="$1" _acr_path="$2" @@ -470,7 +470,7 @@ assert_clean_repo() { git -C "${_acr_path}" rev-parse --git-dir >/dev/null 2>&1 || return 0 if [ -n "$(git -C "${_acr_path}" status --porcelain 2>/dev/null)" ]; then echo "ERROR: release builds require a clean ${_acr_name} repo (no uncommitted or untracked files)" - echo " Dirty tree at: ${_acr_path}" + echo " Modified tree at: ${_acr_path}" _release_errors=$((_release_errors + 1)) fi } @@ -498,7 +498,7 @@ check_release_gate() { fi if [ "${_release_errors}" -gt 0 ]; then - echo "ERROR: release build aborted — ${_release_errors} dirty repo(s). Use BUILD_CHANNEL=dev for iteration builds." + echo "ERROR: release build aborted — ${_release_errors} modified repo(s). Use BUILD_CHANNEL=dev for iteration builds." exit 1 fi } @@ -506,15 +506,15 @@ check_release_gate() { write_build_manifest() { _manifest_path="$1" _iso_repo_commit="unknown" - _iso_repo_dirty="null" + _iso_repo_modified="null" _live_ssh_pubkey_fp_json="null" _tailscale_auth_key_baked="${TAILSCALE_AUTH_KEY_BAKED:-false}" if command -v git >/dev/null 2>&1 && git -C "$SCRIPT_DIR" rev-parse --git-dir >/dev/null 2>&1; then _iso_repo_commit=$(git -C "$SCRIPT_DIR" rev-parse HEAD 2>/dev/null || echo unknown) if git -C "$SCRIPT_DIR" diff --quiet 2>/dev/null && git -C "$SCRIPT_DIR" diff --cached --quiet 2>/dev/null; then - _iso_repo_dirty="false" + _iso_repo_modified="false" else - _iso_repo_dirty="true" + _iso_repo_modified="true" fi fi if [ -n "${LIVE_SSH_PUBKEY_FP:-}" ]; then @@ -523,29 +523,29 @@ write_build_manifest() { # Colibri provenance: the image stages adjacent colibri binaries, so record # which commit produced them (the biggest gap for a reproducible release). _colibri_commit="unknown" - _colibri_dirty="null" + _colibri_modified="null" if [ "${FEATURE_COLIBRI:-NO}" = "YES" ]; then resolve_colibri_paths if git -C "${_resolved_colibri_repo}" rev-parse --git-dir >/dev/null 2>&1; then _colibri_commit=$(git -C "${_resolved_colibri_repo}" rev-parse HEAD 2>/dev/null || echo unknown) if git -C "${_resolved_colibri_repo}" diff --quiet 2>/dev/null && \ git -C "${_resolved_colibri_repo}" diff --cached --quiet 2>/dev/null; then - _colibri_dirty="false" + _colibri_modified="false" else - _colibri_dirty="true" + _colibri_modified="true" fi fi fi - # Zot provenance (agent binary built from source — record if repo is dirty). - _zot_dirty="null" + # Zot provenance (agent binary built from source — record if repo is modified). + _zot_modified="null" if [ "${FEATURE_COLIBRI:-NO}" = "YES" ] && [ "${COLIBRI_STAGE_AGENT:-YES}" = "YES" ]; then resolve_zot_paths if command -v git >/dev/null 2>&1 && git -C "${_resolved_zot_repo}" rev-parse --git-dir >/dev/null 2>&1; then if git -C "${_resolved_zot_repo}" diff --quiet 2>/dev/null && \ git -C "${_resolved_zot_repo}" diff --cached --quiet 2>/dev/null; then - _zot_dirty="false" + _zot_modified="false" else - _zot_dirty="true" + _zot_modified="true" fi fi fi @@ -556,9 +556,9 @@ write_build_manifest() { "version_scheme": "product", "zot_version": "$(json_escape "${ZOT_RESOLVED_VERSION:-${ZOT_VERSION}}")", "zot_commit": "$(json_escape "${ZOT_RESOLVED_COMMIT:-unknown}")", - "zot_dirty": ${_zot_dirty:-null}, + "zot_modified": ${_zot_modified:-null}, "colibri_commit": "$(json_escape "${_colibri_commit:-unknown}")", - "colibri_dirty": ${_colibri_dirty:-null}, + "colibri_modified": ${_colibri_modified:-null}, "build_channel": "$(json_escape "${BUILD_CHANNEL}")", "freebsd_version": "$(json_escape "${FREEBSD_VERSION}")", "freebsd_arch": "$(json_escape "${FREEBSD_ARCH}")", @@ -567,7 +567,7 @@ write_build_manifest() { "live_ssh_pubkey_fp": ${_live_ssh_pubkey_fp_json}, "tailscale_auth_key_baked": ${_tailscale_auth_key_baked}, "iso_repo_commit": "$(json_escape "${_iso_repo_commit}")", - "iso_repo_dirty": ${_iso_repo_dirty}, + "iso_repo_modified": ${_iso_repo_modified}, "built_at": "$(date -u '+%Y-%m-%dT%H:%M:%SZ')" } EOF @@ -1142,9 +1142,9 @@ seed_live_ai_source_repo() { git -C "${_repo_dest}" remote set-url origin "${_repo_origin}" 2>/dev/null || true printf '%s\n' '.clawdie-source.json' >> "${_repo_dest}/.git/info/exclude" - _repo_dirty=false + _repo_modified=false if ! git -C "${_repo_src}" diff --quiet 2>/dev/null || ! git -C "${_repo_src}" diff --cached --quiet 2>/dev/null; then - _repo_dirty=true + _repo_modified=true fi cat > "${_repo_dest}/.clawdie-source.json" </dev/null && git diff --cached --quiet 2>/dev/null; then - _repo_dirty="false" + _repo_modified="false" elif git rev-parse --git-dir >/dev/null 2>&1; then - _repo_dirty="true" + _repo_modified="true" fi _host="$(hostname 2>/dev/null || echo unknown)" _written_at="$(date -u '+%Y-%m-%dT%H:%M:%SZ')" @@ -177,7 +177,7 @@ cat > "${_tmp}" <