resolve merge conflicts for PR #40

- Remove merge conflict markers from blockchain RPC router
- Resolve dev_heartbeat.py conflicts (keep security vulnerability checks)
- Fix claim-task.py conflicts (unify TTL handling with timedelta)
- Preserve all production setup improvements from both branches
This commit is contained in:
AITBC System
2026-03-18 16:36:45 +01:00
38 changed files with 1555 additions and 15 deletions

View File

@@ -7,7 +7,7 @@ Now with TTL/lease: claims expire after 2 hours to prevent stale locks.
import os
import json
import subprocess
from datetime import datetime, timezone
from datetime import datetime, timedelta
REPO_DIR = '/opt/aitbc'
STATE_FILE = '/opt/aitbc/.claim-state.json'
@@ -17,7 +17,7 @@ MY_AGENT = os.getenv('AGENT_NAME', 'aitbc1')
ISSUE_LABELS = ['security', 'bug', 'feature', 'refactor', 'task'] # priority order
BONUS_LABELS = ['good-first-task-for-agent']
AVOID_LABELS = ['needs-design', 'blocked', 'needs-reproduction']
CLAIM_TTL_SECONDS = 7200 # 2 hours lease
CLAIM_TTL = timedelta(hours=2) # Stale claim timeout
def query_api(path, method='GET', data=None):
url = f"{API_BASE}/{path}"
@@ -125,10 +125,9 @@ def create_work_branch(issue_number, title):
return branch_name
def main():
now = datetime.utcnow().replace(tzinfo=timezone.utc)
now_iso = now.isoformat()
now = datetime.utcnow()
now_ts = now.timestamp()
print(f"[{now_iso}] Claim task cycle starting...")
print(f"[{now.isoformat()}Z] Claim task cycle starting...")
state = load_state()
current_claim = state.get('current_claim')
@@ -147,6 +146,28 @@ def main():
save_state(state)
current_claim = None
if current_claim:
claimed_at_str = state.get('claimed_at')
if claimed_at_str:
try:
# Convert 'Z' suffix to offset for fromisoformat
if claimed_at_str.endswith('Z'):
claimed_at_str = claimed_at_str[:-1] + '+00:00'
claimed_at = datetime.fromisoformat(claimed_at_str)
age = now - claimed_at
if age > CLAIM_TTL:
print(f"Claim for issue #{current_claim} is stale (age {age}). Releasing.")
# Try to delete remote claim branch
claim_branch = state.get('claim_branch', f'claim/{current_claim}')
subprocess.run(['git', 'push', 'origin', '--delete', claim_branch],
capture_output=True, cwd=REPO_DIR)
# Clear state
state = {'current_claim': None, 'claimed_at': None, 'work_branch': None}
save_state(state)
current_claim = None
except Exception as e:
print(f"Error checking claim age: {e}. Will attempt to proceed.")
if current_claim:
print(f"Already working on issue #{current_claim} (branch {state.get('work_branch')})")
return
@@ -177,19 +198,19 @@ def main():
if claim_issue(num):
assign_issue(num, MY_AGENT)
work_branch = create_work_branch(num, title)
expires_at = now_ts + CLAIM_TTL_SECONDS
expires_at = now_ts + int(CLAIM_TTL.total_seconds())
state.update({
'current_claim': num,
'claim_branch': claim_branch,
'work_branch': work_branch,
'claimed_at': now_iso,
'claimed_at': now.isoformat() + 'Z',
'expires_at': expires_at,
'issue_title': title,
'labels': labels
})
save_state(state)
print(f"✅ Claimed issue #{num}. Work branch: {work_branch} (expires {datetime.fromtimestamp(expires_at, tz=timezone.utc).isoformat()})")
add_comment(num, f"Agent `{MY_AGENT}` claiming this task with TTL {CLAIM_TTL_SECONDS/3600}h. (automated)")
print(f"✅ Claimed issue #{num}. Work branch: {work_branch} (expires {datetime.fromtimestamp(expires_at).isoformat()})")
add_comment(num, f"Agent `{MY_AGENT}` claiming this task with TTL {int(CLAIM_TTL.total_seconds())/3600}h. (automated)")
return
else:
print(f"Claim failed for #{num} (push error). Trying next...")
@@ -220,7 +241,7 @@ def cleanup_global_stale_claims(now_ts=None):
if ts_result.returncode == 0 and ts_result.stdout.strip():
commit_ts = int(ts_result.stdout.strip())
age = now_ts - commit_ts
if age > CLAIM_TTL_SECONDS:
if age > int(CLAIM_TTL.total_seconds()):
print(f"Expired claim branch: {branch} (age {age/3600:.1f}h). Deleting.")
cleanup_stale_claim(branch)
cleaned += 1