Compare commits
4 Commits
fix/1280
...
fix/1558-p
| Author | SHA1 | Date | |
|---|---|---|---|
| cbfb6ae514 | |||
| 098fe746d7 | |||
| 23b04b50eb | |||
| 205252f048 |
23
.gitea/pull_request_template.md
Normal file
23
.gitea/pull_request_template.md
Normal file
@@ -0,0 +1,23 @@
|
||||
## Description
|
||||
|
||||
<!-- What does this PR do? -->
|
||||
|
||||
## Changes
|
||||
|
||||
- [ ]
|
||||
|
||||
## Testing
|
||||
|
||||
- [ ]
|
||||
|
||||
## Reviewer Checklist
|
||||
|
||||
**IMPORTANT: Do not rubber-stamp. Verify each item below.**
|
||||
|
||||
- [ ] **PR has actual changes** — check additions, deletions, and changed files are > 0
|
||||
- [ ] **Changes match description** — the code changes match what the PR claims to do
|
||||
- [ ] **Code quality** — no obvious bugs, follows conventions, readable
|
||||
- [ ] **Tests are adequate** — new code has tests, existing tests pass
|
||||
- [ ] **Documentation updated** — if applicable
|
||||
|
||||
**By approving, I confirm I have actually reviewed the code changes in this PR.**
|
||||
40
.gitea/workflows/check-pr-changes.yml
Normal file
40
.gitea/workflows/check-pr-changes.yml
Normal file
@@ -0,0 +1,40 @@
|
||||
name: Check PR Changes
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened]
|
||||
|
||||
jobs:
|
||||
check-changes:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Check for actual changes
|
||||
run: |
|
||||
BASE="${{ github.event.pull_request.base.sha }}"
|
||||
HEAD="${{ github.event.pull_request.head.sha }}"
|
||||
|
||||
ADDITIONS=${{ github.event.pull_request.additions }}
|
||||
DELETIONS=${{ github.event.pull_request.deletions }}
|
||||
CHANGED_FILES=${{ github.event.pull_request.changed_files }}
|
||||
|
||||
echo "PR Stats: +${ADDITIONS} -${DELETIONS} files:${CHANGED_FILES}"
|
||||
|
||||
if [ "$ADDITIONS" -eq 0 ] && [ "$DELETIONS" -eq 0 ] && [ "$CHANGED_FILES" -eq 0 ]; then
|
||||
echo "::error::ZOMBIE PR detected — zero changes between base and head."
|
||||
echo "This PR has no additions, deletions, or changed files."
|
||||
echo "Please add actual changes or close this PR."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check for empty commits
|
||||
COMMITS=$(git rev-list --count "$BASE".."$HEAD" 2>/dev/null || echo "0")
|
||||
if [ "$COMMITS" -eq 0 ]; then
|
||||
echo "::warning::PR has no commits between base and head."
|
||||
fi
|
||||
|
||||
echo "PR has valid changes (+${ADDITIONS} -${DELETIONS})."
|
||||
121
bin/check_zombie_prs.py
Normal file
121
bin/check_zombie_prs.py
Normal file
@@ -0,0 +1,121 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Zombie PR Detector — scans Gitea repos for PRs with no changes.
|
||||
|
||||
Usage:
|
||||
python bin/check_zombie_prs.py
|
||||
python bin/check_zombie_prs.py --repos the-nexus timmy-home
|
||||
python bin/check_zombie_prs.py --report
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import os
|
||||
import urllib.request
|
||||
from typing import Optional
|
||||
|
||||
|
||||
def get_token() -> str:
|
||||
"""Read Gitea API token."""
|
||||
for path in ["~/.config/gitea/token", "~/.config/forge.token"]:
|
||||
expanded = os.path.expanduser(path)
|
||||
if os.path.exists(expanded):
|
||||
return open(expanded).read().strip()
|
||||
raise RuntimeError("No Gitea token found")
|
||||
|
||||
|
||||
def get_open_prs(token: str, repo: str, base_url: str) -> list:
|
||||
"""Get all open PRs for a repo."""
|
||||
url = f"{base_url}/repos/{repo}/pulls?state=open&limit=100"
|
||||
req = urllib.request.Request(url, headers={"Authorization": f"token {token}"})
|
||||
return json.loads(urllib.request.urlopen(req, timeout=30).read())
|
||||
|
||||
|
||||
def check_pr_zombie(pr: dict) -> Optional[dict]:
|
||||
"""Check if a PR is a zombie (no changes)."""
|
||||
additions = pr.get("additions", 0)
|
||||
deletions = pr.get("deletions", 0)
|
||||
changed_files = pr.get("changed_files", 0)
|
||||
|
||||
if additions == 0 and deletions == 0 and changed_files == 0:
|
||||
return {
|
||||
"number": pr["number"],
|
||||
"title": pr["title"],
|
||||
"author": pr.get("user", {}).get("login", "unknown"),
|
||||
"url": pr.get("html_url", ""),
|
||||
"created": pr.get("created_at", ""),
|
||||
"additions": additions,
|
||||
"deletions": deletions,
|
||||
"changed_files": changed_files,
|
||||
}
|
||||
return None
|
||||
|
||||
|
||||
def scan_repos(token: str, repos: list, base_url: str) -> list:
|
||||
"""Scan repos for zombie PRs."""
|
||||
zombies = []
|
||||
for repo in repos:
|
||||
try:
|
||||
prs = get_open_prs(token, repo, base_url)
|
||||
for pr in prs:
|
||||
zombie = check_pr_zombie(pr)
|
||||
if zombie:
|
||||
zombie["repo"] = repo
|
||||
zombies.append(zombie)
|
||||
except Exception as e:
|
||||
print(f" Error scanning {repo}: {e}")
|
||||
return zombies
|
||||
|
||||
|
||||
def list_org_repos(token: str, org: str, base_url: str) -> list:
|
||||
"""List all repos in an org."""
|
||||
url = f"{base_url}/orgs/{org}/repos?limit=100"
|
||||
req = urllib.request.Request(url, headers={"Authorization": f"token {token}"})
|
||||
repos = json.loads(urllib.request.urlopen(req, timeout=30).read())
|
||||
return [r["full_name"] for r in repos]
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Detect zombie PRs with no changes")
|
||||
parser.add_argument("--repos", nargs="+", help="Specific repos to scan")
|
||||
parser.add_argument("--org", default="Timmy_Foundation", help="Organization name")
|
||||
parser.add_argument("--base-url", default="https://forge.alexanderwhitestone.com/api/v1")
|
||||
parser.add_argument("--report", action="store_true", help="Generate detailed report")
|
||||
args = parser.parse_args()
|
||||
|
||||
token = get_token()
|
||||
|
||||
if args.repos:
|
||||
repos = [f"{args.org}/{r}" if "/" not in r else r for r in args.repos]
|
||||
else:
|
||||
repos = list_org_repos(token, args.org, args.base_url)
|
||||
|
||||
print(f"Scanning {len(repos)} repos...")
|
||||
zombies = scan_repos(token, repos, args.base_url)
|
||||
|
||||
if zombies:
|
||||
print(f"\nFOUND {len(zombies)} ZOMBIE PR(s):\n")
|
||||
for z in zombies:
|
||||
print(f" [{z['repo']}] #{z['number']}: {z['title']}")
|
||||
print(f" Author: {z['author']} Created: {z['created']}")
|
||||
print(f" Stats: +{z['additions']} -{z['deletions']} files:{z['changed_files']}")
|
||||
print(f" URL: {z['url']}")
|
||||
print()
|
||||
else:
|
||||
print("\nNo zombie PRs found. All clear.")
|
||||
|
||||
if args.report:
|
||||
report = {
|
||||
"scanned_repos": len(repos),
|
||||
"zombie_prs": len(zombies),
|
||||
"zombies": zombies,
|
||||
}
|
||||
report_path = os.path.expanduser("~/.hermes/reports/zombie_prs.json")
|
||||
os.makedirs(os.path.dirname(report_path), exist_ok=True)
|
||||
with open(report_path, "w") as f:
|
||||
json.dump(report, f, indent=2)
|
||||
print(f"Report saved to {report_path}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
52
docs/rubber-stamping-prevention.md
Normal file
52
docs/rubber-stamping-prevention.md
Normal file
@@ -0,0 +1,52 @@
|
||||
# Rubber-Stamping Prevention
|
||||
|
||||
## What is Rubber-Stamping?
|
||||
|
||||
Rubber-stamping is approving a PR without actually reviewing the code. This was observed in PR #359 which received 3 APPROVED reviews despite having zero changes.
|
||||
|
||||
## Why It's Bad
|
||||
|
||||
1. Wastes reviewer time
|
||||
2. Creates false sense of review quality
|
||||
3. Allows zombie PRs to appear reviewed
|
||||
|
||||
## Prevention Measures
|
||||
|
||||
### 1. CI Check (`.gitea/workflows/check-pr-changes.yml`)
|
||||
|
||||
Automated check that runs on every PR:
|
||||
- Detects PRs with no changes (0 additions, 0 deletions, 0 files changed)
|
||||
- Blocks merge if PR is a zombie
|
||||
- Provides clear error messages
|
||||
|
||||
### 2. PR Template
|
||||
|
||||
Enhanced reviewer checklist:
|
||||
- Verify PR has actual changes
|
||||
- Changes match description
|
||||
- Code quality review
|
||||
- Tests are adequate
|
||||
- Documentation is updated
|
||||
|
||||
### 3. Zombie PR Detection
|
||||
|
||||
```bash
|
||||
# Scan all repos
|
||||
python bin/check_zombie_prs.py
|
||||
|
||||
# Scan specific repos
|
||||
python bin/check_zombie_prs.py --repos the-nexus timmy-home
|
||||
|
||||
# Generate report
|
||||
python bin/check_zombie_prs.py --report
|
||||
```
|
||||
|
||||
## Testing
|
||||
|
||||
```bash
|
||||
# Create a test PR with no changes
|
||||
git checkout -b test/zombie-pr
|
||||
git commit --allow-empty -m "test: empty commit"
|
||||
git push origin test/zombie-pr
|
||||
# Create PR — CI should fail
|
||||
```
|
||||
@@ -1,111 +0,0 @@
|
||||
# Night Shift Prediction Report — April 12-13, 2026
|
||||
|
||||
## Starting State (11:36 PM)
|
||||
|
||||
```
|
||||
Time: 11:36 PM EDT
|
||||
Automation: 13 burn loops × 3min + 1 explorer × 10min + 1 backlog × 30min
|
||||
API: Nous/xiaomi/mimo-v2-pro (FREE)
|
||||
Rate: 268 calls/hour
|
||||
Duration: 7.5 hours until 7 AM
|
||||
Total expected API calls: ~2,010
|
||||
```
|
||||
|
||||
## Burn Loops Active (13 @ every 3 min)
|
||||
|
||||
| Loop | Repo | Focus |
|
||||
|------|------|-------|
|
||||
| Testament Burn | the-nexus | MUD bridge + paper |
|
||||
| Foundation Burn | all repos | Gitea issues |
|
||||
| beacon-sprint | the-nexus | paper iterations |
|
||||
| timmy-home sprint | timmy-home | 226 issues |
|
||||
| Beacon sprint | the-beacon | game issues |
|
||||
| timmy-config sprint | timmy-config | config issues |
|
||||
| the-door burn | the-door | crisis front door |
|
||||
| the-testament burn | the-testament | book |
|
||||
| the-nexus burn | the-nexus | 3D world + MUD |
|
||||
| fleet-ops burn | fleet-ops | sovereign fleet |
|
||||
| timmy-academy burn | timmy-academy | academy |
|
||||
| turboquant burn | turboquant | KV-cache compression |
|
||||
| wolf burn | wolf | model evaluation |
|
||||
|
||||
## Expected Outcomes by 7 AM
|
||||
|
||||
### API Calls
|
||||
- Total calls: ~2,010
|
||||
- Successful completions: ~1,400 (70%)
|
||||
- API errors (rate limit, timeout): ~400 (20%)
|
||||
- Iteration limits hit: ~210 (10%)
|
||||
|
||||
### Commits
|
||||
- Total commits pushed: ~800-1,200
|
||||
- Average per loop: ~60-90 commits
|
||||
- Unique branches created: ~300-400
|
||||
|
||||
### Pull Requests
|
||||
- Total PRs created: ~150-250
|
||||
- Average per loop: ~12-19 PRs
|
||||
|
||||
### Issues Filed
|
||||
- New issues created (QA, explorer): ~20-40
|
||||
- Issues closed by PRs: ~50-100
|
||||
|
||||
### Code Written
|
||||
- Estimated lines added: ~50,000-100,000
|
||||
- Estimated files created/modified: ~2,000-3,000
|
||||
|
||||
### Paper Progress
|
||||
- Research paper iterations: ~150 cycles
|
||||
- Expected paper word count growth: ~5,000-10,000 words
|
||||
- New experiment results: 2-4 additional experiments
|
||||
- BibTeX citations: 10-20 verified citations
|
||||
|
||||
### MUD Bridge
|
||||
- Bridge file: 2,875 → ~5,000+ lines
|
||||
- New game systems: 5-10 (combat tested, economy, social graph, leaderboard)
|
||||
- QA cycles: 15-30 exploration sessions
|
||||
- Critical bugs found: 3-5
|
||||
- Critical bugs fixed: 2-3
|
||||
|
||||
### Repository Activity (per repo)
|
||||
| Repo | Expected PRs | Expected Commits |
|
||||
|------|-------------|-----------------|
|
||||
| the-nexus | 30-50 | 200-300 |
|
||||
| the-beacon | 20-30 | 150-200 |
|
||||
| timmy-config | 15-25 | 100-150 |
|
||||
| the-testament | 10-20 | 80-120 |
|
||||
| the-door | 5-10 | 40-60 |
|
||||
| timmy-home | 10-20 | 80-120 |
|
||||
| fleet-ops | 5-10 | 40-60 |
|
||||
| timmy-academy | 5-10 | 40-60 |
|
||||
| turboquant | 3-5 | 20-30 |
|
||||
| wolf | 3-5 | 20-30 |
|
||||
|
||||
### Dream Cycle
|
||||
- 5 dreams generated (11:30 PM, 1 AM, 2:30 AM, 4 AM, 5:30 AM)
|
||||
- 1 reflection (10 PM)
|
||||
- 1 timmy-dreams (5:30 AM)
|
||||
- Total dream output: ~5,000-8,000 words of creative writing
|
||||
|
||||
### Explorer (every 10 min)
|
||||
- ~45 exploration cycles
|
||||
- Bugs found: 15-25
|
||||
- Issues filed: 15-25
|
||||
|
||||
### Risk Factors
|
||||
- API rate limiting: Possible after 500+ consecutive calls
|
||||
- Large file patch failures: Bridge file too large for agents
|
||||
- Branch conflicts: Multiple agents on same repo
|
||||
- Iteration limits: 5-iteration agents can't push
|
||||
- Repository cloning: May hit timeout on slow clones
|
||||
|
||||
### Confidence Level
|
||||
- High confidence: 800+ commits, 150+ PRs
|
||||
- Medium confidence: 1,000+ commits, 200+ PRs
|
||||
- Low confidence: 1,200+ commits, 250+ PRs (requires all loops running clean)
|
||||
|
||||
---
|
||||
|
||||
*This report is a prediction. The 7 AM morning report will compare actual results.*
|
||||
*Generated: 2026-04-12 23:36 EDT*
|
||||
*Author: Timmy (pre-shift prediction)*
|
||||
@@ -1,34 +0,0 @@
|
||||
# OpenClaw Purge Report — 2026-04-12
|
||||
|
||||
Directive: We are Hermes maxis. OpenClaw has been removed from the entire Timmy Foundation stack.
|
||||
|
||||
## What was done
|
||||
|
||||
### the-nexus (this repo)
|
||||
- PR #1278 — Removed OpenClaw sidecar reference from README.md (merged)
|
||||
- PR #1279 — Rewrote Fleet Vocabulary: deprecated Robing pattern, replaced with Hermes-native comms (merged)
|
||||
- Issue #830 — Updated body to remove OpenClaw references from intelligence briefing scope
|
||||
- ACTION NEEDED (rockachopa): Update org description at https://forge.alexanderwhitestone.com/Timmy_Foundation — remove `OpenClaw sidecar` from description.
|
||||
- Current: `Sovereign AI foundation — Hermes harness, canonical Nexus repo, timmy-home training ground, OpenClaw sidecar.`
|
||||
- New: `Sovereign AI foundation — Hermes harness, canonical Nexus repo, timmy-home training ground.`
|
||||
|
||||
### timmy-config (complete)
|
||||
- PR #487 — 7 files changed: code-claw-delegation.md, allegro-wizard-house.md, automation-inventory.md, capacity-inventory.md, topology.md, son-of-timmy.md, health_check.py (merged)
|
||||
|
||||
### timmy-home (complete)
|
||||
- PR #595 — 3 files changed: USER_AUDIT_2026-04-04.md, two greptard reports with deprecation headers (merged)
|
||||
- Issue #141 closed (Robing architecture superseded)
|
||||
- Issue #140 closed (lobster cutover superseded)
|
||||
|
||||
## What was preserved
|
||||
- 27 previously closed OpenClaw issues across repos — left as historical record
|
||||
- Greptard memory architecture reports — preserved with deprecation headers
|
||||
- The Robing/Lobster/Unrobed vocabulary entries — marked deprecated, not deleted
|
||||
|
||||
## Remaining action
|
||||
- Org description — needs org owner (rockachopa) to update. The perplexity account lacks org admin permission.
|
||||
|
||||
## Architecture going forward
|
||||
Hermes handles everything: cognitive core, channels, dispatch, memory, cron, health monitoring. No external gateway layer. Two states: Online or Dead. No split-brain failure modes.
|
||||
|
||||
Reference: openclaw-purge-2026-04-12
|
||||
@@ -1,25 +0,0 @@
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
REPORT = Path("reports/night-shift-prediction-2026-04-12.md")
|
||||
|
||||
|
||||
def test_prediction_report_exists_with_required_sections():
|
||||
assert REPORT.exists(), "expected night shift prediction report to exist"
|
||||
content = REPORT.read_text()
|
||||
assert "# Night Shift Prediction Report — April 12-13, 2026" in content
|
||||
assert "## Starting State (11:36 PM)" in content
|
||||
assert "## Burn Loops Active (13 @ every 3 min)" in content
|
||||
assert "## Expected Outcomes by 7 AM" in content
|
||||
assert "### Risk Factors" in content
|
||||
assert "### Confidence Level" in content
|
||||
assert "This report is a prediction" in content
|
||||
|
||||
|
||||
def test_prediction_report_preserves_core_forecast_numbers():
|
||||
content = REPORT.read_text()
|
||||
assert "Total expected API calls: ~2,010" in content
|
||||
assert "Total commits pushed: ~800-1,200" in content
|
||||
assert "Total PRs created: ~150-250" in content
|
||||
assert "the-nexus | 30-50 | 200-300" in content
|
||||
assert "Generated: 2026-04-12 23:36 EDT" in content
|
||||
@@ -1,28 +0,0 @@
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
REPORT = Path("reports/openclaw-purge-2026-04-12.md")
|
||||
|
||||
|
||||
def test_openclaw_purge_report_exists_with_required_sections():
|
||||
assert REPORT.exists(), "expected OpenClaw purge report artifact to exist"
|
||||
content = REPORT.read_text()
|
||||
assert "# OpenClaw Purge Report — 2026-04-12" in content
|
||||
assert "## What was done" in content
|
||||
assert "### the-nexus (this repo)" in content
|
||||
assert "### timmy-config (complete)" in content
|
||||
assert "### timmy-home (complete)" in content
|
||||
assert "## What was preserved" in content
|
||||
assert "## Remaining action" in content
|
||||
assert "## Architecture going forward" in content
|
||||
|
||||
|
||||
def test_openclaw_purge_report_preserves_core_findings():
|
||||
content = REPORT.read_text()
|
||||
assert "PR #1278" in content
|
||||
assert "PR #1279" in content
|
||||
assert "PR #487" in content
|
||||
assert "PR #595" in content
|
||||
assert "27 previously closed OpenClaw issues across repos" in content
|
||||
assert "The perplexity account lacks org admin permission." in content
|
||||
assert "Reference: openclaw-purge-2026-04-12" in content
|
||||
Reference in New Issue
Block a user