[verified] Document tracked bounded evidence execution

This commit is contained in:
2026-10-12 08:02:25 +00:00
parent 62d2262b27
commit 32a3c1ad44
2 changed files with 26 additions and 0 deletions
@@ -368,6 +368,16 @@ PR/결과 보고에는 stage/run-id, source SHA, manifest·plan digest, 실제 c
Remote branch/PR head를 authoritative하게 읽어 source SHA·exact artifact와 결합하고 cleanup 전 실제 record readback을 확인한다. Helper의 local validate가 Gitea readback을 대신하지 않는다. Checkout 제거와 verification 폐기는 분리하며 완료 기록은 보존한다. 기존 v1은 `validate-legacy`의 read-only 호환 경로만 사용하고 새 publication proof로 사용하지 않는다. Remote branch/PR head를 authoritative하게 읽어 source SHA·exact artifact와 결합하고 cleanup 전 실제 record readback을 확인한다. Helper의 local validate가 Gitea readback을 대신하지 않는다. Checkout 제거와 verification 폐기는 분리하며 완료 기록은 보존한다. 기존 v1은 `validate-legacy`의 read-only 호환 경로만 사용하고 새 publication proof로 사용하지 않는다.
### Tracked bounded background evidence execution
Full suite가 포함되어 foreground tool 시간창을 넘을 것으로 예상되는 evidence plan은 기존 실행 도구의 **tracked background** 작업으로 시작한다. 새 scheduler나 process manager를 만들지 않는다. 실행 직전에 exact run-id, source SHA, plan digest, output path와 command를 기록하고 시작 응답의 process/session handle을 결합해 보존한다. Handle이나 PID만으로 source/admission 또는 실행 성공을 판정하지 않는다. Broker Issue lease와 기존 admission 조건은 그대로 유지하며, lease를 유지·검증할 수 없으면 외부 handoff를 차단한다.
Background도 각 command의 `timeout_seconds`를 유지하고, main이 plan의 command deadline·정리 예산·실행 overhead를 고려해 유한한 outer deadline을 명시한다. 기존 도구의 tracked wait/poll·완료 통지를 사용하되 outer deadline 만료 시에는 기존 bounded process-group cleanup과 wait/reap 절차를 적용한다. Notification이나 manifest 파일의 존재만으로 완료를 선언하지 않는다. 실행 도구가 필요한 추적·deadline·정리 capability를 제공하지 않으면 시작하지 않고 blocker를 보고한다. Background가 세션 종료 이후의 지속성을 보장하지 않는다.
Foreground 응답 timeout이나 통지 누락은 실제 evidence process 종료 또는 제품 assertion 실패의 증거가 아니다. 자동 retry하지 않는다. 재실행 판단 전에 보존한 handle로 기존 실행의 상태·실제 exit code·process 종료와 cleanup을 확인하고, 같은 run-id의 manifest를 exact source SHA·plan digest·output path에 결합해 canonical `validate`로 확인한다. Validation은 process 종료·cleanup 확인을 대신하지 않는다. Tool 관측과 artifact가 상충하거나 실행 중·상태 불명·cleanup 미확인이면 `pending/partial`로 유지하고 중복 실행하지 않는다. 같은 실행의 상태 확인과 기록 정리만 계속하며 완료된 suite를 다시 수행하지 않는다.
종료·cleanup이 확인된 성공은 기존 evidence를 검증해 재사용한다. 실패는 실제 exit code와 bounded 부분 출력·termination 기록을 보존한다. Timeout·출력 초과·cleanup 실패를 TDD RED로 보고하지 않는다. 실패한 manifest가 `validate`를 통과하지 못하더라도 그 자체를 재실행 허가로 삼지 않는다. 기존 실행이 종료되고 cleanup이 확인된 실패에 대해서만 별도 승인 후 새 run-id로 실행한다. 기존 artifact를 덮어쓰지 않는다. 이 절차는 workflow 문서 계약이며 evidence runner/schema, lease/TTL, publication journal 및 자동 retry 정책을 변경하지 않는다.
Command deadline 계약: 새 plan의 각 command는 `timeout_seconds`를 명시한다. Boolean·문자열·누락·비유한 값·0 이하·3600 초 초과를 실행 전에 거절하며 암묵적인 무제한 실행을 허용하지 않는다. Monotonic wall-clock deadline은 무출력 대기, stdout EOF 이후 실행, leader 종료 뒤 descendant가 유지한 pipe까지 적용한다. 각 command는 별도 session/process group에서 실행하고 timeout·출력 초과·예외 시 해당 process group을 TERM → 제한된 grace → KILL로 정리하고 direct child를 bounded wait로 회수한다. Leader가 종료됐어도 같은 group의 살아 있는 descendant를 남기지 않는다. 이것은 sandbox가 아니며 별도 session으로 이탈한 descendant의 containment나 grandchild 직접 회수를 보장하지 않는다. Command deadline 계약: 새 plan의 각 command는 `timeout_seconds`를 명시한다. Boolean·문자열·누락·비유한 값·0 이하·3600 초 초과를 실행 전에 거절하며 암묵적인 무제한 실행을 허용하지 않는다. Monotonic wall-clock deadline은 무출력 대기, stdout EOF 이후 실행, leader 종료 뒤 descendant가 유지한 pipe까지 적용한다. 각 command는 별도 session/process group에서 실행하고 timeout·출력 초과·예외 시 해당 process group을 TERM → 제한된 grace → KILL로 정리하고 direct child를 bounded wait로 회수한다. Leader가 종료됐어도 같은 group의 살아 있는 descendant를 남기지 않는다. 이것은 sandbox가 아니며 별도 session으로 이탈한 descendant의 containment나 grandchild 직접 회수를 보장하지 않는다.
`evidence-manifest/v3`는 실제 timeout, `execution_status`, `termination`의 `term_sent`·`kill_sent`·`direct_child_reaped`·`group_cleanup_completed` boolean 기록과 안전한 bounded 부분 출력을 결합한다. 새 claim pointer는 기존 6개에 timeout·실행 상태·종료 기록을 추가한 9개다. Linux `/proc`로 같은 group의 non-zombie 프로세스를 확인하며 확인·정리에 실패하면 `cleanup_failed`로 차단한다. TERM grace는 0.25 초, KILL 후 group 종료·direct-child 회수 예산은 0.5 초이며 오류 fallback wait도 0.1 초로 제한한다. 새 command의 `max_output_bytes`는 0–1048576 범위의 non-boolean 정수만 허용한다. `execution_status`는 `completed|timeout|output_limit|cleanup_failed|descendant_cleanup`이다. 정상 종료 leader 뒤의 살아 있는 descendant를 신호로 정리한 경우도 `descendant_cleanup` 실패이며, `completed`에는 TERM/KILL 신호가 있을 수 없다. Timeout·출력 한도 초과·정리 실패는 expected exit code와 같아도 성공이 아니며 **TDD RED** 또는 제품 assertion 실패로 분류하지 않는다. 해당 실행은 실패로 보존하고 뒤의 command를 실행하지 않으며, 실행된 prefix의 claim만 기록한다. Partial output에도 기존 secret 탐지를 적용하고 인증정보 유출은 fail-closed한다. 자동 재실행은 하지 않으며 새로 승인된 실행에는 새 run-id를 사용한다. `evidence-manifest/v3`는 실제 timeout, `execution_status`, `termination`의 `term_sent`·`kill_sent`·`direct_child_reaped`·`group_cleanup_completed` boolean 기록과 안전한 bounded 부분 출력을 결합한다. 새 claim pointer는 기존 6개에 timeout·실행 상태·종료 기록을 추가한 9개다. Linux `/proc`로 같은 group의 non-zombie 프로세스를 확인하며 확인·정리에 실패하면 `cleanup_failed`로 차단한다. TERM grace는 0.25 초, KILL 후 group 종료·direct-child 회수 예산은 0.5 초이며 오류 fallback wait도 0.1 초로 제한한다. 새 command의 `max_output_bytes`는 0–1048576 범위의 non-boolean 정수만 허용한다. `execution_status`는 `completed|timeout|output_limit|cleanup_failed|descendant_cleanup`이다. 정상 종료 leader 뒤의 살아 있는 descendant를 신호로 정리한 경우도 `descendant_cleanup` 실패이며, `completed`에는 TERM/KILL 신호가 있을 수 없다. Timeout·출력 한도 초과·정리 실패는 expected exit code와 같아도 성공이 아니며 **TDD RED** 또는 제품 assertion 실패로 분류하지 않는다. 해당 실행은 실패로 보존하고 뒤의 command를 실행하지 않으며, 실행된 prefix의 claim만 기록한다. Partial output에도 기존 secret 탐지를 적용하고 인증정보 유출은 fail-closed한다. 자동 재실행은 하지 않으며 새로 승인된 실행에는 새 run-id를 사용한다.
@@ -54,6 +54,22 @@ class VerificationContractDocumentationTests(unittest.TestCase):
self.assertIn("-B", plan["commands"][0]["argv"]) self.assertIn("-B", plan["commands"][0]["argv"])
self.assertIn("PYTHONDONTWRITEBYTECODE", plan["command_env_allowlist"]) self.assertIn("PYTHONDONTWRITEBYTECODE", plan["command_env_allowlist"])
def test_full_suite_tracked_background_reconciles_before_retry(self):
text = SKILLS['rework'].read_text()
heading = '### Tracked bounded background evidence execution'
self.assertIn(heading, text)
section = text.split(heading, 1)[1].split('\n### ', 1)[0]
for rule in ['foreground tool 시간창', 'tracked background',
'process/session handle', 'run-id', 'source SHA', 'plan digest',
'output path', 'timeout_seconds', '유한한 outer deadline',
'manifest', 'validate', 'cleanup', 'pending/partial',
'중복 실행하지 않는다', '새 run-id', '별도 승인',
'TDD RED', '세션 종료 이후의 지속성을 보장하지 않는다']:
with self.subTest(rule=rule):
self.assertIn(rule, section)
self.assertIn('자동 retry하지 않는다', section)
self.assertIn('기존 artifact를 덮어쓰지 않는다', section)
def test_command_deadline_and_archive_policy_is_explicit(self): def test_command_deadline_and_archive_policy_is_explicit(self):
import json import json
template = ROOT / "harness/agents/impl/skills/gitea-issue-rework/templates/evidence-plan.json" template = ROOT / "harness/agents/impl/skills/gitea-issue-rework/templates/evidence-plan.json"