From d8ae25506d40327f48d14a6e69026ce83c3aa25d Mon Sep 17 00:00:00 2001 From: reachableceo Date: Wed, 2 Sep 2026 21:27:16 -0500 Subject: [PATCH] docs(governance): PR template + two-approver human gate for access work [#345] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Founder ruling 2026-09-02: physical server-room access 100% human required — logged, approved, signed off. Branch protection lands immediately after this commit. Also excludes .gitea/ from the doc-pointer scan (framework dir, not docs). https://projects.knownelement.com/issues/345 --- .gitea/pull_request_template.md | 33 +++++++++++++++++++++++++++++++++ AGENTS.md | 14 ++++++++++++++ scripts/check-rules.sh | 2 +- 3 files changed, 48 insertions(+), 1 deletion(-) create mode 100644 .gitea/pull_request_template.md 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