feat(runners): 러너 불변식 5종 — 턴 전 자격 판정·크로스 프로세스 잠금·실패 구조화+출처 프로브·원격 모델 카탈로그·구독 차단 분리 - #432
Open
beyondworks wants to merge 2 commits into
Open
feat(runners): 러너 불변식 5종 — 턴 전 자격 판정·크로스 프로세스 잠금·실패 구조화+출처 프로브·원격 모델 카탈로그·구독 차단 분리#432beyondworks wants to merge 2 commits into
beyondworks wants to merge 2 commits into
Conversation
…카탈로그·구독 차단 분리
유건 지시(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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
유건 지시(2026-09-05): "연결 후 러너·모델 스위치에 절대 오류가 없도록, Hermes·OpenClaw 구조를 참조해 100% 확실하게."
Hermes(
NousResearch/hermes-agent)·OpenClaw(openclaw/openclaw) 코드베이스를 직접 클론해 대조한 결과, 두 프로젝트도 러너 오류를 45일간 각각 826·1,464건 고치고 있었다(커밋 로그). 차이는 "오류가 없다"가 아니라 특정 실패 클래스를 통째로 없애는 불변식이 코드에 박혀 있다는 것. 그 다섯을 Argo에 도입했다.cli-backend.ts:120"must fail loudly … did not start the run"runnerCredEnv턴 전 게이트(검진 엔트리reason:'auth'+credHash대조),markRunnerAuthFailauth.py:5"cross-process flock + atomic write"mutex.withDirLock(mkdir 배타·stale 회수·타임아웃 throw) →saveRunnerCred·recoverCodexAuthauth-credential-semantics.mdstatus 버킷, Hermesapi_error_summary.pyerror-class.mjsFAIL_CODES 10종 +surfaceRunnerFailure(같은 자격 verify 1회 →vendor/argo/probe)model_catalog.py(TTL 20분·stale), OpenClawclaude-model-refs.tscatalog-remote.mjs(릴리스 자산model-catalog.json) +updateAgentMeta검증 +modelFallback고지flow:'external'주석, 상주 실측 3건SUBSCRIPTION_BLOCKED_RE→ 키 전환 안내,authError미부착(자가치유 미발동)유건 판정 기준 "Argo 오류 = 벤더에서도 나야 한다"는 C의
failOrigin(vendor/argo/probe)으로 매 실패에 각인된다 — 이벤트failCode/failOrigin, 스레드failedCode/failedOrigin, UIchat.fail.<code>.실측 근거(상주 실패 37건)
Claude OAuth 세션 만료 9(1위 —
AUTH_ERR_RE어느 갈래에도 안 걸려 자가치유·안내 둘 다 못 받던 문구) · 구독 차단 3 · 한도 4 · Codex refresh-token 재사용 2 · 과부하 4.검증
test/runner-invariants.test.mjs10건(A: 게이트·지문 대조·재연결 삭제 / B: 자식 프로세스 3개 임계 구간 실측·stale·타임아웃 / C: 분류표 13케이스·프로브 7분기·게이트 연동 / D: 오버레이 검증·add/retire/alias·TTL·stale·디스크 캐시·저장 검증·배선 핀 / E: 정규식·안내·i18n 1:1)runner-cred(verify가reason:'auth'를 돌려주는 계약),runner-health(실패 엔트리credHash),artifacts-diff·runner-p1-p2배선 핀(modelFallback 추가)영향 반경(문맥 행렬)
surfaceRunnerFailure경유 — 격리·주입 테스트로 검증. 실벤더 401·윈도우 복사 폴백(B)은 미검증 — 발행 후 윈도우 검증 세션이 덮는다.model-catalog.json은 릴리스 자산으로 CIserver잡이 생성(빈 오버레이 = 코드 목록 그대로).🤖 Generated with Claude Code