From 2f44164dbd6ce389c125be38a42a615f7c9943fd Mon Sep 17 00:00:00 2001 From: hyperpolymath <6759885+hyperpolymath@users.noreply.github.com> Date: Mon, 22 Jun 2026 12:47:56 +0100 Subject: [PATCH 1/3] =?UTF-8?q?feat(bots):=20add=20rhodibot=20per-repo=20d?= =?UTF-8?q?irective=20=E2=80=94=20full=20enforcement=20(software/language?= =?UTF-8?q?=20repo)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Every other estate repo carries .machine_readable/bot_directives/rhodibot.a2ml; these three lacked it, so rhodibot (the deterministic RSR verifier) had no per-repo allow/ deny/scope guidance here. Adds it modelled on the estate schema (schema_version 1.0), with the deterministic licence guards in deny: never relicense third-party/vendored, never change an undeterminable licence (leave untouched), never convert deliberate (non-drift) licences. SPDX normalisation to the declared licences (code -> MPL-2.0, prose -> CC-BY-SA-4.0) is allowed (rhodibot is symbolic/deterministic, out of scope of the neural-agent no-automated-licence-edits rule). root-shape: ok. --- .machine_readable/bot_directives/rhodibot.a2ml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 .machine_readable/bot_directives/rhodibot.a2ml diff --git a/.machine_readable/bot_directives/rhodibot.a2ml b/.machine_readable/bot_directives/rhodibot.a2ml new file mode 100644 index 0000000..4a46d72 --- /dev/null +++ b/.machine_readable/bot_directives/rhodibot.a2ml @@ -0,0 +1,18 @@ +# SPDX-FileCopyrightText: © 2026 Jonathan D.A. Jewell (hyperpolymath) +# SPDX-License-Identifier: MPL-2.0 +# Media-Type: application/vnd.bot-directive+a2ml +# +# Per-repo directive for rhodibot — the DETERMINISTIC, symbolic RSR verifier +# (gitbot-fleet/bots/rhodibot). rhodibot acts from hard-coded rules + these guards; +# it is NOT a neural agent, so the no-automated-licence-edits rule (which scopes to +# neural agents) does not forbid its bounded SPDX normalisation. Repo kind: software. + +schema_version = "1.0" +directive_type = "bot-directive" +bot = "rhodibot" +scope = "rsr-compliance" +enforcement = "full" +repo-kind = "software" +allow = ["metadata", "docs", "repo-structure checks", "policy validation", "SPDX normalisation to the repo's declared licences (code/config -> MPL-2.0; prose docs -> CC-BY-SA-4.0)"] +deny = ["destructive edits without approval", "relicensing third-party / vendored / forked files", "any SPDX change whose correct licence cannot be deterministically determined (leave untouched)", "converting AGPL or other deliberate (non-drift) licences"] +notes = "ACK: haec rhodibot directive loaded (FULL enforcement, software/language repo). Auto-fix limited to docs/metadata formatting + deterministic SPDX normalisation; never edit design/ or examples/ semantics without approval; leave undeterminable licences untouched." From 6f1b386ed66f572d54cafd370d019e868a884811 Mon Sep 17 00:00:00 2001 From: hyperpolymath <6759885+hyperpolymath@users.noreply.github.com> Date: Mon, 22 Jun 2026 12:56:46 +0100 Subject: [PATCH 2/3] fix(bots): put rhodibot in AUDIT MODE (report-only) pending convergence Replace the directive-blind inline auto-fix (delete/rename/SPDX-sed/file-create + auto-PR) with a detect-and-LOG audit run: it reports what it WOULD do, mutates nothing, opens no PR, and runs with contents:read only. Neutralises the guardless SPDX sed immediately while preserving rhodibot's detection value. Aligns with the report-only-canary directive. Convergence onto the deterministic engine tracked HIGH PRIORITY in gitbot-fleet. --- .github/workflows/rhodibot.yml | 222 +++++++++------------------------ 1 file changed, 58 insertions(+), 164 deletions(-) diff --git a/.github/workflows/rhodibot.yml b/.github/workflows/rhodibot.yml index 0f679dc..d605e15 100644 --- a/.github/workflows/rhodibot.yml +++ b/.github/workflows/rhodibot.yml @@ -1,189 +1,83 @@ # SPDX-License-Identifier: MPL-2.0 -# rhodibot.yml — Automated RSR compliance enforcement +# rhodibot.yml — AUDIT MODE (report-only) # -# Reads root-hygiene rules and auto-fixes what it can: -# - Delete banned files (AI.djot, duplicate CONTRIBUTING.adoc, stale snapshots) -# - Rename misnamed files (AI.a2ml → 0-AI-MANIFEST.a2ml) -# - Fix SPDX headers (AGPL → PMPL in dotfiles) -# - Create missing required files (SECURITY.md, CONTRIBUTING.md) -# - Report unfixable issues as PR comments -# -# Runs weekly and on Hypatia scan completion. - -name: "⚙ Auto: Rhodibot RSR auto-fix" +# rhodibot is temporarily in AUDIT MODE in this repo: it DETECTS RSR-compliance drift +# and LOGS what it WOULD do, but performs NO mutations (no delete / rename / SPDX edit / +# file create) and opens NO PR. Permissions are read-only. This neutralises the +# directive-blind inline auto-fix (including the guardless SPDX sed) pending +# convergence onto the deterministic gitbot-fleet engine. Tracked HIGH PRIORITY in +# gitbot-fleet. Per-repo policy: .machine_readable/bot_directives/rhodibot.a2ml. + +name: "⚙ Auto: Rhodibot RSR audit (report-only)" on: schedule: - cron: '0 6 * * 1' # Every Monday at 06:00 UTC - workflow_dispatch: # Manual trigger - workflow_run: - workflows: ["Hypatia Neurosymbolic Analysis"] - types: [completed] + workflow_dispatch: permissions: - contents: write - pull-requests: write + contents: read jobs: - rhodibot: + rhodibot-audit: runs-on: ubuntu-latest - timeout-minutes: 15 + timeout-minutes: 10 steps: - name: Checkout uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 with: fetch-depth: 1 - - name: Rhodibot — Scan and Fix - id: fix + - name: Rhodibot — AUDIT (detect + log; NO mutation, NO PR) run: | set -euo pipefail - FIXES="" + WOULD="" ISSUES="" - CHANGED=false + note() { WOULD="$WOULD\n- $1"; echo "AUDIT — would: $1"; } + issue() { ISSUES="$ISSUES\n- $1"; echo "ISSUE: $1"; } - # --- 1. Delete banned files --- - for pattern in "AI.djot" "NEXT_STEPS.md" "TODO.md" "NOTES.md" "TASKS.md"; do - if [ -f "$pattern" ]; then - rm "$pattern" - FIXES="$FIXES\n- Deleted \`$pattern\` (superseded)" - CHANGED=true - fi + # 1. Banned files + for p in AI.djot NEXT_STEPS.md TODO.md NOTES.md TASKS.md; do + if [ -f "$p" ]; then note "delete banned file \`$p\`"; fi done - - # Delete stale snapshot files + # 2. Stale snapshots for f in *-STATUS-*.md *-COMPLETION-*.md *-COMPLETE.md *-VERIFIED-*.md; do - if [ -f "$f" ]; then - rm "$f" - FIXES="$FIXES\n- Deleted stale snapshot \`$f\`" - CHANGED=true - fi + if [ -f "$f" ]; then note "delete stale snapshot \`$f\`"; fi done - - # --- 2. Rename misnamed files --- - if [ -f "AI.a2ml" ] && [ ! -f "0-AI-MANIFEST.a2ml" ]; then - mv AI.a2ml 0-AI-MANIFEST.a2ml - FIXES="$FIXES\n- Renamed \`AI.a2ml\` → \`0-AI-MANIFEST.a2ml\`" - CHANGED=true - fi - - # --- 3. Delete duplicate format files --- - if [ -f "CONTRIBUTING.md" ] && [ -f "CONTRIBUTING.adoc" ]; then - rm CONTRIBUTING.adoc - FIXES="$FIXES\n- Deleted duplicate \`CONTRIBUTING.adoc\` (keeping .md for GitHub)" - CHANGED=true - fi - - if [ -f "README.md" ] && [ -f "README.adoc" ]; then - # Only delete README.md if it's a stub (<5 lines) - lines=$(wc -l < README.md) - if [ "$lines" -lt 5 ]; then - rm README.md - FIXES="$FIXES\n- Deleted stub \`README.md\` (keeping .adoc)" - CHANGED=true - fi - fi - - # --- 4. Fix SPDX headers in dotfiles --- - for dotfile in .gitignore .gitattributes .editorconfig; do - if [ -f "$dotfile" ] && grep -q "AGPL-3.0" "$dotfile" 2>/dev/null; then - sed -i 's/AGPL-3.0-or-later/MPL-2.0/g; s/AGPL-3.0/MPL-2.0/g' "$dotfile" - FIXES="$FIXES\n- Fixed SPDX header in \`$dotfile\` (AGPL → PMPL)" - CHANGED=true + # 3. Misnamed + if [ -f AI.a2ml ] && [ ! -f 0-AI-MANIFEST.a2ml ]; then note "rename \`AI.a2ml\` -> \`0-AI-MANIFEST.a2ml\`"; fi + # 4. Duplicate format files + if [ -f CONTRIBUTING.md ] && [ -f CONTRIBUTING.adoc ]; then note "remove duplicate \`CONTRIBUTING.adoc\`"; fi + if [ -f README.md ] && [ -f README.adoc ] && [ "$(wc -l < README.md)" -lt 5 ]; then note "remove stub \`README.md\`"; fi + # 5. SPDX drift in dotfiles — DETECT ONLY. Licence normalisation is the + # deterministic engine's job (bot_directives/rhodibot.a2ml); never auto-edit here. + for d in .gitignore .gitattributes .editorconfig; do + if [ -f "$d" ] && grep -q "AGPL-3.0" "$d" 2>/dev/null; then + note "flag SPDX drift in \`$d\` (AGPL-3.0 present) — DEFER to the deterministic engine; do NOT auto-edit" fi done - - # --- 5. Create missing required files --- - if [ ! -f "SECURITY.md" ]; then - cat > SECURITY.md << 'SECEOF' - - # Security Policy - - ## Reporting a Vulnerability - - **Email:** j.d.a.jewell@open.ac.uk - - **Response timeline:** - - Acknowledgement within 48 hours - - Initial assessment within 7 days - - Fix or mitigation within 90 days - - **Safe harbour:** We will not pursue legal action against security researchers who follow responsible disclosure. - SECEOF - FIXES="$FIXES\n- Created missing \`SECURITY.md\`" - CHANGED=true - fi - - if [ ! -f "CONTRIBUTING.md" ]; then - cat > CONTRIBUTING.md << 'CONTEOF' - - # Contributing - - 1. Fork the repository - 2. Create a feature branch - 3. Ensure SPDX headers on all files - 4. Submit a pull request - - **Author:** Jonathan D.A. Jewell - CONTEOF - FIXES="$FIXES\n- Created missing \`CONTRIBUTING.md\`" - CHANGED=true - fi - - # --- 6. Check for issues we can't auto-fix --- - if [ ! -f "0-AI-MANIFEST.a2ml" ] && [ ! -f "AI.a2ml" ]; then - ISSUES="$ISSUES\n- Missing AI manifest (0-AI-MANIFEST.a2ml)" - fi - - if [ ! -f "LICENSE" ] && [ ! -f "LICENSE.md" ] && [ ! -f "LICENSE.txt" ]; then - ISSUES="$ISSUES\n- Missing LICENSE file" - fi - - if [ ! -f "README.adoc" ] && [ ! -f "README.md" ]; then - ISSUES="$ISSUES\n- Missing README" - fi - - # Check for third-party fork (skip SPDX enforcement) - if [ -f "LICENSE" ] && grep -q "multiple licenses\|LGPL\|Apache" LICENSE 2>/dev/null; then - echo "FORK=true" >> $GITHUB_OUTPUT - fi - - # --- 7. Check dangerous patterns --- - DANGEROUS="" - for pattern in "believe_me" "assert_total" "Admitted" "sorry" "unsafeCoerce" "Obj.magic"; do - count=$(grep -r "$pattern" --include='*.idr' --include='*.v' --include='*.lean' --include='*.hs' --include='*.ml' --include='*.res' . 2>/dev/null | grep -v node_modules | wc -l || echo 0) - if [ "$count" -gt 0 ]; then - DANGEROUS="$DANGEROUS\n- \`$pattern\`: $count occurrences" - fi + # 6. Missing required files + if [ ! -f SECURITY.md ]; then note "create missing \`SECURITY.md\`"; fi + if [ ! -f CONTRIBUTING.md ]; then note "create missing \`CONTRIBUTING.md\`"; fi + # 7. Unfixable / report-only + if [ ! -f 0-AI-MANIFEST.a2ml ] && [ ! -f AI.a2ml ]; then issue "missing AI manifest (0-AI-MANIFEST.a2ml)"; fi + if [ ! -f LICENSE ] && [ ! -f LICENSE.md ] && [ ! -f LICENSE.txt ]; then issue "missing LICENSE file"; fi + if [ ! -f README.adoc ] && [ ! -f README.md ]; then issue "missing README"; fi + # 8. Dangerous patterns (soundness escape hatches) + for pat in believe_me assert_total Admitted sorry unsafeCoerce Obj.magic; do + c=$(grep -rl "$pat" --include='*.idr' --include='*.v' --include='*.lean' --include='*.hs' --include='*.ml' --include='*.res' . 2>/dev/null | grep -cv node_modules || true) + if [ "${c:-0}" -gt 0 ]; then issue "dangerous pattern \`$pat\` in ${c} file(s)"; fi done - # Output results - echo "CHANGED=$CHANGED" >> $GITHUB_OUTPUT - { - echo "FIXES<> $GITHUB_OUTPUT - { - echo "ISSUES<> $GITHUB_OUTPUT { - echo "DANGEROUS<> $GITHUB_OUTPUT - - name: Create PR with fixes - if: steps.fix.outputs.CHANGED == 'true' - run: "git config user.name \"rhodibot\"\ngit config user.email \"rhodibot@hyperpolymath.dev\"\nBRANCH=\"rhodibot/rsr-compliance-$(date +%Y%m%d)\"\ngit checkout -b \"$BRANCH\"\ngit add -A\ngit commit -m \"fix(rhodibot): automated RSR compliance fixes\n\n${{ steps.fix.outputs.FIXES }}\n\nCo-Authored-By: rhodibot \"\n\ngit push origin \"$BRANCH\"\n\nBODY=\"## \U0001F916 Rhodibot — RSR Compliance Fixes\n\n### Changes Made\n${{ steps.fix.outputs.FIXES }}\n\"\n\nif [ -n \"${{ steps.fix.outputs.ISSUES }}\" ]; then\n BODY=\"$BODY\n### Issues Found (manual fix needed)\n${{ steps.fix.outputs.ISSUES }}\n\"\nfi\n\nif [ -n \"${{ steps.fix.outputs.DANGEROUS }}\" ]; then\n BODY=\"$BODY\n### ⚠️ Dangerous Patterns Detected\n${{ steps.fix.outputs.DANGEROUS }}\n\n_These bypass formal verification. See \\`proven\\` repo for alternatives._\n\"\nfi\n\ngh pr create \\\n --title \"\U0001F916 Rhodibot: RSR compliance fixes\" \\\n --body \"$BODY\" \\\n --base main \\\n --head \"$BRANCH\"\n" - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - name: Report (no changes needed) - if: steps.fix.outputs.CHANGED != 'true' - run: | - echo "✅ Repository is RSR-compliant. No fixes needed." - if [ -n "${{ steps.fix.outputs.ISSUES }}" ]; then - echo "⚠️ Issues found (manual fix needed):" - echo -e "${{ steps.fix.outputs.ISSUES }}" - fi - if [ -n "${{ steps.fix.outputs.DANGEROUS }}" ]; then - echo "⚠️ Dangerous patterns:" - echo -e "${{ steps.fix.outputs.DANGEROUS }}" - fi + echo "## 🔎 Rhodibot — AUDIT MODE (report-only; NO changes made)" + echo "" + if [ -n "$WOULD" ]; then + echo "### Would auto-fix (SUPPRESSED in audit mode)" + echo -e "$WOULD" + else + echo "_No auto-fixes would be applied._" + fi + echo "" + if [ -n "$ISSUES" ]; then + echo "### Issues (manual / deterministic engine)" + echo -e "$ISSUES" + fi + } >> "$GITHUB_STEP_SUMMARY" + echo "Rhodibot audit complete (report-only). No mutations, no PR." From 0efe81101b743934a1f9507b325fb7a0241bf7a3 Mon Sep 17 00:00:00 2001 From: hyperpolymath <6759885+hyperpolymath@users.noreply.github.com> Date: Mon, 22 Jun 2026 12:58:36 +0100 Subject: [PATCH 3/3] feat(bots): add fleet mode setting (active/audit/passive/ignore) to rhodibot directive Adds a first-class 'mode' field to the rhodibot directive (fleet-standard; all gitbot-fleet bots should honour it): active=do it; audit=dry-run + full forensic log of what it WOULD do; passive=normal report only; ignore=skip the repo entirely. Set to mode=audit here, matching the report-only audit workflow now deployed. Orthogonal to enforcement (full/partial) and repo-kind (software/lean-docs). --- .machine_readable/bot_directives/rhodibot.a2ml | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/.machine_readable/bot_directives/rhodibot.a2ml b/.machine_readable/bot_directives/rhodibot.a2ml index 4a46d72..d368617 100644 --- a/.machine_readable/bot_directives/rhodibot.a2ml +++ b/.machine_readable/bot_directives/rhodibot.a2ml @@ -6,13 +6,21 @@ # (gitbot-fleet/bots/rhodibot). rhodibot acts from hard-coded rules + these guards; # it is NOT a neural agent, so the no-automated-licence-edits rule (which scopes to # neural agents) does not forbid its bounded SPDX normalisation. Repo kind: software. +# +# OPERATING MODE (fleet-standard; ALL gitbot-fleet bots honour 'mode'): +# active — perform actions: mutate the repo / open PRs. +# audit — dry-run: do everything EXCEPT mutate; emit FULL forensic/debug output of +# what it WOULD have done to a log/artefact (a pretend run). +# passive — normal-mode reporting only: summarise repo state; no pretend-run forensics. +# ignore — skip this repo entirely; do not even scan. schema_version = "1.0" directive_type = "bot-directive" bot = "rhodibot" scope = "rsr-compliance" +mode = "audit" enforcement = "full" repo-kind = "software" allow = ["metadata", "docs", "repo-structure checks", "policy validation", "SPDX normalisation to the repo's declared licences (code/config -> MPL-2.0; prose docs -> CC-BY-SA-4.0)"] deny = ["destructive edits without approval", "relicensing third-party / vendored / forked files", "any SPDX change whose correct licence cannot be deterministically determined (leave untouched)", "converting AGPL or other deliberate (non-drift) licences"] -notes = "ACK: haec rhodibot directive loaded (FULL enforcement, software/language repo). Auto-fix limited to docs/metadata formatting + deterministic SPDX normalisation; never edit design/ or examples/ semantics without approval; leave undeterminable licences untouched." +notes = "ACK: haec rhodibot directive loaded (mode=audit, FULL enforcement, software/language repo). While mode=audit rhodibot mutates nothing and only logs. When active: docs/metadata formatting + deterministic SPDX normalisation; never edit design/ or examples/ semantics without approval; leave undeterminable licences untouched."