{"definition_raw":"---\nid: log-rotate\nname: Rotate oversized workspace logs (copytruncate, keep 4)\nschedule: \"40 4 * * *\"\ntimeout: 300\nretry: false\nenabled: true\ncriticality: medium\nrun_as: script\ncommand: python3 ~/workspace/scripts/rotate_logs.py\ndescription: Daily copytruncate rotation for ~/workspace/logs (MC-4942 U11 item 16). No sudo / no logrotate on this box, so a pure-python rotator gzips every top-level logs/*.log over 20MB to <name>.log.1.gz (keeping 4 archives) and truncates the live file in place. Safe for the O_APPEND writers that own these logs (systemd append:, shell >>); files modified in the last 5s are skipped (a 60s guard would permanently exempt scheduler.log, which the tick touches every minute).\nruntime_profile: direct_python\nnotify_on: failure\nnotify_to: elmar\ntags: [infrastructure, logs, cleanup]\n---\n\n**OVERRIDES runtime profile:** uses `direct_python` (plain Python, no model)\nbecause the script is a stdlib-only file rotator \u2014 it never invokes the\n`claude` CLI or any LLM API, so the scheduler must not inject a provider env.\n\nDaily gzip rotation of oversized workspace logs. Copytruncate semantics: the\nlive file is gzip-copied to `<name>.log.1.gz` and then `os.truncate()`d in\nplace, so long-running writers (scheduler systemd unit, tmux pipe-pane, cron\n`>>` redirects \u2014 all O_APPEND, verified via /proc fdinfo) keep writing into\nthe truncated file without restart. Retention: `.1.gz` (newest) through\n`.4.gz` (oldest); the oldest archive is dropped on each rotation.\n\nManual run / dry-run:\n\n```bash\npython3 ~/workspace/scripts/rotate_logs.py --dry-run\npython3 ~/workspace/scripts/rotate_logs.py --size-mb 20 --keep 4\n```\n","id":"log-rotate","last_run":{"duration_s":0.090684,"log_path":"/home/lucienne/workspace/logs/task-runs/log-rotate/413607.log","output":"nothing to rotate in /home/lucienne/workspace/logs (threshold 20MB, keep 4)\n","started_at":"2026-06-13T04:40:01.034472+02:00","status":"completed"},"next_run":"2026-06-14 04:40","next_run_iso":"2026-06-14T04:40:00+02:00","runs":[{"duration_s":0.090684,"finished_at":"2026-06-13T04:40:01.127398+02:00","id":413607,"log_path":"/home/lucienne/workspace/logs/task-runs/log-rotate/413607.log","output":"nothing to rotate in /home/lucienne/workspace/logs (threshold 20MB, keep 4)\n","started_at":"2026-06-13T04:40:01.034472+02:00","status":"completed","task_id":"log-rotate","task_name":"log-rotate"},{"duration_s":0.110763,"finished_at":"2026-06-12T04:40:01.131491+02:00","id":409440,"log_path":"/home/lucienne/workspace/logs/task-runs/log-rotate/409440.log","output":"nothing to rotate in /home/lucienne/workspace/logs (threshold 20MB, keep 4)\n","started_at":"2026-06-12T04:40:01.017780+02:00","status":"completed","task_id":"log-rotate","task_name":"log-rotate"},{"duration_s":2.513776,"finished_at":"2026-06-11T10:15:06.972704+02:00","id":406067,"log_path":"/home/lucienne/workspace/logs/task-runs/log-rotate/406067.log","output":"rotated persistent-luci.tmux.log (68.4MB) -> persistent-luci.tmux.log.1.gz (5.3MB), truncated in place\n","started_at":"2026-06-11T10:15:04.454806+02:00","status":"completed","task_id":"log-rotate","task_name":"log-rotate"}],"runs_limit":20,"schedule":"40 4 * * *","schedule_label":{"description":"Daily at 04:40","is_custom":false,"label":"Daily","sort":4,"sort_time":"04:40"},"stats":{"avg_duration":0.9050743333333333,"completed":3,"failed":0,"timeout":0,"total":3},"task":{"_description":"**OVERRIDES runtime profile:** uses `direct_python` (plain Python, no model)\nbecause the script is a stdlib-only file rotator \u2014 it never invokes the\n`claude` CLI or any LLM API, so the scheduler must not inject a provider env.\n\nDaily gzip rotation of oversized workspace logs. Copytruncate semantics: the\nlive file is gzip-copied to `<name>.log.1.gz` and then `os.truncate()`d in\nplace, so long-running writers (scheduler systemd unit, tmux pipe-pane, cron\n`>>` redirects \u2014 all O_APPEND, verified via /proc fdinfo) keep writing into\nthe truncated file without restart. Retention: `.1.gz` (newest) through\n`.4.gz` (oldest); the oldest archive is dropped on each rotation.\n\nManual run / dry-run:\n\n```bash\npython3 ~/workspace/scripts/rotate_logs.py --dry-run\npython3 ~/workspace/scripts/rotate_logs.py --size-mb 20 --keep 4\n```","_file":"log-rotate.md","_path":"/home/lucienne/workspace/tasks/log-rotate.md","command":"python3 ~/workspace/scripts/rotate_logs.py","criticality":"medium","description":"Daily copytruncate rotation for ~/workspace/logs (MC-4942 U11 item 16). No sudo / no logrotate on this box, so a pure-python rotator gzips every top-level logs/*.log over 20MB to <name>.log.1.gz (keeping 4 archives) and truncates the live file in place. Safe for the O_APPEND writers that own these logs (systemd append:, shell >>); files modified in the last 5s are skipped (a 60s guard would permanently exempt scheduler.log, which the tick touches every minute).","enabled":true,"id":"log-rotate","name":"Rotate oversized workspace logs (copytruncate, keep 4)","notify_on":"failure","notify_to":"elmar","retry":false,"run_as":"script","runtime_profile":"direct_python","schedule":"40 4 * * *","tags":["infrastructure","logs","cleanup"],"timeout":300}}
