diff --git a/.gitea/pull_request_template.md b/.gitea/pull_request_template.md new file mode 100644 index 0000000..8b27246 --- /dev/null +++ b/.gitea/pull_request_template.md @@ -0,0 +1,33 @@ + + +## Ticket + +[#NNN](https://projects.knownelement.com/issues/NNN) + +## What changed / why + +## Access-control checklist (tick ONLY what applies) + +- [ ] This PR touches badge roster / unlock policy / door-adjacent code +- [ ] If yes: scope is the SERVER-ROOM DOOR ONLY (no other locks in the + house — founder ruling 2026-09-02) +- [ ] If yes: founder-approved Redmine ticket referenced above +- [ ] No webhook URLs, tokens, or badge credentials in the diff + (secrets live in /etc/default/doorman, 0600, on the host) + +## Verification + +- [ ] `bash scripts/test.sh` green +- [ ] `bash tests/shellcheck.sh` clean +- [ ] `bash scripts/check-rules.sh` PASS + +## Sign-off + +- Approver 1 (name / date): +- Approver 2 (name / date): + +_Physical server-room access is 100% human-gated. Agents may author +and push PR branches; only humans approve and merge._ diff --git a/AGENTS.md b/AGENTS.md index 936f36b..1fb1ff9 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -26,6 +26,20 @@ House rules layering: `~/.zcode/AGENTS.md` (global) < PFVCluster [tests/remote.sh](https://git.knownelement.com/KNEL/PFVCluster/src/branch/main/tests/remote.sh). - DNS names only — never IP literals. +## PR process (founder ruling 2026-09-02) — physical access is 100% human-gated + +- `main` is BRANCH-PROTECTED: no direct pushes, by anyone. All changes + land via PR with the sign-off template (`.gitea/pull_request_template.md`). +- **Two human approvals required to merge.** Gitea enforces the count. + Agents may author and push PR branches and implement review feedback — + agents NEVER approve, NEVER merge, NEVER bypass (`--no-verify` is not + a bypass for the approval count). +- Access-control changes (badge roster, unlock policy, door-adjacent + code) additionally require the template's checklist and a + founder-approved ticket. +- Full audit chain per change: Redmine ticket → PR description → two + named human approvals → merge → deployment note (#356). + ## Scope boundary: Home Assistant The HA side (webhook receiver, whitelist, automations, alerts) is owned by diff --git a/scripts/check-rules.sh b/scripts/check-rules.sh index 2d8bc23..562ea74 100755 --- a/scripts/check-rules.sh +++ b/scripts/check-rules.sh @@ -163,7 +163,7 @@ while IFS= read -r -d '' f; do POINTER_MISSING=$((POINTER_MISSING + 1)) $RULE_VERBOSE && printf ' %s\n' "$f" fi -done < <(find . -path ./.git -prune -o -path ./.crush -prune -o -path ./.tmp -prune -o -path ./vendor -prune -o -path ./archive -prune -o -name '*.md' -print0 2>/dev/null) +done < <(find . -path ./.git -prune -o -path ./.crush -prune -o -path ./.tmp -prune -o -path ./vendor -prune -o -path ./archive -prune -o -path ./.gitea -prune -o -name "*.md" -print0 2>/dev/null) if [ "$POINTER_MISSING" -eq 0 ]; then check "All non-exempt .md cite Discourse ($DISCOURSE_HOST)" "pass" else