Compare commits
1 Commits
step35/595
...
step35/442
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d913be508b |
@@ -78,6 +78,27 @@ Long-running autonomous processes managed by launchd or tmux supervisors. Status
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
||||||
|
## Ad-hoc recovery mechanisms — superseded by Ansible IaC (#442)
|
||||||
|
|
||||||
|
The following ad-hoc recovery/drift/healing scripts have been superseded by the
|
||||||
|
canonical Ansible deployment. They remain in the repo for historical reference
|
||||||
|
only. Do not use. New recovery must go through Ansible.
|
||||||
|
|
||||||
|
| Ad-hoc script | Superseded by | Reason |
|
||||||
|
|----------------|---------------|--------|
|
||||||
|
| `scripts/config_drift.py` | `ansible/roles/golden_state` | Drift detection is unnecessary — golden_state enforces convergence |
|
||||||
|
| `scripts/config_drift_detector.py` | `ansible/roles/golden_state` | Duplicate — use golden_state playbook |
|
||||||
|
| `scripts/self_healing.py` | `ansible/roles/deadman_switch` | Self-healing is the deadman switch ACTION |
|
||||||
|
| `scripts/reset_pipeline_state.py` | `ansible/playbooks/agent_startup.yml` | Wizard resurrection is agent_startup |
|
||||||
|
| `scripts/provision_wizard.py` | `ansible/roles/wizard_base` | Provisioning is wizard_base + site.yml |
|
||||||
|
| `scripts/cron_audit.py` / `cron-audit-662.py` | `ansible/roles/cron_manager` | Cron jobs are declared in Ansible, not audited ad-hoc |
|
||||||
|
|
||||||
|
**DEPRECATION NOTICE** (2026-04-29, STEP35): All scripts above now contain
|
||||||
|
inline DEPRECATION headers pointing to their Ansible replacements. They will
|
||||||
|
be removed after fleet verification.
|
||||||
|
|
||||||
|
|
||||||
## Change log
|
## Change log
|
||||||
|
|
||||||
| Date | Change | By |
|
| Date | Change | By |
|
||||||
|
|||||||
@@ -1,3 +1,21 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
config_drift.py — DEPRECATED
|
||||||
|
|
||||||
|
Config drift detection and sync is handled by Ansible golden_state role; ad-hoc manual sync bypasses PR review.
|
||||||
|
|
||||||
|
Superseded by: ansible/playbooks/golden_state.yml + ansible/roles/golden_state
|
||||||
|
|
||||||
|
DO NOT USE. Use the Ansible canonical playbook instead:
|
||||||
|
ansible-playbook -i ansible/inventory/hosts.yml ansible/playbooks/golden_state.yml
|
||||||
|
|
||||||
|
This script remains only for historical reference. It will be removed after
|
||||||
|
fleet-wide migration is verified (issue timmy-config #442).
|
||||||
|
|
||||||
|
Issue: timmy-config #442 — [P2] Ansible IaC — Canonical Playbook for Fleet Management
|
||||||
|
Deprecated: 2026-04-29 (STEP35 FREE BURN)
|
||||||
|
"""
|
||||||
|
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
"""
|
"""
|
||||||
config_drift.py — Detect configuration drift across fleet nodes.
|
config_drift.py — Detect configuration drift across fleet nodes.
|
||||||
|
|||||||
@@ -1,3 +1,21 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
config_drift_detector.py — DEPRECATED
|
||||||
|
|
||||||
|
Config drift detection is superseded by Ansible golden_state convergence.
|
||||||
|
|
||||||
|
Superseded by: ansible/playbooks/golden_state.yml + ansible/roles/golden_state
|
||||||
|
|
||||||
|
DO NOT USE. Use the Ansible canonical playbook instead:
|
||||||
|
ansible-playbook -i ansible/inventory/hosts.yml ansible/playbooks/golden_state.yml
|
||||||
|
|
||||||
|
This script remains only for historical reference. It will be removed after
|
||||||
|
fleet-wide migration is verified (issue timmy-config #442).
|
||||||
|
|
||||||
|
Issue: timmy-config #442 — [P2] Ansible IaC — Canonical Playbook for Fleet Management
|
||||||
|
Deprecated: 2026-04-29 (STEP35 FREE BURN)
|
||||||
|
"""
|
||||||
|
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
"""
|
"""
|
||||||
config_drift_detector.py — Detect config drift across fleet nodes.
|
config_drift_detector.py — Detect config drift across fleet nodes.
|
||||||
|
|||||||
@@ -1,3 +1,21 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
cron-audit-662.py — DEPRECATED
|
||||||
|
|
||||||
|
Cron job auditing is superseded by source-controlled Ansible cron_manager.
|
||||||
|
|
||||||
|
Superseded by: ansible/playbooks/cron_schedule.yml + ansible/roles/cron_manager
|
||||||
|
|
||||||
|
DO NOT USE. Use the Ansible canonical playbook instead:
|
||||||
|
ansible-playbook -i ansible/inventory/hosts.yml ansible/playbooks/cron_schedule.yml
|
||||||
|
|
||||||
|
This script remains only for historical reference. It will be removed after
|
||||||
|
fleet-wide migration is verified (issue timmy-config #442).
|
||||||
|
|
||||||
|
Issue: timmy-config #442 — [P2] Ansible IaC — Canonical Playbook for Fleet Management
|
||||||
|
Deprecated: 2026-04-29 (STEP35 FREE BURN)
|
||||||
|
"""
|
||||||
|
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
"""
|
"""
|
||||||
Cron Fleet Audit Script — #662
|
Cron Fleet Audit Script — #662
|
||||||
|
|||||||
@@ -1,3 +1,21 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
cron_audit.py — DEPRECATED
|
||||||
|
|
||||||
|
Cron job auditing is superseded by source-controlled Ansible cron_manager; cron jobs are declared, not audited.
|
||||||
|
|
||||||
|
Superseded by: ansible/playbooks/cron_schedule.yml + ansible/roles/cron_manager
|
||||||
|
|
||||||
|
DO NOT USE. Use the Ansible canonical playbook instead:
|
||||||
|
ansible-playbook -i ansible/inventory/hosts.yml ansible/playbooks/cron_schedule.yml
|
||||||
|
|
||||||
|
This script remains only for historical reference. It will be removed after
|
||||||
|
fleet-wide migration is verified (issue timmy-config #442).
|
||||||
|
|
||||||
|
Issue: timmy-config #442 — [P2] Ansible IaC — Canonical Playbook for Fleet Management
|
||||||
|
Deprecated: 2026-04-29 (STEP35 FREE BURN)
|
||||||
|
"""
|
||||||
|
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
"""
|
"""
|
||||||
Cron Job Audit — Identify erroring jobs, categorize health, recommend actions.
|
Cron Job Audit — Identify erroring jobs, categorize health, recommend actions.
|
||||||
|
|||||||
@@ -1,3 +1,21 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
provision_wizard.py — DEPRECATED
|
||||||
|
|
||||||
|
Wizard provisioning is handled by Ansible wizard_base role; never ad-hoc.
|
||||||
|
|
||||||
|
Superseded by: ansible/playbooks/site.yml + ansible/roles/wizard_base
|
||||||
|
|
||||||
|
DO NOT USE. Use the Ansible canonical playbook instead:
|
||||||
|
ansible-playbook -i ansible/inventory/hosts.yml ansible/playbooks/site.yml
|
||||||
|
|
||||||
|
This script remains only for historical reference. It will be removed after
|
||||||
|
fleet-wide migration is verified (issue timmy-config #442).
|
||||||
|
|
||||||
|
Issue: timmy-config #442 — [P2] Ansible IaC — Canonical Playbook for Fleet Management
|
||||||
|
Deprecated: 2026-04-29 (STEP35 FREE BURN)
|
||||||
|
"""
|
||||||
|
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
"""
|
"""
|
||||||
[OPS] Automated VPS Provisioning System (Von Neumann as Code)
|
[OPS] Automated VPS Provisioning System (Von Neumann as Code)
|
||||||
|
|||||||
@@ -1,3 +1,21 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
reset_pipeline_state.py — DEPRECATED
|
||||||
|
|
||||||
|
Pipeline state reset/wizard resurrection is handled by Ansible agent_startup playbook.
|
||||||
|
|
||||||
|
Superseded by: ansible/playbooks/agent_startup.yml
|
||||||
|
|
||||||
|
DO NOT USE. Use the Ansible canonical playbook instead:
|
||||||
|
ansible-playbook -i ansible/inventory/hosts.yml ansible/playbooks/agent_startup.yml
|
||||||
|
|
||||||
|
This script remains only for historical reference. It will be removed after
|
||||||
|
fleet-wide migration is verified (issue timmy-config #442).
|
||||||
|
|
||||||
|
Issue: timmy-config #442 — [P2] Ansible IaC — Canonical Playbook for Fleet Management
|
||||||
|
Deprecated: 2026-04-29 (STEP35 FREE BURN)
|
||||||
|
"""
|
||||||
|
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
"""
|
"""
|
||||||
reset_pipeline_state.py — Daily reset for pipeline_state.json
|
reset_pipeline_state.py — Daily reset for pipeline_state.json
|
||||||
|
|||||||
@@ -1,3 +1,21 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
self_healing.py — DEPRECATED
|
||||||
|
|
||||||
|
Self-healing recovery is handled by the Ansible-deployed deadman switch service.
|
||||||
|
|
||||||
|
Superseded by: ansible/playbooks/deadman_switch.yml + ansible/roles/deadman_switch
|
||||||
|
|
||||||
|
DO NOT USE. Use the Ansible canonical playbook instead:
|
||||||
|
ansible-playbook -i ansible/inventory/hosts.yml ansible/playbooks/deadman_switch.yml
|
||||||
|
|
||||||
|
This script remains only for historical reference. It will be removed after
|
||||||
|
fleet-wide migration is verified (issue timmy-config #442).
|
||||||
|
|
||||||
|
Issue: timmy-config #442 — [P2] Ansible IaC — Canonical Playbook for Fleet Management
|
||||||
|
Deprecated: 2026-04-29 (STEP35 FREE BURN)
|
||||||
|
"""
|
||||||
|
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
"""
|
"""
|
||||||
[OPS] Self-Healing Infrastructure
|
[OPS] Self-Healing Infrastructure
|
||||||
|
|||||||
151
tests/test_ansible_structure.py
Normal file
151
tests/test_ansible_structure.py
Normal file
@@ -0,0 +1,151 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
Smoke test — verify ansible/ directory has required canonical structure.
|
||||||
|
Issue: timmy-config #442 — [P2] Ansible IaC — Canonical Playbook for Fleet Management
|
||||||
|
"""
|
||||||
|
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
REPO_ROOT = Path(__file__).parent.parent
|
||||||
|
ANSIBLE_DIR = REPO_ROOT / "ansible"
|
||||||
|
|
||||||
|
def test_ansible_readme_exists():
|
||||||
|
"""README.md exists and is the single source of truth declaration."""
|
||||||
|
assert (ANSIBLE_DIR / "README.md").exists()
|
||||||
|
|
||||||
|
def test_ansible_cfg_exists():
|
||||||
|
"""ansible.cfg exists with inventory configured."""
|
||||||
|
cfg_path = ANSIBLE_DIR / "ansible.cfg"
|
||||||
|
assert cfg_path.exists()
|
||||||
|
content = cfg_path.read_text()
|
||||||
|
assert "inventory = inventory/hosts.yml" in content
|
||||||
|
|
||||||
|
def test_banned_providers_exists():
|
||||||
|
"""BANNED_PROVIDERS.yml exists to enforce the Anthropic ban."""
|
||||||
|
assert (ANSIBLE_DIR / "BANNED_PROVIDERS.yml").exists()
|
||||||
|
|
||||||
|
def test_inventory_hosts_exists_and_lists_all_wizards():
|
||||||
|
"""inventory/hosts.yml lists all fleet wizards."""
|
||||||
|
hosts_file = ANSIBLE_DIR / "inventory/hosts.yml"
|
||||||
|
assert hosts_file.exists()
|
||||||
|
content = hosts_file.read_text()
|
||||||
|
for wizard in ["timmy", "allegro", "bezalel", "ezra"]:
|
||||||
|
assert wizard in content, f"missing wizard: {wizard}"
|
||||||
|
|
||||||
|
def test_inventory_group_vars_exists():
|
||||||
|
"""inventory/group_vars/wizards.yml with deadman/cron config."""
|
||||||
|
gv = ANSIBLE_DIR / "inventory/group_vars/wizards.yml"
|
||||||
|
assert gv.exists()
|
||||||
|
content = gv.read_text()
|
||||||
|
# Should define deadman settings and cron_jobs (golden_state_providers is in hosts.yml)
|
||||||
|
assert "deadman_enabled" in content
|
||||||
|
assert "cron_jobs:" in content
|
||||||
|
|
||||||
|
def test_playbooks_all_exist():
|
||||||
|
"""All required playbooks are present."""
|
||||||
|
required = [
|
||||||
|
"site.yml",
|
||||||
|
"deadman_switch.yml",
|
||||||
|
"golden_state.yml",
|
||||||
|
"agent_startup.yml",
|
||||||
|
"cron_schedule.yml",
|
||||||
|
"request_log.yml",
|
||||||
|
]
|
||||||
|
for pb in required:
|
||||||
|
assert (ANSIBLE_DIR / "playbooks" / pb).exists(), f"missing playbook: {pb}"
|
||||||
|
|
||||||
|
def test_site_yml_orchestrates_all_roles():
|
||||||
|
"""site.yml orchestrates wizard_base, golden_state, deadman_switch, request_log, cron_manager."""
|
||||||
|
site = (ANSIBLE_DIR / "playbooks/site.yml").read_text()
|
||||||
|
for role in ["wizard_base", "golden_state", "deadman_switch", "request_log", "cron_manager"]:
|
||||||
|
assert f"role: {role}" in site, f"site.yml missing role: {role}"
|
||||||
|
|
||||||
|
def test_golden_state_role_scans_for_banned_providers():
|
||||||
|
"""golden_state role includes banned provider detection."""
|
||||||
|
tasks = (ANSIBLE_DIR / "roles/golden_state/tasks/main.yml").read_text()
|
||||||
|
# Should grep for anthropic/claude patterns
|
||||||
|
assert "grep" in tasks.lower() and "anthropic" in tasks.lower()
|
||||||
|
|
||||||
|
def test_deadman_switch_role_has_templates():
|
||||||
|
"""deadman_switch role includes systemd timer and service templates."""
|
||||||
|
role_dir = ANSIBLE_DIR / "roles/deadman_switch/templates"
|
||||||
|
assert (role_dir / "deadman_switch.service.j2").exists()
|
||||||
|
assert (role_dir / "deadman_switch.timer.j2").exists()
|
||||||
|
assert (role_dir / "deadman_action.sh.j2").exists()
|
||||||
|
|
||||||
|
def test_cron_manager_role_exists():
|
||||||
|
"""cron_manager role exists and deploys managed cron jobs."""
|
||||||
|
assert (ANSIBLE_DIR / "roles/cron_manager/tasks/main.yml").exists()
|
||||||
|
tasks = (ANSIBLE_DIR / "roles/cron_manager/tasks/main.yml").read_text()
|
||||||
|
assert "cron:" in tasks
|
||||||
|
|
||||||
|
def test_request_log_role_has_schema():
|
||||||
|
"""request_log role includes SQLite schema."""
|
||||||
|
assert (ANSIBLE_DIR / "roles/request_log/files/request_log_schema.sql").exists()
|
||||||
|
|
||||||
|
def test_webhook_deploy_script_exists():
|
||||||
|
"""ansible/scripts/deploy_on_webhook.sh exists for automated deployment."""
|
||||||
|
script = ANSIBLE_DIR / "scripts" / "deploy_on_webhook.sh"
|
||||||
|
assert script.exists()
|
||||||
|
content = script.read_text()
|
||||||
|
assert "ansible-pull" in content
|
||||||
|
|
||||||
|
def test_config_validator_exists():
|
||||||
|
"""ansible/scripts/validate_config.py validates configs against golden state."""
|
||||||
|
validator = ANSIBLE_DIR / "scripts" / "validate_config.py"
|
||||||
|
assert validator.exists()
|
||||||
|
content = validator.read_text()
|
||||||
|
assert "BANNED_PROVIDERS" in content or "banned_providers" in content
|
||||||
|
|
||||||
|
def test_readme_declares_canonical_status_and_webhook():
|
||||||
|
"""README declares ansible/ as single source of truth and documents webhook."""
|
||||||
|
readme = (ANSIBLE_DIR / "README.md").read_text()
|
||||||
|
assert "single source of truth" in readme.lower()
|
||||||
|
assert "webhook" in readme.lower() or "deploy_on_webhook" in readme.lower()
|
||||||
|
|
||||||
|
def test_readme_documents_quick_start():
|
||||||
|
"""README includes Quick Start usage examples."""
|
||||||
|
readme = (ANSIBLE_DIR / "README.md").read_text()
|
||||||
|
assert "ansible-playbook" in readme.lower()
|
||||||
|
|
||||||
|
def test_golden_state_enforces_banned_providers_in_playbook():
|
||||||
|
"""golden_state.yml playbook or role enforces banned provider policy."""
|
||||||
|
gs_content = (ANSIBLE_DIR / "playbooks" / "golden_state.yml").read_text()
|
||||||
|
# The playbook either has the check directly or includes the role that does
|
||||||
|
# We know the role does, so this passes if role is included
|
||||||
|
assert "role: golden_state" in gs_content
|
||||||
|
|
||||||
|
def test_agent_startup_sequence_is_serial():
|
||||||
|
"""agent_startup.yml uses serial execution to avoid cascading."""
|
||||||
|
startup = (ANSIBLE_DIR / "playbooks/agent_startup.yml").read_text()
|
||||||
|
assert "serial:" in startup
|
||||||
|
|
||||||
|
def test_golden_state_providers_defined_in_hosts():
|
||||||
|
"""Golden state provider chain is defined in inventory."""
|
||||||
|
hosts = (ANSIBLE_DIR / "inventory/hosts.yml").read_text()
|
||||||
|
assert "golden_state_providers:" in hosts
|
||||||
|
assert "kimi" in hosts.lower()
|
||||||
|
assert "openrouter" in hosts.lower() or "anthropic" not in hosts.lower()
|
||||||
|
|
||||||
|
def test_ad_hoc_recovery_scripts_have_deprecation_headers():
|
||||||
|
"""Overlapping ad-hoc recovery scripts are marked DEPRECATED with Ansible alternatives."""
|
||||||
|
scripts_dir = REPO_ROOT / "scripts"
|
||||||
|
deprecation_targets = [
|
||||||
|
"config_drift.py",
|
||||||
|
"config_drift_detector.py",
|
||||||
|
"self_healing.py",
|
||||||
|
"reset_pipeline_state.py",
|
||||||
|
"provision_wizard.py",
|
||||||
|
"cron_audit.py",
|
||||||
|
"cron-audit-662.py",
|
||||||
|
]
|
||||||
|
for script in deprecation_targets:
|
||||||
|
path = scripts_dir / script
|
||||||
|
assert path.exists(), f"missing script: {script}"
|
||||||
|
content = path.read_text()
|
||||||
|
assert "DEPRECATED" in content[:500], f"{script} missing DEPRECATED header"
|
||||||
|
cl = content.lower()
|
||||||
|
assert "superseded" in cl and "ansible" in cl, f"{script} missing ansible supersession"
|
||||||
|
assert "timmy-config #442" in content, f"{script} missing issue reference"
|
||||||
|
|
||||||
Reference in New Issue
Block a user