Skip to content

feat(runners): 러너 불변식 5종 — 턴 전 자격 판정·크로스 프로세스 잠금·실패 구조화+출처 프로브·원격 모델 카탈로그·구독 차단 분리 - #432

Open
beyondworks wants to merge 2 commits into
mainfrom
fix/runner-invariants

Conversation

@beyondworks

Copy link
Copy Markdown
Owner

Summary

유건 지시(2026-09-05): "연결 후 러너·모델 스위치에 절대 오류가 없도록, Hermes·OpenClaw 구조를 참조해 100% 확실하게."

Hermes(NousResearch/hermes-agent)·OpenClaw(openclaw/openclaw) 코드베이스를 직접 클론해 대조한 결과, 두 프로젝트도 러너 오류를 45일간 각각 826·1,464건 고치고 있었다(커밋 로그). 차이는 "오류가 없다"가 아니라 특정 실패 클래스를 통째로 없애는 불변식이 코드에 박혀 있다는 것. 그 다섯을 Argo에 도입했다.

불변식 원본 근거 Argo 구현
A 인증 실패로 확정된 자격은 실행하지 않는다 OpenClaw cli-backend.ts:120 "must fail loudly … did not start the run" runnerCredEnv 턴 전 게이트(검진 엔트리 reason:'auth' + credHash 대조), markRunnerAuthFail
B 자격 파일 read-modify-write 크로스 프로세스 직렬화 Hermes auth.py:5 "cross-process flock + atomic write" mutex.withDirLock(mkdir 배타·stale 회수·타임아웃 throw) → saveRunnerCred·recoverCodexAuth
C 실패는 코드 표로, 출처는 맨 프로브로 확정 OpenClaw auth-credential-semantics.md status 버킷, Hermes api_error_summary.py error-class.mjs FAIL_CODES 10종 + surfaceRunnerFailure(같은 자격 verify 1회 → vendor/argo/probe)
D 모델 목록은 코드 밖·alias·저장 검증·강등 고지 Hermes model_catalog.py(TTL 20분·stale), OpenClaw claude-model-refs.ts catalog-remote.mjs(릴리스 자산 model-catalog.json) + updateAgentMeta 검증 + modelFallback 고지
E 구독 차단 ≠ 인증 실패 Hermes 대시보드 flow:'external' 주석, 상주 실측 3건 SUBSCRIPTION_BLOCKED_RE → 키 전환 안내, authError 미부착(자가치유 미발동)

유건 판정 기준 "Argo 오류 = 벤더에서도 나야 한다"는 C의 failOrigin(vendor/argo/probe)으로 매 실패에 각인된다 — 이벤트 failCode/failOrigin, 스레드 failedCode/failedOrigin, UI chat.fail.<code>.

실측 근거(상주 실패 37건)

Claude OAuth 세션 만료 9(1위 — AUTH_ERR_RE 어느 갈래에도 안 걸려 자가치유·안내 둘 다 못 받던 문구) · 구독 차단 3 · 한도 4 · Codex refresh-token 재사용 2 · 과부하 4.

검증

  • test/runner-invariants.test.mjs 10건(A: 게이트·지문 대조·재연결 삭제 / B: 자식 프로세스 3개 임계 구간 실측·stale·타임아웃 / C: 분류표 13케이스·프로브 7분기·게이트 연동 / D: 오버레이 검증·add/retire/alias·TTL·stale·디스크 캐시·저장 검증·배선 핀 / E: 정규식·안내·i18n 1:1)
  • 변이 배터리 14종 전부 red(A-1은 1차 GREEN → 테스트가 지문 비교를 직접 핀하도록 보강 후 red)
  • 회귀 갱신: runner-cred(verify가 reason:'auth'를 돌려주는 계약), runner-health(실패 엔트리 credHash), artifacts-diff·runner-p1-p2 배선 핀(modelFallback 추가)
  • 전체 스위트: (아래 실행 결과 기입)

