#33 은 위임 잡의 예산(기본 3600초)을 조절할 수 있게 하고, 벽시계 초과(wall_clock_timeout)와 무응답(deadline_timeout)을 구분하는 것으로 해결한다. #33 댓글에 있던 예산 연장(벽시계에 닿으면 멈춰서 묻고, 정책이 연장 여부를 정함)은 #30·#33 묶음 작업의 설계 검토에서 범위 밖으로 뺐다. 이 이슈는 그 연장을 다른 방식으로 다시 다룬다.
댓글의 원래 계획으로는 안 되는 이유
루프 러너는 결정을 내릴 위치에 있지 않다. 계획은 래퍼가 결정 파일을 기다리고 루프 러너가 정책으로 답한다고 했다. 그런데 러너는 delegate-job 래퍼를 동기 호출하고(lib_py/loop/runner.py 의 subprocess.run), 래퍼는 잡이 끝날 때까지 반환하지 않는다. 러너가 답을 쓸 수 없으니 기다림은 늘 유예 만료로 끝난다.
연장 거부 근거로 제안된 state_change_seq 정체는 연장이 필요한 사례를 거부한다.#33 의 동기 사례는 creator 가 전체 스위트를 두 번 돌리며 한 시간 넘게 같은 상태에 머문 경우다. 상태 전환이 없으니 순번도 멈춰 있다. 반대로 codex 가 "Reconnecting…" 에 3시간 갇힌 경우도 working 에 순번 정체다. herdr 신호로는 둘을 구분할 수 없다.
하트비트는 근거가 될 수 없다. 타이머는 에이전트 프로세스에 묶여 있어 프로세스 생존만 증명한다(#18 의 의도된 절충). 위 codex 사례에서도 30초마다 정상 발행됐다.
제안 — 에이전트가 스스로 연장을 요청한다
긴 작업을 하는 에이전트는 자기가 오래 걸린다는 것을 안다. 예를 들어 전체 스위트를 두 번 돌리는 계획을 세운 creator 다. 그러니 연장 여부를 바깥에서 추측하지 말고, 에이전트가 프로토콜 이벤트로 요청하게 한다. #18 의 원칙("잡의 운명을 바꾸는 근거는 에이전트가 보낸 프로토콜 이벤트뿐")과 맞고, herdr 가 없는 원격 잡에서도 그대로 동작한다.
에이전트가 publish_event.py 로 연장 요청 이벤트를 보낸다. 요청에는 원하는 추가 시간과 이유를 담는다. 다른 이벤트처럼 디스패치 토큰과 서명에 묶인다.
판정은 Python(구독자)이 한다. 벽시계에 닿기 전에 받은 요청만, 상한 안에서만 받아들이고, 받아들인 새 기한을 잡 레코드에 기록한다.
루프 러너와 #34 의 하위 잡 대기는 고정 상한 대신 잡 레코드의 현재 기한을 따라간다. #33 에서 대기 상한을 예산에서 도출하게 바꾼 것의 연장선이다.
코드로 강제할 것
지침으로 남기지 않고 코드와 테스트로 고정한다.
연장 횟수와 총량의 상한(예: MAM_JOB_EXTEND_MAX). 상한을 넘는 요청은 거부 이벤트로 남긴다.
기한이 지난 뒤의 요청은 거부한다. 연장은 잡을 되살리는 수단이 아니다.
이유가 빈 요청은 거부한다.
받아들여진 연장과 거부된 연장이 잡 레코드와 이벤트에 남아, 사후에 "왜 이 잡이 1시간을 넘겼는가"를 한 줄로 알 수 있게 한다.
에이전트가 요청 방법을 알게 하는 통로는 잡마다 주입되는 안내문과 발행 명령이다(#34 의 --parent-job 과 같은 방식). 에이전트에게 문서를 읽으라고 기대하지 않는다.
## 배경
#33 은 위임 잡의 예산(기본 3600초)을 조절할 수 있게 하고, 벽시계 초과(`wall_clock_timeout`)와 무응답(`deadline_timeout`)을 구분하는 것으로 해결한다. #33 댓글에 있던 **예산 연장**(벽시계에 닿으면 멈춰서 묻고, 정책이 연장 여부를 정함)은 #30·#33 묶음 작업의 설계 검토에서 범위 밖으로 뺐다. 이 이슈는 그 연장을 다른 방식으로 다시 다룬다.
## 댓글의 원래 계획으로는 안 되는 이유
1. **루프 러너는 결정을 내릴 위치에 있지 않다.** 계획은 래퍼가 결정 파일을 기다리고 루프 러너가 정책으로 답한다고 했다. 그런데 러너는 delegate-job 래퍼를 동기 호출하고(`lib_py/loop/runner.py` 의 `subprocess.run`), 래퍼는 잡이 끝날 때까지 반환하지 않는다. 러너가 답을 쓸 수 없으니 기다림은 늘 유예 만료로 끝난다.
2. **연장 거부 근거로 제안된 `state_change_seq` 정체는 연장이 필요한 사례를 거부한다.** #33 의 동기 사례는 creator 가 전체 스위트를 두 번 돌리며 한 시간 넘게 같은 상태에 머문 경우다. 상태 전환이 없으니 순번도 멈춰 있다. 반대로 codex 가 "Reconnecting…" 에 3시간 갇힌 경우도 `working` 에 순번 정체다. herdr 신호로는 둘을 구분할 수 없다.
3. **하트비트는 근거가 될 수 없다.** 타이머는 에이전트 프로세스에 묶여 있어 프로세스 생존만 증명한다(#18 의 의도된 절충). 위 codex 사례에서도 30초마다 정상 발행됐다.
## 제안 — 에이전트가 스스로 연장을 요청한다
긴 작업을 하는 에이전트는 자기가 오래 걸린다는 것을 안다. 예를 들어 전체 스위트를 두 번 돌리는 계획을 세운 creator 다. 그러니 연장 여부를 바깥에서 추측하지 말고, 에이전트가 **프로토콜 이벤트로 요청**하게 한다. #18 의 원칙("잡의 운명을 바꾸는 근거는 에이전트가 보낸 프로토콜 이벤트뿐")과 맞고, herdr 가 없는 원격 잡에서도 그대로 동작한다.
- 에이전트가 `publish_event.py` 로 연장 요청 이벤트를 보낸다. 요청에는 원하는 추가 시간과 이유를 담는다. 다른 이벤트처럼 디스패치 토큰과 서명에 묶인다.
- 판정은 Python(구독자)이 한다. 벽시계에 닿기 전에 받은 요청만, 상한 안에서만 받아들이고, 받아들인 새 기한을 잡 레코드에 기록한다.
- 루프 러너와 #34 의 하위 잡 대기는 고정 상한 대신 잡 레코드의 현재 기한을 따라간다. #33 에서 대기 상한을 예산에서 도출하게 바꾼 것의 연장선이다.
## 코드로 강제할 것
지침으로 남기지 않고 코드와 테스트로 고정한다.
- 연장 횟수와 총량의 상한(예: `MAM_JOB_EXTEND_MAX`). 상한을 넘는 요청은 거부 이벤트로 남긴다.
- 기한이 지난 뒤의 요청은 거부한다. 연장은 잡을 되살리는 수단이 아니다.
- 이유가 빈 요청은 거부한다.
- 받아들여진 연장과 거부된 연장이 잡 레코드와 이벤트에 남아, 사후에 "왜 이 잡이 1시간을 넘겼는가"를 한 줄로 알 수 있게 한다.
- 에이전트가 요청 방법을 알게 하는 통로는 잡마다 주입되는 안내문과 발행 명령이다(#34 의 `--parent-job` 과 같은 방식). 에이전트에게 문서를 읽으라고 기대하지 않는다.
## 검토할 점
- #30 의 `stalled:idle` 이 난 잡이 연장을 요청하면 어떻게 할지.
- 하위 잡(#34)이 연장되면 부모 잡의 대기에 어떻게 반영할지.
- 사람이 직접 위임한 잡(루프 밖)의 연장을 오케스트레이터가 승인·거부할 수단이 필요한지.
## 관련
- #33 벽시계 예산과 라벨 (이 기능을 분리해 낸 곳)
- #30 살아 있지만 멈춘 에이전트의 알림
- #18 생존·전달 판정의 프로토콜 계약
- #17 항목 4 래퍼 종결 처리의 Python 이전 (연장 판정이 Python 에 있어야 하는 이유)
- #34 하위 위임 잡 대기
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
배경
#33 은 위임 잡의 예산(기본 3600초)을 조절할 수 있게 하고, 벽시계 초과(
wall_clock_timeout)와 무응답(deadline_timeout)을 구분하는 것으로 해결한다. #33 댓글에 있던 예산 연장(벽시계에 닿으면 멈춰서 묻고, 정책이 연장 여부를 정함)은 #30·#33 묶음 작업의 설계 검토에서 범위 밖으로 뺐다. 이 이슈는 그 연장을 다른 방식으로 다시 다룬다.댓글의 원래 계획으로는 안 되는 이유
lib_py/loop/runner.py의subprocess.run), 래퍼는 잡이 끝날 때까지 반환하지 않는다. 러너가 답을 쓸 수 없으니 기다림은 늘 유예 만료로 끝난다.state_change_seq정체는 연장이 필요한 사례를 거부한다. #33 의 동기 사례는 creator 가 전체 스위트를 두 번 돌리며 한 시간 넘게 같은 상태에 머문 경우다. 상태 전환이 없으니 순번도 멈춰 있다. 반대로 codex 가 "Reconnecting…" 에 3시간 갇힌 경우도working에 순번 정체다. herdr 신호로는 둘을 구분할 수 없다.제안 — 에이전트가 스스로 연장을 요청한다
긴 작업을 하는 에이전트는 자기가 오래 걸린다는 것을 안다. 예를 들어 전체 스위트를 두 번 돌리는 계획을 세운 creator 다. 그러니 연장 여부를 바깥에서 추측하지 말고, 에이전트가 프로토콜 이벤트로 요청하게 한다. #18 의 원칙("잡의 운명을 바꾸는 근거는 에이전트가 보낸 프로토콜 이벤트뿐")과 맞고, herdr 가 없는 원격 잡에서도 그대로 동작한다.
publish_event.py로 연장 요청 이벤트를 보낸다. 요청에는 원하는 추가 시간과 이유를 담는다. 다른 이벤트처럼 디스패치 토큰과 서명에 묶인다.코드로 강제할 것
지침으로 남기지 않고 코드와 테스트로 고정한다.
MAM_JOB_EXTEND_MAX). 상한을 넘는 요청은 거부 이벤트로 남긴다.--parent-job과 같은 방식). 에이전트에게 문서를 읽으라고 기대하지 않는다.검토할 점
stalled:idle이 난 잡이 연장을 요청하면 어떻게 할지.관련