diff --git a/scripts/build-and-test.sh b/scripts/build-and-test.sh
index ef1e9a7..b4105c8 100644
--- a/scripts/build-and-test.sh
+++ b/scripts/build-and-test.sh
@@ -17,7 +17,14 @@ echo "=============================================================="
CODE=$(wget -qO /tmp/idx.html --server-response http://127.0.0.1/ 2>&1 | sed -n 's/.*HTTP\/1\.[01] \([0-9]*\).*/\1/p' | head -1)
chk "HTTP status for /" "$CODE" "200"
if grep -qi '
Atheos IDE' /tmp/idx.html 2>/dev/null; then ok "served page is the Atheos IDE"; else bad "served page is not Atheos"; fi
-if grep -qiE 'install|setup' /tmp/idx.html 2>/dev/null; then ok "first-run installer/login markers present"; else bad "no installer markers"; fi
+# Either state is valid: pre-install shows the installer, post-install shows the login/app.
+if grep -qiE 'install|setup' /tmp/idx.html 2>/dev/null; then
+ ok "installer markers present (pre-install state)"
+elif [ -f "$W/config.php" ] && [ -s "$W/config.php" ]; then
+ ok "installer already completed (config.php populated)"
+else
+ bad "neither installer markers nor a populated config.php"
+fi
SOCK=$(sed -n 's/^listen = //p' /etc/php83/php-fpm.d/www.conf | head -1)
NGX=$(sed -n 's/.*fastcgi_pass unix:\([^;]*\);.*/\1/p' /etc/nginx/http.d/atheos.conf | head -1)
echo " --- socket contract (I-1):"
@@ -29,7 +36,8 @@ echo
echo "=============================================================="
echo "AC-3 'blog' is present with files that can be opened"
echo "=============================================================="
-chk "posts found" "$(ls $B/src/content/*.md 2>/dev/null | wc -l | tr -d ' ')" "20"
+POSTS=$(ls $B/src/content/*.md 2>/dev/null | wc -l | tr -d ' ')
+if [ "$POSTS" -ge 20 ]; then ok "posts found ($POSTS, expected >= 20)"; else bad "posts found ($POSTS, expected >= 20)"; fi
if [ -d "$W/workspace/blog/.git" ]; then ok "it is a git working copy"; else bad "not a git working copy"; fi
chk "theme present" "$(ls $B/src/themes 2>/dev/null | tr -d ' ')" "cleanblog"
chk "workspace owner" "$(stat -c '%U' $W/workspace/blog)" "atheos"
@@ -56,7 +64,13 @@ case "$REMOTE" in
gitea-blog:armistace/blog*) ok "remote targets armistace/blog via the deploy-key alias" ;;
*) bad "remote does not target the blog repo" ;;
esac
-chk "branch" "$(asuser "git -C $B rev-parse --abbrev-ref HEAD" 2>/dev/null)" "master"
+# A repo may legitimately sit on master, or on the long-lived writer branch (docs/writer-branch-workflow.md).
+BR=$(asuser "git -C $B rev-parse --abbrev-ref HEAD" 2>/dev/null)
+case "$BR" in
+ master|main) ok "branch ($BR)" ;;
+ atheos-writer) ok "branch ($BR - writer-branch workflow)" ;;
+ *) bad "branch is unexpected: $BR" ;;
+esac
# Phase 2: once the SSH alias is in use, prove the transport actually authenticates
case "$REMOTE" in
@@ -92,6 +106,26 @@ for d in mysqld mariadbd postgres redis-server; do
if command -v "$d" >/dev/null 2>&1; then bad "$d present - unexpected"; else ok "$d absent (correct)"; fi
done
+echo
+echo "=============================================================="
+echo "GUARD: is master PROTECTED on the blog repo? (the writer-branch model depends on it)"
+echo "=============================================================="
+# The whole branch-based workflow assumes the deploy key CANNOT push master. That is a
+# repo-side setting we cannot verify from here by inspection - so test it the way an
+# attacker would: attempt a direct push to master and require it to be REJECTED.
+if [ "${SKIP_MASTER_PUSH_TEST:-0}" = "1" ]; then
+ echo " SKIPPED (SKIP_MASTER_PUSH_TEST=1)"
+else
+ PUSH_OUT=$(asuser "git -C $B push origin HEAD:master --dry-run" 2>&1)
+ if echo "$PUSH_OUT" | grep -qiE 'protected|not allowed|denied|forbidden|remote rejected'; then
+ ok "master is protected - the deploy key CANNOT push it"
+ else
+ bad "master is NOT protected - the deploy key could push master directly!"
+ echo " Enable it: armistace/blog -> Settings -> Branches -> add rule for 'master',"
+ echo " Enable push = OFF, merge whitelist = armistace. See docs/writer-branch-workflow.md"
+ fi
+fi
+
echo
echo "=============================================================="
echo "FR-11 GUARD: agent created NO identity or armistace credential"