영향 반경(문맥 행렬)

  • 러너(SDK·CLI) 두 catch 경로 모두 surfaceRunnerFailure 경유 — 격리·주입 테스트로 검증. 실벤더 401·윈도우 복사 폴백(B)은 미검증 — 발행 후 윈도우 검증 세션이 덮는다.
  • 산출물: main 머지만으로 도달하는 것은 상주(:3001)뿐. 앱·셀프호스트는 다음 발행(v0.1.62). model-catalog.json은 릴리스 자산으로 CI server 잡이 생성(빈 오버레이 = 코드 목록 그대로).
  • 정직 표기: "100%"는 Argo 코드가 만드는 실패 클래스에 한한다. 벤더 축(한도·정책·과부하)은 E·C가 "즉시 알려준다"까지.

🤖 Generated with Claude Code

beyondworks and others added 2 commits September 5, 2026 13:32
…카탈로그·구독 차단 분리

유건 지시(2026-09-05): "연결 후 러너·모델 스위치에 절대 오류 없이 — Hermes·OpenClaw 구조 참조, 100% 확실하게".
두 코드베이스(NousResearch/hermes-agent, openclaw/openclaw)를 직접 클론·대조 — 그들도 45일간 러너 오류를
826·1,464건 고친다(커밋 로그). 차이는 실패 클래스를 없애는 불변식이 코드에 박혀 있다는 것. 그 다섯을 도입한다.

A 턴 전 자격 판정(OpenClaw cli-backend.ts "must fail loudly … did not start the run"):
  runnerCredEnv가 검진 엔트리 {ok:false, reason:'auth', credHash}와 현재 자격 지문이 일치하면 실행하지 않고
  authExpired로 끊는다. markRunnerAuthFail이 턴 실패 직후 각인(30분 검진 대기 없음). 재연결은 엔트리 삭제.
B 자격 파일 크로스 프로세스 잠금(Hermes auth.py "cross-process flock + atomic write"):
  mutex.withDirLock(mkdir 배타·stale 회수·ELOCKTIMEOUT throw) → saveRunnerCred·recoverCodexAuth.
C 실패 구조화 + 출처 프로브(OpenClaw status 버킷·Hermes api_error_summary):
  error-class.mjs FAIL_CODES 10종. surfaceRunnerFailure가 CLI·SDK 두 catch에서 같은 자격으로 verify 1회를 쏴
  failOrigin vendor/argo/probe를 확정(유건 기준 "Argo 오류 = 벤더에서도 나야 한다") — vendor일 때만 다음 턴 차단.
  이벤트 failCode/failOrigin, 스레드 failedCode/failedOrigin, UI chat.fail.<code>(원문 대신 할 일 먼저).
D 모델 스위치(Hermes model_catalog.py 원격 매니페스트·OpenClaw claude-model-refs alias):
  catalog-remote.mjs — argo-agent 릴리스 자산 model-catalog.json(add/retire/alias, TTL 20분, 디스크 캐시, stale 유지),
  chat.mjs alias 정규화 + 조용한 강등 → modelFallback 고지, updateAgentMeta 저장 검증(model_not_in_catalog),
  /api/runners effectiveModels, CI server 잡이 자산 생성(첫 발행 = 빈 오버레이).
E 구독 차단 ≠ 인증(상주 실측 3건·Hermes 대시보드 정책 주석): SUBSCRIPTION_BLOCKED_RE → 키 전환 안내, authError
  미부착(자가치유 미발동). AUTH_ERR_RE에 'oauth session expired'(상주 실패 1위 9건 — 어느 갈래에도 안 걸리던 문구).

검증: test/runner-invariants.test.mjs 10건(B는 자식 프로세스 3개 실측), 변이 14종 전부 red(A-1은 1차 GREEN →
지문 비교 직접 핀으로 보강), 회귀 기대값 갱신(verify reason:'auth', 검진 credHash, 배선 핀 2곳), 전체 1474건.
미검증(정직 표기): 실벤더 401·윈도우 복사 폴백(B) 실기기 — 발행 후 윈도우 검증 세션이 덮는다.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
… ESM 지정자 오류(CI windows-latest child exit 1)

D:\a\… 경로가 'd:' 스킴으로 해석돼 ERR_UNSUPPORTED_ESM_URL_SCHEME. pathToFileURL로 고치고 자식 stderr를 상속해
다음엔 CI 로그에 원인이 남게 한다. 테스트 전용 변경.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant