# ============================================================================= # AWOOOI Agent Version Lifecycle Watch (Gitea Actions) # ============================================================================= # Weekly no-write AI Agent / SDK / framework version lifecycle readback. This # workflow validates the committed proposal queue only; it does not install SDKs, # call model APIs, write lockfiles, change workflows, send Telegram, switch # providers, replace OpenClaw, or mutate production. name: Agent Version Lifecycle Watch on: workflow_dispatch: schedule: - cron: '0 1 * * 2' # 每週二 09:00 台北 (UTC+8) jobs: version-lifecycle-watch: runs-on: awoooi-non110-ubuntu timeout-minutes: 5 steps: - name: Checkout source from Gitea env: GITEA_SOURCE_URL: http://192.168.0.110:3001/wooo/awoooi.git run: | set -euo pipefail git init . git remote remove origin 2>/dev/null || true git remote add origin "$GITEA_SOURCE_URL" git fetch --no-tags --prune --depth=1 origin "$GITHUB_SHA" git checkout --force --detach FETCH_HEAD - name: Validate no-write version lifecycle proposal id: lifecycle run: | set -euo pipefail python3 - <<'PY' import json import os from pathlib import Path candidates = sorted( Path("docs/evaluations").glob("ai_agent_version_lifecycle_update_proposal_*.json") ) if not candidates: raise SystemExit("no version lifecycle proposal snapshots found") latest = candidates[-1] with latest.open(encoding="utf-8") as handle: data = json.load(handle) if data.get("schema_version") != "ai_agent_version_lifecycle_update_proposal_v1": raise SystemExit("unexpected version lifecycle schema_version") if (data.get("program_status") or {}).get("read_only_mode") is not True: raise SystemExit("version lifecycle proposal must stay read_only") boundaries = data.get("runtime_boundaries") or {} forbidden = [ "schedule_activation_allowed", "external_market_lookup_allowed", "external_registry_lookup_allowed", "external_cve_lookup_allowed", "package_upgrade_allowed", "lockfile_write_allowed", "workflow_write_allowed", "provider_route_switch_allowed", "openclaw_replacement_allowed", "paid_api_call_allowed", "telegram_direct_send_allowed", "telegram_gateway_queue_write_allowed", "production_write_allowed", ] unsafe = [key for key in forbidden if boundaries.get(key) is not False] if unsafe: raise SystemExit(f"version lifecycle runtime boundary must stay false: {unsafe}") rollups = data.get("rollups") or {} if rollups.get("proposal_count") != len(data.get("update_proposals") or []): raise SystemExit("proposal_count rollup mismatch") if rollups.get("cadence_count") != len(data.get("cadence_matrix") or []): raise SystemExit("cadence_count rollup mismatch") required_proposals = { "ai_agent_market_primary_source_radar", "openclaw_challenger_replay_bench", "mainstream_agent_framework_version_lifecycle_review", } proposal_ids = set(rollups.get("proposal_ids") or []) missing = sorted(required_proposals - proposal_ids) if missing: raise SystemExit(f"missing required AI Agent version lifecycle proposals: {missing}") summary = { "snapshot": str(latest), "current_task_id": (data.get("program_status") or {}).get("current_task_id"), "domain_count": rollups.get("domain_count"), "proposal_count": rollups.get("proposal_count"), "cadence_count": rollups.get("cadence_count"), "approval_required_count": rollups.get("approval_required_count"), "auto_execution_allowed_count": rollups.get("auto_execution_allowed_count"), "production_write_count": rollups.get("production_write_count"), "openclaw_replacement_allowed": boundaries.get("openclaw_replacement_allowed"), } output_path = os.environ.get("GITHUB_OUTPUT") if output_path: with open(output_path, "a", encoding="utf-8") as handle: for key, value in summary.items(): handle.write(f"{key}={value}\n") step_summary_path = os.environ.get("GITHUB_STEP_SUMMARY") if step_summary_path: with open(step_summary_path, "a", encoding="utf-8") as handle: handle.write("## Agent Version Lifecycle Watch\n\n") handle.write(f"- Snapshot: {summary['snapshot']}\n") handle.write(f"- Current task: {summary['current_task_id']}\n") handle.write(f"- Domains: {summary['domain_count']}\n") handle.write(f"- Proposals: {summary['proposal_count']}\n") handle.write(f"- Cadences: {summary['cadence_count']}\n") handle.write(f"- Approval-required proposals: {summary['approval_required_count']}\n") handle.write(f"- Auto executions approved: {summary['auto_execution_allowed_count']}\n") handle.write(f"- Production writes approved: {summary['production_write_count']}\n") handle.write(f"- OpenClaw replacement allowed: {summary['openclaw_replacement_allowed']}\n") handle.write("\nPolicy: no-write proposal validation only.\n") print(json.dumps(summary, ensure_ascii=False, sort_keys=True)) PY