From 99f73e4f12440bbce42f7ac76482a11555989a46 Mon Sep 17 00:00:00 2001 From: bitkyc08-arch Date: Mon, 27 Jul 2026 19:07:07 +0900 Subject: [PATCH 01/10] docs(devlog): record owner decision ledger for open issues and PRs Inventory all 23 open issues and 14 open PRs on the axis of decisions only the maintainer can make, clustered into six bundles for later PABCD cycles. Live findings: - anthropics/claude-code#1124, cited as upstream evidence on #543, is CLOSED - #462/#401/#241 carry upstream-tracking with no upstream ticket at all - #92's encrypted_content body has no filed upstream issue either - #527 targets #526's head branch, so enforce-target fails - #529 is already merged; the earlier triage matrix still lists it in flight --- .../260727_owner_decision_ledger/000_scope.md | 44 +++++++ .../001_decision_bundles.md | 107 ++++++++++++++++ .../002_upstream_tracking.md | 70 +++++++++++ .../003_enhancement_ledger.md | 105 ++++++++++++++++ .../004_pr_decision_ledger.md | 117 ++++++++++++++++++ .../005_open_questions.md | 69 +++++++++++ 6 files changed, 512 insertions(+) create mode 100644 devlog/_plan/260727_owner_decision_ledger/000_scope.md create mode 100644 devlog/_plan/260727_owner_decision_ledger/001_decision_bundles.md create mode 100644 devlog/_plan/260727_owner_decision_ledger/002_upstream_tracking.md create mode 100644 devlog/_plan/260727_owner_decision_ledger/003_enhancement_ledger.md create mode 100644 devlog/_plan/260727_owner_decision_ledger/004_pr_decision_ledger.md create mode 100644 devlog/_plan/260727_owner_decision_ledger/005_open_questions.md diff --git a/devlog/_plan/260727_owner_decision_ledger/000_scope.md b/devlog/_plan/260727_owner_decision_ledger/000_scope.md new file mode 100644 index 0000000000..157d89a689 --- /dev/null +++ b/devlog/_plan/260727_owner_decision_ledger/000_scope.md @@ -0,0 +1,44 @@ +# 000 — 오너 결정 원장 (Owner Decision Ledger) + +세션: `019fa303-fff0-7c53-b203-33b4e18010cc` +수집 시각: 2026-07-27 (KST 오후) +기준: `origin/dev` = `c05e88fd` + +## 이 문서 묶음이 하는 일 + +기존 트리아지 문서(`260727_bug_triage_loop/001`, `002`)는 **각 항목을 어떻게 +처리할지**를 판정했다. 이 원장은 다른 축을 본다: **오너(jun)가 직접 결정하지 +않으면 누구도 대신 결정할 수 없는 것**만 뽑아 묶는다. + +판정과 결정의 차이: + +- 판정 = "이건 upstream 버그다" → 근거로 확정 가능, 오너 개입 불필요 +- 결정 = "upstream이 안 고치면 우리가 우회할 것인가" → 정책 선택, 오너만 가능 + +## 수집 범위 + +| 대상 | 수 | +| --- | --- | +| 열린 이슈 | 23 | +| 열린 PR | 14 (ready 4, draft 10) | +| enhancement 라벨 이슈 | 11 | +| enhancement 라벨 PR | 7 | +| upstream-tracking 이슈 | 5 | +| 라벨 없는 신규 이슈 | 2 (#545, #546) | + +## 문서 구성 + +| 파일 | 내용 | +| --- | --- | +| `001_decision_bundles.md` | 결정 묶음 A~F — 인터뷰의 실제 단위 | +| `002_upstream_tracking.md` | 업스트림 추적 상태 + 만료 확인 | +| `003_enhancement_ledger.md` | enhancement 전수 판단 (수락/보류/거절 축) | +| `004_pr_decision_ledger.md` | 열린 PR 14건의 오너 결정 지점 | +| `005_open_questions.md` | 인터뷰에서 물을 질문 목록 | + +## 원칙 + +1. 이 라운드에서는 **기록만** 한다. 이슈/PR에 코멘트하거나 라벨을 바꾸지 않는다. +2. 각 항목은 "결정하지 않으면 무슨 일이 일어나는가"를 명시한다. 결정 회피의 + 비용이 보이지 않으면 인터뷰가 의미 없다. +3. 묶음은 **결정 축이 같은 것끼리** 묶는다. 라벨이나 파일 경로가 아니라. diff --git a/devlog/_plan/260727_owner_decision_ledger/001_decision_bundles.md b/devlog/_plan/260727_owner_decision_ledger/001_decision_bundles.md new file mode 100644 index 0000000000..32dba6b89f --- /dev/null +++ b/devlog/_plan/260727_owner_decision_ledger/001_decision_bundles.md @@ -0,0 +1,107 @@ +# 001 — 결정 묶음 + +각 묶음 = 향후 PABCD 한 사이클의 후보 단위. 라벨이 아니라 **결정 축**으로 묶었다. + +## 묶음 지도 + +| 묶음 | 항목 | 결정 축 | 방치 비용 | +| --- | --- | --- | --- | +| **A. 이미지 생성 경로** | PR #424, #355, #528 | 두 백엔드 중 무엇을 기본으로 | 5100줄 PR 3건이 계속 부패. 기여자 2명 대기 | +| **B. 계정 정책 모델** | PR #512, #495, #498 / 이슈 #425, #294 / PR #493 | `main`의 의미 통일 + opt-in 플래그 승인 단위 | 상충하는 계정 개념이 각각 머지될 위험 | +| **C. 업스트림 추적 정직성** | 이슈 #92, #417, #462, #401, #241, #543 | 티켓 없는 추적을 어떻게 할 것인가 | #543이 stale 워크플로에 잘못 걸림 | +| **D. 프로바이더 요청 기준** | 이슈 #177, #178, #201 (기준: #540) | #540 기준의 소급 적용 여부 | 판단 기준이 케이스마다 다름 | +| **E. Desktop 3P 신규 리포트** | 이슈 #545, #546 | 라벨/분류 + #545가 우리 결함인가 | #545는 이미 1,084건 실패 누적 | +| **F. 되살릴 수 있는 소형 PR** | PR #491, #429 (+#526/#527 순서) | 우리가 리베이스할 것인가 | 명확한 버그 수정이 충돌로 사장 | + +## A — 이미지 생성 경로 + +**핵심 질문**: `image_generation`이 non-OpenAI 라우팅에서 죽는 문제를 xAI Grok +유료 호출(#424)로 풀 것인가, 사용자의 기존 Gemini/CCA 계정(#355)으로 풀 것인가, +둘 다 두고 라우팅 규칙을 새로 만들 것인가. + +지금 어느 PR도 "둘 다 있을 때 어디로 가는가"를 정의하지 않는다. 이건 기여자가 +결정할 수 없는 제품 결정이다. + +상세: `004_pr_decision_ledger.md` 결정 축 1. + +## B — 계정 정책 모델 + +**핵심 질문**: `main` 계정이 사용자에게 하나의 의미를 갖는가. + +#512는 네임스페이스 층에서 `__main__`/`@main`/`main`을 구분하고, #495는 라우팅 +층에서 main을 최후 수단으로 예약한다. 각각은 합리적이지만 함께 놓으면 사용자가 +"main"을 세 군데서 다른 뜻으로 만난다. + +여기에 #493(Claude 계정별 한도)과 #294(Claude 계정 풀)가 붙는다. #493이 이미 +"계정별 헤드룸 조회"를 구현했으므로 #294의 핵심 전제가 풀린 상태다 — +`003_enhancement_ledger.md` B 참조. + +**하위 결정**: + +1. 계정 정책을 한 사이클로 묶어 모델을 먼저 정할 것인가 +2. #493을 #294의 첫 조각으로 승격할 것인가 +3. #498(native subagent 기본값)은 계정 축인가 별개 축인가 — 55파일 CONFLICTING + +## C — 업스트림 추적 정직성 + +**핵심 질문**: `upstream-tracking` 라벨이 실제 추적을 뜻하게 만들 것인가. + +실측 세 가지: + +1. #543이 근거로 인용한 anthropics/claude-code#1124는 **CLOSED**(2025-08-10) +2. #462/#401/#241은 업스트림 티켓 링크가 **아예 없다** +3. #92의 본체(Fernet encrypted_content)에 대응하는 업스트림 티켓도 **미제출** — + 인용된 openai/codex#32453은 메인테이너 본인이 "separate issue"라고 분리함 + +유효한 추적은 #417(openai/codex#35161, OPEN, 07-24 갱신) 하나뿐이다. + +상세: `002_upstream_tracking.md`. + +## D — 프로바이더 요청 기준 + +**핵심 질문**: #540에서 세운 기준을 표준으로 승격할 것인가. + +#540 기준: 공식 문서화된 서드파티 게이트웨이 계약 또는 전용 OAuth 등록이 +없으면 구현하지 않는다. 클라이언트 ID 재사용/토큰 파일 무단 임포트 거부. + +#201은 본문이 스스로 커뮤니티 우회책의 위험성(크리덴셜 추출, 사설 헤더 캡처)을 +인정한다. 같은 기준을 적용하면 결론이 달라질 수 있다. + +상세: `003_enhancement_ledger.md` A. + +## E — Desktop 3P 신규 리포트 + +**핵심 질문**: #545가 우리 결함인가. + +#545는 `outputTokens:64`에서 `max_output_tokens` incomplete로 끊긴 요청이 +1,084건 누적됐다. Auto Mode 권한 분류기 요청이 우리 번역 경로를 두 번 지나는 +구조이므로, `max_tokens` 전달/클램프가 우리 쪽 문제일 여지가 있다. +enhancement가 아니라 bug 후보. + +#546은 제보자가 문서 수정이라는 낮은 비용 대안을 이미 제안했다. + +상세: `003_enhancement_ledger.md` D. + +## F — 되살릴 수 있는 소형 PR + +**핵심 질문**: 충돌로 멈춘 작은 버그 수정을 우리가 리베이스할 것인가. + +- #491: +251/3파일. OAuth 로그인이 저장된 API 키를 삭제하는 실제 데이터 손실 +- #429: +48/5파일. Cursor 프롬프트 오염이 대화 영속화로 재생됨 + +둘 다 CONFLICTING이지만 크기가 작다. 여기에 #526에서 #527로 이어지는 머지 순서 +문제가 붙는다 (#527이 `dev`가 아닌 #526 헤드를 base로 삼아 `enforce-target` +FAILURE). + +상세: `004_pr_decision_ledger.md` 결정 축 2, 5. + +## 사이클 순서 제안 (미확정 — 인터뷰 대상) + +비용과 가역성 기준으로 본 초안: + +1. **F** — 작고 되돌리기 쉬움. 워밍업으로 적합 +2. **E** — #545는 실패가 계속 쌓이는 중 +3. **C** — 코드 변경 없이 기록 정직성 회복. 방치 시 자동 stale 위험 +4. **A** — 기여자 2명이 대기 중, PR이 가장 크게 부패 중 +5. **B** — 가장 크고 설계 결정이 선행돼야 함 +6. **D** — 급하지 않으나 기준 일관성 문제 diff --git a/devlog/_plan/260727_owner_decision_ledger/002_upstream_tracking.md b/devlog/_plan/260727_owner_decision_ledger/002_upstream_tracking.md new file mode 100644 index 0000000000..ed013ed44f --- /dev/null +++ b/devlog/_plan/260727_owner_decision_ledger/002_upstream_tracking.md @@ -0,0 +1,70 @@ +# 002 — 업스트림 추적 상태 + +확인 시각: 2026-07-27, `gh issue view` 실측. + +## 추적 중인 업스트림 티켓 + +| ocx 이슈 | 업스트림 | 업스트림 상태 | 최종 갱신 | 판단 | +| --- | --- | --- | --- | --- | +| #92 (V2 cross-provider NEW_TASK 유실) | openai/codex#32453 | **OPEN** | 2026-07-12 | 유효. 단 아래 주석 참조 | +| #417 (한국어 음성 U+FFFD) | openai/codex#35161 | **OPEN** | 2026-07-24 | 유효, 최근 활동 있음 | +| #543 (Claude Code mid-turn queue 무시) | anthropics/claude-code#1124 | **CLOSED** | 2025-08-10 | **인용 무효** — 아래 참조 | +| #462 (모델 제거 시 Codex 크래시) | 없음 | — | — | 업스트림 티켓 미제출 | +| #401 (voice chat 모델 변경) | 없음 | — | — | 업스트림 티켓 미제출 | +| #241 (라우팅 모델 Desktop 피커 미표시) | 없음 | — | — | 업스트림 티켓 미제출 | + +## 발견 1 — #543이 인용한 업스트림 이슈는 닫혀 있다 + +`#543`의 메인테이너 코멘트(2026-07-27T08:26Z)는 anthropics/claude-code#1124를 +"같은 native 증상의 업스트림 리포트"로 인용하며, 이를 근거로 native-vs-proxy +대조 실험을 요구했다. + +실측: **#1124는 2025-08-10에 CLOSED다.** 약 1년 전이고, 리포터가 검증한 +클라이언트(Claude Code 2.1.220)보다 한참 앞선다. + +게다가 리포터는 그 대조 실험을 이미 수행해 답변했다(08:34Z): native Claude +Code 2.1.220에서 mid-turn queue가 **정상 동작**한다고. 즉 우리가 근거로 든 +업스트림 티켓은 닫혔고, 우리가 요구한 대조 실험은 우리 쪽에 불리한 결과로 +돌아왔다. + +**결정 지점**: `needs-info` 라벨을 유지할 것인가. 리포터는 요구한 두 데이터를 +모두 제출했다(native 대조 + 번역 후 상태 grep 결과). 남은 미제출 항목은 +`OCX_QUEUE_543` 마커 실측 하나인데, 리포터가 "지원되는 로깅 스위치를 알려주면 +하겠다"고 되물은 상태다. 이 공을 우리가 쥐고 있다. + +> 참고: `stale-needs-info.yml` 워크플로가 돌고 있으므로, 방치하면 리포터가 +> 답변을 다 했는데도 자동으로 stale 처리될 수 있다. **결정 회피의 비용이 +> 실재한다.** + +## 발견 2 — 업스트림 티켓 없이 upstream-tracking 라벨만 붙은 항목 + +`#462`, `#401`, `#241`은 `upstream-tracking` 라벨을 달고 있지만 대응하는 +업스트림 이슈 링크가 본문/코멘트 어디에도 없다. + +이 라벨의 정의는 "Blocked on an upstream (Codex CLI/Desktop) fix; kept open for +discoverability"다. 업스트림에 티켓이 없으면 그 fix는 영원히 오지 않는다. +추적이 아니라 보류에 가깝다. + +**결정 지점**: 세 건에 대해 (a) 업스트림 이슈를 우리가 제출할 것인가, +(b) 라벨을 `wontfix`/`roadmap`으로 정직하게 바꿀 것인가, (c) 그대로 둘 것인가. + +#92는 반대 사례다 — 우리가 직접 upstream 이슈를 제출했고(코멘트 이력에 +"Draft is ready and will be filed separately"), 주기적 상태 점검 +(2026-07-23 코멘트)까지 남겼다. 이게 upstream-tracking의 모범 형태다. + +## 발견 3 — #92의 업스트림 인용도 정확하지 않다 + +#92가 인용한 openai/codex#32453의 제목은 "Model switch is blocked when +previous-model compaction hits HTTP 429"다. 이건 스레드 중간에 섞여 들어온 +**compaction/429 문제**이고, 메인테이너가 2026-07-18 코멘트에서 직접 +"the compaction/429 reports mixed in here are a separate issue (upstream +#32453-family), not this encryption path"라고 분리해 놓았다. + +즉 #92의 본체(Fernet `encrypted_content`로 NEW_TASK body 유실)에 대응하는 +업스트림 티켓은 **인용된 적이 없다**. 2026-07-18 코멘트가 "Draft is ready and +will be filed separately"라고 했으나, 이후 코멘트(07-22, 07-23)에도 실제 제출 +링크가 없다. + +**결정 지점**: #92의 업스트림 이슈를 실제로 제출할 것인가. 제출하지 않으면 +"upstream fix를 기다린다"는 우리 공개 입장에 대응하는 티켓이 없는 상태가 +계속된다. diff --git a/devlog/_plan/260727_owner_decision_ledger/003_enhancement_ledger.md b/devlog/_plan/260727_owner_decision_ledger/003_enhancement_ledger.md new file mode 100644 index 0000000000..a6d7af6d40 --- /dev/null +++ b/devlog/_plan/260727_owner_decision_ledger/003_enhancement_ledger.md @@ -0,0 +1,105 @@ +# 003 — enhancement 전수 판단 + +대상: `enhancement` 라벨이 붙은 열린 이슈 11건 + 라벨 없는 신규 2건. + +## 판단 축 + +enhancement에 대한 오너 결정은 세 가지 중 하나로 수렴한다: + +- **ACCEPT-CYCLE**: 받아들이고 자체 사이클(P/A/B/C/D)을 배정한다 +- **ACCEPT-GATED**: 받아들이되 외부 조건(업스트림 허가, 계약, 수요)이 풀릴 때까지 착수 안 함 +- **DECLINE**: 명시적으로 안 한다고 밝히고 닫는다 + +가장 나쁜 상태는 셋 중 어느 것도 아닌 채 열려 있는 것이다. 사용자는 기다리고, +우리는 안 하고, 아무도 그 사실을 모른다. + +## 원장 + +| # | 제목 | 라벨 | 현재 상태 | 결정 필요 지점 | +| --- | --- | --- | --- | --- | +| 42 | Storage 페이지 + 세션 정리 정책 | enhancement, roadmap | Phase 1 완료, Phase 2가 PR #529 | Phase 3(자동 정리 정책) 착수 여부 | +| 95 | 멀티유저 프록시 + LiteLLM | enhancement, roadmap | 07-22 "long-term roadmap" 확정 | 프로젝트 사이클 배정 시점. **아래 A 참조** | +| 177 | Warp 프로바이더 | enhancement, roadmap | 07-22 roadmap 일괄 라벨 | 인증 경로 실재 여부 미조사 | +| 178 | Factory 프로바이더 | enhancement, roadmap | 07-22 roadmap 일괄 라벨 | 동일 | +| 201 | TRAE International 프로바이더 | enhancement, roadmap | 07-22 roadmap 일괄 라벨 | 본문이 "공식 인증 경로"를 명시 요구. 조사 필요 | +| 294 | Claude 계정 풀 (ChatGPT 패리티) | enhancement, roadmap | roadmap | PR #493(per-account 한도)이 선행 조각. **아래 B 참조** | +| 386 | macOS 메뉴바 컴패니언 | enhancement | 별도 워크트리 `opencodex-macos-app` 진행 중 | 릴리스 에셋 편입 시점 | +| 401 | voice chat 모델 변경 | enhancement, upstream-tracking | 업스트림 티켓 없음 | 002 참조 | +| 414 | Exa 등 검색 프로바이더 사이드카 | enhancement | 자체 제출 (#398 분할) | 착수 순서 | +| 415 | Gemini 등 자체 검색 API 사이드카 | enhancement | 자체 제출 (#398 분할) | 414와 선후 관계. **아래 C 참조** | +| 425 | Codex 계정 네임스페이스 | enhancement | PR #512가 foundation 담당 | 나머지 레이어 착수 | +| 476 | 카탈로그 변경이 running app-server 미반영 | enhancement | PR #526/#527 승계 | #527이 잘못된 base를 향함 | +| 540 | WordPress Studio Code 프로바이더 | enhancement, provider-compat, roadmap | 07-27 ACCEPT-GATED 확정 | **이미 결정 완료** — 모범 사례 | + +## A — 프로바이더 요청군의 구조적 문제 (#177 / #178 / #201 / #540) + +네 건은 같은 형태다: "X를 프로바이더로 추가해달라". 그런데 #540만 실제로 +판단됐다. 2026-07-27 코멘트에서 공식 문서를 확인하고, OAuth 클라이언트 ID +재사용을 거부하고, "written Automattic confirmation 또는 OpenCodex 전용 OAuth +등록이 선행 조건"이라고 명시했다. + +#177/#178/#201은 2026-07-22에 `roadmap` 라벨이 **일괄로** 붙었을 뿐, +#540 수준의 인증 경로 조사가 없다. 실제로 #201 본문은 커뮤니티 우회책이 +"IDE 크리덴셜 추출, 사설 헤더 캡처, 미문서화 엔드포인트 호출"에 의존한다고 +스스로 밝히고 있다 — #540에서 우리가 거부한 바로 그 패턴이다. + +**결정 지점**: #540에서 세운 기준(공식 인증 경로 없으면 구현 안 함)을 +#177/#178/#201에도 소급 적용할 것인가. 적용하면 세 건 중 일부는 DECLINE 또는 +ACCEPT-GATED로 재분류된다. 적용하지 않으면 기준이 케이스마다 달라진다. + +## B — Claude 계정 풀 (#294) 과 PR #493의 관계 + +#294는 "ChatGPT/Codex 계정 풀과 동등한 Claude 계정 풀"을 요구한다. PR #493은 +그 일부인 **per-account rate limit 조회**를 이미 구현했다 — "Anthropic reports +OAuth usage per credential, so every logged-in Claude account can be probed with +its own bearer token". + +즉 #294의 가장 어려운 전제(계정별 헤드룸을 알 수 있는가)가 PR #493에서 이미 +풀렸다. 그런데 #493은 draft + CONFLICTING + CHANGES_REQUESTED로 멈춰 있고, +#294는 roadmap으로 대기 중이다. 둘이 연결돼 있다는 기록이 어느 쪽에도 없다. + +**결정 지점**: #493을 #294 사이클의 첫 조각으로 승격할 것인가, 아니면 독립 +quota 개선으로 따로 처리할 것인가. + +## C — 사이드카 백엔드 확장 (#414 / #415) + +둘 다 오너 본인이 #398에서 분할 제출했다. 결정 축은 **어느 쪽이 먼저인가**다. + +- #414 (Exa): 전용 검색 벤더. 별도 API 키 필요 → 사용자에게 새 비용 +- #415 (Gemini grounding): 사용자가 **이미 가진** 계정 재사용 → 새 비용 없음 + +#414 본문이 지적한 원래 문제는 "메인 모델이 non-OpenAI/Anthropic일 때 검색이 +두 계정 중 하나를 빌려야 하고, 그 계정이 소진되면 검색이 degrade된다"다. +이 문제에 대해서는 #415가 더 직접적인 해답이다 — Gemini로 라우팅 중인 +사용자는 이미 Gemini 계정이 있다. + +**결정 지점**: #415를 먼저 할 것인가. 기록상 두 이슈는 동등한 형제로만 남아 +있고 우선순위 판단이 없다. + +## D — 라벨 없는 신규 이슈 2건 (#545 / #546) + +2026-07-27 09:23Z 제출, 라벨 미부착, 응답 없음. 둘 다 같은 제보자(PBJ-2), +같은 환경(Desktop 3P + gateway key). + +### #546 — 모델 피커 변경이 세션에 미반영 + +제보자가 스스로 대안을 제시했다: "대응이 어렵다면 README의 Claude Desktop +섹션에 `/model` 커맨드 안내 추가". 즉 **문서 수정만으로 닫을 수 있는 경로**가 +제보자 동의 하에 열려 있다. + +주의: #241(라우팅 모델이 Desktop 피커에 미표시)과 표면이 비슷하지만 다르다. +#241은 **Codex** Desktop, #546은 **Claude** Desktop 3P다. `001_issue_triage_matrix.md`가 +#539에서 같은 혼동을 한 번 겪고 철회한 이력이 있으므로 여기서도 분리 유지. + +### #545 — Auto Mode 권한 분류기가 64토큰에서 잘리고 5회 반복 + +이쪽은 정량 데이터가 강하다: `outputTokens:64`에서 502로 끊긴 요청 **1,084건**, +64 미만 정상 종료 143건, 동일 입력 연속 클러스터 232개. 도구 승인 1건당 +12~22초. + +`max_output_tokens` 초과로 incomplete 처리되는 경로이므로, 우리 쪽 번역 +경로에서 `max_tokens`를 어떻게 전달/클램프하는지에 따라 우리 결함일 수 있다. +**enhancement가 아니라 bug일 가능성이 높다.** + +**결정 지점**: 두 건의 라벨 부착과 우선순위. #545는 1,084건의 실패가 이미 +쌓인 상태이므로 방치 비용이 큰 편이다. diff --git a/devlog/_plan/260727_owner_decision_ledger/004_pr_decision_ledger.md b/devlog/_plan/260727_owner_decision_ledger/004_pr_decision_ledger.md new file mode 100644 index 0000000000..ae9e8006d1 --- /dev/null +++ b/devlog/_plan/260727_owner_decision_ledger/004_pr_decision_ledger.md @@ -0,0 +1,117 @@ +# 004 — 열린 PR 결정 원장 + +측정: 2026-07-27, `gh pr view` 실측. 열린 PR 14건. + +## 전체 상태표 + +| # | 제목 | 라벨 | draft | merge | review | 규모 | 오너 결정 축 | +| --- | --- | --- | --- | --- | --- | --- | --- | +| 533 | update: npm 캐시 실패 시 프록시 보존 | bug | ready | MERGEABLE/UNSTABLE | CHANGES_REQ | +2405/-73, 21f | 재리뷰 | +| 528 | images: #424 P2 후속 | bug | ready | CLEAN | — | +2770/-60, 21f | **#424 의존** | +| 527 | codex: stale app-server 경고 | bug | ready | UNSTABLE | — | +1162/-45, 15f | **base가 dev 아님** | +| 526 | codex: 카탈로그 write 여부 보고 | bug | ready | CLEAN | — | +86/-14, 6f | 머지 가능 | +| 512 | codex: 계정 네임스페이스 foundation | enhancement | ready | UNSTABLE | CHANGES_REQ | +1698/-46, 28f | 설계 승인 | +| 498 | codex: native subagent 기본값 opt-in | enhancement | draft | **CONFLICTING** | CHANGES_REQ | +2427/-286, 55f | 정책 | +| 495 | codex: main 계정 최후 수단 예약 | enhancement | draft | UNSTABLE | CHANGES_REQ | +294/-11, 14f | 정책 | +| 493 | quota: Claude 계정별 한도 | enhancement | draft | **CONFLICTING** | CHANGES_REQ | +291/-20, 14f | **#294 승격 여부** | +| 491 | oauth: 로그인이 저장된 API 키 삭제 | bug | draft | **CONFLICTING** | CHANGES_REQ | +251/-4, 3f | 소규모, 충돌만 | +| 461 | cli: `ocx opencode` 런처 | enhancement | draft | **CONFLICTING** | — | +868/-0, 6f | **표면 확장 여부** | +| 447 | kiro: 브라우저 멀티계정 로그인 | bug | draft | CLEAN | CHANGES_REQ | +1625/-137, 20f | 재리뷰 | +| 429 | cursor: shell-alias 힌트 주입 제거 | bug | draft | **CONFLICTING** | — | +48/-37, 5f | 소규모, 충돌만 | +| 424 | images: Grok 이미지 브리지 | enhancement | draft | UNSTABLE | CHANGES_REQ | +2333/-59, 22f | **유료 호출 정책** | +| 355 | google: Gemini 인라인 이미지 | enhancement | draft | UNSTABLE | CHANGES_REQ | +1435/-15, 16f | **424와 중복 축** | + +합계: ready 4, draft 10. CONFLICTING 5건. CHANGES_REQUESTED 9건. + +## 결정 축 1 — 이미지 생성 경로가 두 개다 (#424 / #355 / #528) + +이게 가장 큰 미결정이다. 두 PR이 **같은 사용자 문제**를 서로 다른 방식으로 푼다: + +| | #424 (Grok 브리지) | #355 (Gemini 인라인) | +| --- | --- | --- | +| 문제 | non-OpenAI 라우팅 시 `image_generation` 호스티드 툴이 죽음 | 동일 | +| 방식 | 합성 함수 툴로 치환 → xAI Grok Imagine 호출 → 아티팩트 주입 → 모델 재호출 | CCA(Antigravity) 이미지 모델로 `/v1/images/generations` 폴백 + `inlineData` 파싱 | +| 비용 | **xAI 유료 호출** 발생 | 사용자의 기존 Gemini/CCA 계정 | +| 규모 | +2333 (+#528의 +2770) | +1435 | +| 아키텍처 | `src/web-search/` 사이드카 패턴 복제 | 기존 google 어댑터 확장 | + +#528은 #424의 P2 후속이고 본문이 "**Depends on #424** — 먼저 #424를 머지하라"고 +명시한다. 즉 #424를 받으면 #528도 따라온다: 합계 +5100줄. + +**결정 지점**: 둘 다 받을 것인가, 하나만 받을 것인가, 어느 쪽을 기본 경로로 +삼을 것인가. 둘 다 받으면 "이미지 생성 시 어느 백엔드로 가는가"라는 새 라우팅 +규칙이 필요해진다 — 지금 어느 PR도 그걸 정의하지 않는다. + +#528이 클램프하려는 것("hand-edited `10000` 값이 xAI 유료 호출을 무한정 태우지 +못하게")이 이 축의 성격을 잘 보여준다. #424 경로는 사용자 돈이 나가는 경로다. + +## 결정 축 2 — #527이 잘못된 base를 향한다 + +실측: `#527 base=codex/catalog-written-signal head=codex/app-server-restart`. + +제목에 이미 `[WRONG BRANCH]`가 붙어 있고, `enforce-target` 체크가 +**FAILURE**다. base가 `#526`의 헤드 브랜치이므로 스택 PR 형태인데, 우리 +`enforce-target` 워크플로는 `dev`/`dev2-go`만 허용한다. + +두 PR은 #476(카탈로그 변경 미반영)을 승계한 쌍이다. #526(신호 보고, +86줄, +CLEAN)이 먼저 머지되면 #527의 base를 `dev`로 리타깃할 수 있다. + +**결정 지점**: 스택 PR을 허용할 것인가(=워크플로 예외), 아니면 #526 머지 후 +리타깃할 것인가. 후자가 기존 정책과 일관되지만 순서 강제가 필요하다. + +## 결정 축 3 — 계정 정책 3종 (#512 / #495 / #498) + +세 PR 모두 "계정/기본값을 누가 소유하는가"를 건드린다. 각각 독립적으로 보이지만 +한 사용자에게는 겹쳐 보인다. + +- **#512** (#425의 foundation): 모델 셀렉터 네임스페이스 → 저장된 Codex 계정 ID. + `__main__`을 내부 Desktop 계정용으로 예약하고 `@main`을 맵에서만 쓴다. +- **#495**: `mainAccountLastResort` 정책(기본 off). main 로그인을 최후 수단으로 + 예약하고, 쿼터 리밸런싱이 건강한 added 계정을 main으로 옮기지 못하게 막는다. +- **#498**: `syncCodexSubagentDefaults`(기본 off). Codex native `[agents]` + 기본값에 injectionModel/effort를 마커 범위 TOML 편집으로 주입. + +#512와 #495는 **같은 `main` 계정 개념**을 다르게 다룬다. #512는 이름공간에서 +`main`을 실제 풀 계정 ID로 남겨두려 하고, #495는 `main`을 라우팅에서 특별 +취급한다. 둘 다 머지되면 "main"이 사용자에게 두 가지 의미를 갖는다. + +**결정 지점**: 세 opt-in 플래그를 각각 독립 승인할 것인가, 아니면 계정 정책 +하나의 사이클로 묶어 일관된 모델을 먼저 정할 것인가. #498은 55파일 +CONFLICTING이라 어차피 재작업이 필요하다. + +## 결정 축 4 — 새 클라이언트 표면 (#461) + +`ocx opencode`는 `ocx claude`, native Codex injection에 이은 **세 번째 클라이언트 +표면**이다. +868/-0, 순수 추가. + +표면이 늘면 유지 비용이 영구적으로 늘어난다. 지금도 Codex CLI/App/SDK, Claude +Code, Claude Desktop 3P, Cursor, Kiro를 지탱하고 있고, 이번 라운드 이슈 중 +상당수(#545, #546, #543)가 Desktop 3P 표면에서 나왔다. + +**결정 지점**: 표면을 하나 더 받을 것인가. 받는다면 지원 등급(1급/실험적)을 +무엇으로 선언할 것인가. 현재 PR은 draft/CONFLICTING이고 리뷰가 없다. + +## 결정 축 5 — CHANGES_REQUESTED 9건이 멈춰 있다 + +9건이 변경 요청 상태다. 이 중 CONFLICTING까지 겹친 것이 #498, #493, #491. + +`#491`과 `#429`는 작다(+251/3파일, +48/5파일). 둘 다 명확한 버그 수정이고 +본문 진단이 구체적이다: + +- #491: `upsertOAuthProvider`가 매 OAuth 로그인마다 provider 항목을 프리셋으로 + 덮어써서 `apiKey`/`apiKeyPool`이 삭제됨. `allowKeyAuthOverride` 프로바이더 + (`xai`, `github-copilot`)에서 과금 선택이 조용히 뒤집힘. +- #429: Cursor 어댑터가 사용자 메시지에 `"Use exec_command for this shell + command."`를 덧붙이고, Cursor 대화 영속화가 오염된 프롬프트를 재생. + +둘 다 리베이스만 하면 진행 가능한 크기다. + +**결정 지점**: 작고 명확한 수정(#491, #429)을 우리가 리베이스해서 살릴 것인가, +기여자 응답을 기다릴 것인가. 메모리 기준 "우리가 수정할 수 있으면 rework나 +얹어서 수정하는 방향"에 해당하는 후보군이다. + +## 참고 — #529는 이미 머지됨 + +`001_issue_triage_matrix.md`가 #42를 "PR #529가 phase 2 담당, IN-FLIGHT"로 +기록했으나 실측상 **#529는 MERGED**다. #42의 Phase 2는 끝났고 남은 것은 +Phase 2.1(복원 UI)과 Phase 3(자동 정책)이다. diff --git a/devlog/_plan/260727_owner_decision_ledger/005_open_questions.md b/devlog/_plan/260727_owner_decision_ledger/005_open_questions.md new file mode 100644 index 0000000000..1c447dd434 --- /dev/null +++ b/devlog/_plan/260727_owner_decision_ledger/005_open_questions.md @@ -0,0 +1,69 @@ +# 005 — 인터뷰 질문 목록 + +`request_user_input`으로 물을 것과, 근거로 대신 판정 가능한 것을 분리한다. + +## 오너만 답할 수 있는 것 (실제 질문) + +### Q1 — 이미지 생성 백엔드 (묶음 A) + +#424(xAI Grok, 유료 호출)와 #355(Gemini/CCA, 기존 계정) 중 무엇이 기본 경로인가. + +선택지: + +- (a) #355 먼저 — 사용자 추가 비용 없음, 규모 작음(+1435). #424는 보류 +- (b) #424+#528 먼저 — 이미 후속 PR까지 준비됨. 대신 +5100줄, 유료 호출 노출 +- (c) 둘 다 수용 + 백엔드 선택 라우팅 규칙 신설 — 가장 크지만 완전 + +### Q2 — 계정 정책 묶음 처리 (묶음 B) + +#512/#495/#498을 개별 승인할 것인가, 계정 모델을 먼저 정하는 한 사이클로 묶을 +것인가. 그리고 #493을 #294의 첫 조각으로 승격할 것인가. + +### Q3 — 프로바이더 기준 소급 (묶음 D) + +#540 기준(공식 인증 경로 없으면 미구현)을 #177/#178/#201에 소급할 것인가. +소급하면 일부는 DECLINE으로 닫힌다. + +### Q4 — 표면 확장 (#461) + +`ocx opencode`를 네 번째 클라이언트 표면으로 받을 것인가. 받는다면 지원 등급은. + +### Q5 — 이번 라운드의 실행 범위 + +기록만 하고 끝낼 것인가, 아니면 이어지는 PABCD 사이클에서 어디까지 실제로 +손댈 것인가. 특히 GitHub 코멘트/라벨 변경 권한을 이번 라운드에 줄 것인가. + +## 근거로 판정 가능한 것 (질문 불필요) + +| 항목 | 판정 | 근거 | +| --- | --- | --- | +| #543의 업스트림 인용 무효 | 확정 | anthropics/claude-code#1124 = CLOSED 2025-08-10 | +| #529 머지 완료 | 확정 | `gh pr view 529` state=MERGED | +| #527 base 위반 | 확정 | base=`codex/catalog-written-signal`, enforce-target=FAILURE | +| #528의 #424 의존 | 확정 | PR 본문이 명시 | +| #462/#401/#241 업스트림 티켓 부재 | 확정 | 본문+코멘트 전문 grep 결과 0건 | +| #545/#546 라벨 없음 | 확정 | `gh issue list` 라벨 필드 공란 | + +## 인터뷰 차원 점수 (I 단계 시작 시점) + +| 차원 | 점수 | 근거 | +| --- | --- | --- | +| Goal | 3 | "결정할 것을 전수 기록하고 묶는다"는 명확. 이후 실행 범위는 미정 | +| Constraint | 2 | 기록만 하는지, GitHub 상태를 바꿔도 되는지 미확정 | +| Success criteria | 2 | 묶음 6개가 산출물인지, 각 묶음의 결정까지가 산출물인지 미확정 | +| Ontology | 4 | 이슈/PR/업스트림/묶음 용어가 기존 devlog와 일관 | + +## 루프 아키타입 + +**spec-satisfaction.** 검증자가 done을 정의한다: "열린 이슈 23 + 열린 PR 14 +전부가 최소 한 묶음에 배정되었고, 각 항목마다 결정 지점이 명시되었는가"는 +기계적으로 확인 가능하다. open-ended 최적화가 아니다. + +커버리지 확인: + +- 이슈 23건 → A(0) B(2: #425,#294) C(6) D(3) E(2) F(0) + 이미 결정됨(#540) + + IN-FLIGHT PR 담당(#476, #42, #418) + roadmap 대기(#95, #386, #414, #415, #509, #521) +- PR 14건 → A(3) B(4) F(4: #491,#429,#526,#527) + 개별(#533, #447, #461) + +> 미배정 잔여: 이슈 #509/#521(needs-info 자동 처리), #95/#386/#414/#415(roadmap), +> PR #533/#447(단순 재리뷰). 이들은 결정 축이 아니라 대기 상태이므로 묶지 않았다. From 01aa08ff9e2e92a67442490179fede62d2854dca Mon Sep 17 00:00:00 2001 From: bitkyc08-arch Date: Mon, 27 Jul 2026 19:22:17 +0900 Subject: [PATCH 02/10] docs(devlog): correct the decision ledger against an independent audit Two read-only audit lenses returned 30 contradictions; the verified ones are applied here and recorded in 006_corrections.md. Factual corrections: - claude-code#1124 closed 2025-05-16, not 2025-08-10 - 13 open enhancement issues, not 11 - #529 is merged, so issue #42 phase 2 is done - #418 has no PR behind it; it was miscounted as in-flight - #528 does not contain #424's current head, so it needs a rebase - #527's retarget is manual and independent of #526 Framing corrections: - stale-needs-info.yml is absent from the default branch, so bundle C had no real deadline - the debug switch #543's reporter asked for already exists - whether #545 is our defect is answerable from code, not owner judgment - #491 is an OAuth credential change, so it is a security boundary rather than a warm-up; #533 and #447 keep their security classification too Structure: bundle G added for roadmap honesty, bundle F rescoped to the security boundary, #498 split out of bundle B, cycle order rebuilt, and the loop archetype corrected to decision-elicitation. --- .../260727_owner_decision_ledger/000_scope.md | 12 +- .../001_decision_bundles.md | 115 ++++++++++++---- .../002_upstream_tracking.md | 32 +++-- .../003_enhancement_ledger.md | 19 ++- .../004_pr_decision_ledger.md | 57 ++++++-- .../005_open_questions.md | 73 ++++++---- .../006_corrections.md | 126 ++++++++++++++++++ 7 files changed, 357 insertions(+), 77 deletions(-) create mode 100644 devlog/_plan/260727_owner_decision_ledger/006_corrections.md diff --git a/devlog/_plan/260727_owner_decision_ledger/000_scope.md b/devlog/_plan/260727_owner_decision_ledger/000_scope.md index 157d89a689..f9a833f52e 100644 --- a/devlog/_plan/260727_owner_decision_ledger/000_scope.md +++ b/devlog/_plan/260727_owner_decision_ledger/000_scope.md @@ -21,9 +21,11 @@ | --- | --- | | 열린 이슈 | 23 | | 열린 PR | 14 (ready 4, draft 10) | -| enhancement 라벨 이슈 | 11 | +| enhancement 라벨 이슈 | 13 (`[540,476,425,415,414,401,386,294,201,178,177,95,42]`) | | enhancement 라벨 PR | 7 | +| roadmap 라벨 이슈 | 7 (`[540,294,201,178,177,95,42]`) | | upstream-tracking 이슈 | 5 | +| needs-info 이슈 | 4 (#462, #509, #521, #543) | | 라벨 없는 신규 이슈 | 2 (#545, #546) | ## 문서 구성 @@ -35,10 +37,18 @@ | `003_enhancement_ledger.md` | enhancement 전수 판단 (수락/보류/거절 축) | | `004_pr_decision_ledger.md` | 열린 PR 14건의 오너 결정 지점 | | `005_open_questions.md` | 인터뷰에서 물을 질문 목록 | +| `006_corrections.md` | 초안 대비 정정 기록 (Mind 감사 결과) | ## 원칙 1. 이 라운드에서는 **기록만** 한다. 이슈/PR에 코멘트하거나 라벨을 바꾸지 않는다. + (이 원칙은 이번 라운드 한정이며, 다음 사이클의 실행 권한은 미확정 — + `005_open_questions.md` Q5.) 2. 각 항목은 "결정하지 않으면 무슨 일이 일어나는가"를 명시한다. 결정 회피의 비용이 보이지 않으면 인터뷰가 의미 없다. 3. 묶음은 **결정 축이 같은 것끼리** 묶는다. 라벨이나 파일 경로가 아니라. + +## 검증 + +초안 작성 후 독립 감사(2 Mind, read-only)를 돌려 30건의 모순 지적을 받았고 +그중 실측으로 확인된 것을 반영했다. 정정 내역은 `006_corrections.md`. diff --git a/devlog/_plan/260727_owner_decision_ledger/001_decision_bundles.md b/devlog/_plan/260727_owner_decision_ledger/001_decision_bundles.md index 32dba6b89f..c06f3513c0 100644 --- a/devlog/_plan/260727_owner_decision_ledger/001_decision_bundles.md +++ b/devlog/_plan/260727_owner_decision_ledger/001_decision_bundles.md @@ -2,16 +2,32 @@ 각 묶음 = 향후 PABCD 한 사이클의 후보 단위. 라벨이 아니라 **결정 축**으로 묶었다. +> 이 문서는 독립 감사 후 개정됐다. 초안 대비 변경: 묶음 G 신설, 묶음 F 축소, +> #498을 B에서 분리, 사이클 순서 재작성. 근거는 `006_corrections.md`. + ## 묶음 지도 | 묶음 | 항목 | 결정 축 | 방치 비용 | | --- | --- | --- | --- | | **A. 이미지 생성 경로** | PR #424, #355, #528 | 두 백엔드 중 무엇을 기본으로 | 5100줄 PR 3건이 계속 부패. 기여자 2명 대기 | -| **B. 계정 정책 모델** | PR #512, #495, #498 / 이슈 #425, #294 / PR #493 | `main`의 의미 통일 + opt-in 플래그 승인 단위 | 상충하는 계정 개념이 각각 머지될 위험 | -| **C. 업스트림 추적 정직성** | 이슈 #92, #417, #462, #401, #241, #543 | 티켓 없는 추적을 어떻게 할 것인가 | #543이 stale 워크플로에 잘못 걸림 | +| **B. 계정 정책 모델** | PR #512, #495, #493 / 이슈 #425, #294 | `main`의 의미 통일 + Claude 풀 승격 | 상충하는 계정 개념이 각각 머지될 위험 | +| **C. 업스트림 추적 정직성** | 이슈 #92, #417, #462, #401, #241, #418 | 티켓 없는 추적을 어떻게 할 것인가 | 공개 입장과 실제 추적이 불일치 | | **D. 프로바이더 요청 기준** | 이슈 #177, #178, #201 (기준: #540) | #540 기준의 소급 적용 여부 | 판단 기준이 케이스마다 다름 | -| **E. Desktop 3P 신규 리포트** | 이슈 #545, #546 | 라벨/분류 + #545가 우리 결함인가 | #545는 이미 1,084건 실패 누적 | -| **F. 되살릴 수 있는 소형 PR** | PR #491, #429 (+#526/#527 순서) | 우리가 리베이스할 것인가 | 명확한 버그 수정이 충돌로 사장 | +| **E. Desktop 3P 신규 리포트** | 이슈 #545, #546, #543 | 우선순위 + 조사 착수 | #545는 이미 1,084건 실패 누적 | +| **F. 보안 경계 PR** | PR #491, #533, #447 | 보안 리뷰를 언제 누가 | 크리덴셜/인증 수정이 무기한 정체 | +| **G. 로드맵 정직성** | 이슈 #95, #386, #414, #415, #42 | roadmap을 실제 상태로 확정 | 사용자는 기다리고 우리는 안 함 | + +묶음에 들지 않는 잔여: + +| 항목 | 처리 | +| --- | --- | +| PR #429 | 보안 경계 밖 소형 수정. 우리가 리베이스 가능한 **유일한** 후보 | +| PR #526 | ready/CLEAN. 그냥 머지 대상 | +| PR #527 | base 수동 리타깃 후 머지. 순서 의존 없음 | +| PR #461 | 표면 확장 단독 결정 (`004` 결정 축 4) | +| PR #498 | 계정 축 아님. 55파일 CONFLICTING이라 재작업 선행 | +| 이슈 #509, #521 | `needs-info`. 리포터 응답 대기 | +| 이슈 #476 | PR #526/#527이 담당 | ## A — 이미지 생성 경로 @@ -22,6 +38,10 @@ 지금 어느 PR도 "둘 다 있을 때 어디로 가는가"를 정의하지 않는다. 이건 기여자가 결정할 수 없는 제품 결정이다. +주의: #528이 #424에 의존한다고 본문에 적혀 있으나 **자동으로 딸려오지 않는다**. +실측상 #528(`553e9afc`)은 #424의 현재 head(`a8b769c9`)를 포함하지 않으므로 +#424를 먼저 머지하면 #528 리베이스가 필요하다. + 상세: `004_pr_decision_ledger.md` 결정 축 1. ## B — 계정 정책 모델 @@ -40,7 +60,9 @@ 1. 계정 정책을 한 사이클로 묶어 모델을 먼저 정할 것인가 2. #493을 #294의 첫 조각으로 승격할 것인가 -3. #498(native subagent 기본값)은 계정 축인가 별개 축인가 — 55파일 CONFLICTING + +> #498은 초안에서 이 묶음에 넣었으나 축이 다르다(Codex native `[agents]` TOML +> 편집, `main` 계정 의미와 무관). 분리했다. ## C — 업스트림 추적 정직성 @@ -48,13 +70,17 @@ 실측 세 가지: -1. #543이 근거로 인용한 anthropics/claude-code#1124는 **CLOSED**(2025-08-10) -2. #462/#401/#241은 업스트림 티켓 링크가 **아예 없다** -3. #92의 본체(Fernet encrypted_content)에 대응하는 업스트림 티켓도 **미제출** — +1. #462/#401/#241은 업스트림 티켓 링크가 **아예 없다** +2. #92의 본체(Fernet encrypted_content)에 대응하는 업스트림 티켓도 **미제출** — 인용된 openai/codex#32453은 메인테이너 본인이 "separate issue"라고 분리함 +3. #418(V2 custom-parent→child 위임 실패)은 #92와 같은 V2 계열인데 담당 PR도 + 업스트림 티켓도 없다. 초안이 이를 "IN-FLIGHT PR 담당"으로 잘못 적었다 유효한 추적은 #417(openai/codex#35161, OPEN, 07-24 갱신) 하나뿐이다. +> #543은 이 묶음에서 뺐다. 라벨이 `bug/provider-compatibility/needs-info`이고 +> `upstream-tracking`이 아니다. 묶음 E로 옮긴다. + 상세: `002_upstream_tracking.md`. ## D — 프로바이더 요청 기준 @@ -71,37 +97,74 @@ ## E — Desktop 3P 신규 리포트 -**핵심 질문**: #545가 우리 결함인가. +**핵심 질문**: 이 표면의 리포트 3건을 언제 조사할 것인가. #545는 `outputTokens:64`에서 `max_output_tokens` incomplete로 끊긴 요청이 -1,084건 누적됐다. Auto Mode 권한 분류기 요청이 우리 번역 경로를 두 번 지나는 -구조이므로, `max_tokens` 전달/클램프가 우리 쪽 문제일 여지가 있다. -enhancement가 아니라 bug 후보. +1,084건 누적됐다. 코드 확인 결과 최소한 **로그 표기는 우리 코드가 만든다**: +`src/server/request-log.ts:610`이 `completed`가 아닌 모든 terminal 상태를 502로 +기록한다. `max_tokens`는 `src/claude/inbound.ts:436`에서 클램프 없이 전달되고, +`src/server/claude-messages.ts:596`은 native passthrough 경로에서만 제거한다. +"우리 결함인가"는 오너가 아니라 조사가 답할 문제다 — `006_corrections.md` F4. #546은 제보자가 문서 수정이라는 낮은 비용 대안을 이미 제안했다. +#543은 리포터가 요구받은 대조 실험을 이미 제출했고, 남은 마커 캡처에 필요한 +스위치(`ocx debug claude`)도 이미 존재한다. 답변만 하면 진행된다. + 상세: `003_enhancement_ledger.md` D. -## F — 되살릴 수 있는 소형 PR +## F — 보안 경계 PR + +**핵심 질문**: 보안 경계에 걸린 PR 3건의 리뷰를 언제, 누가 할 것인가. -**핵심 질문**: 충돌로 멈춘 작은 버그 수정을 우리가 리베이스할 것인가. +- **#491**: OAuth 로그인이 저장된 API 키를 삭제하는 실제 데이터 손실. + `src/oauth/index.ts` 변경 → AGENTS.md 최우선 보안 경계 +- **#533**: npm 캐시 소유권 검사. 의존성 설치 경계 +- **#447**: Kiro 브라우저 멀티계정 로그인. 인증/크리덴셜 경계 -- #491: +251/3파일. OAuth 로그인이 저장된 API 키를 삭제하는 실제 데이터 손실 -- #429: +48/5파일. Cursor 프롬프트 오염이 대화 영속화로 재생됨 +초안은 #491을 "작으니까 워밍업"으로 분류했다. **철회한다** — 크기가 아니라 +경계가 기준이고, MAINTAINERS.md는 보안 민감 변경에 두 메인테이너 리뷰를 +권한다. #533/#447도 초안이 "단순 재리뷰"로 낮춰 적었던 것을 복원했다. -둘 다 CONFLICTING이지만 크기가 작다. 여기에 #526에서 #527로 이어지는 머지 순서 -문제가 붙는다 (#527이 `dev`가 아닌 #526 헤드를 base로 삼아 `enforce-target` -FAILURE). +상세: `004_pr_decision_ledger.md` 결정 축 5, `006_corrections.md` S2/S3. -상세: `004_pr_decision_ledger.md` 결정 축 2, 5. +## G — 로드맵 정직성 + +**핵심 질문**: `roadmap` 라벨이 약속인가 보류인가. + +`003_enhancement_ledger.md`는 "ACCEPT-CYCLE / ACCEPT-GATED / DECLINE 중 어느 +것도 아닌 채 열려 있는 것이 가장 나쁜 상태"라고 규정한다. 그런데 `roadmap`은 +정확히 그 상태다. 해당 항목: + +| # | 제목 | 마지막 실질 갱신 | +| --- | --- | --- | +| 95 | 멀티유저 프록시 + LiteLLM | 2026-07-22 "long-term roadmap" | +| 386 | macOS 메뉴바 컴패니언 | 별도 워크트리 진행 중 | +| 414 | Exa 검색 사이드카 | 자체 제출, 착수 없음 | +| 415 | Gemini 검색 사이드카 | 자체 제출, 착수 없음 | +| 42 | Storage — Phase 2.1 / Phase 3 | Phase 2(#529) 머지 완료 | + +#95는 특히 크다. 단일 사용자 로컬 프록시에서 공유 서비스로 **제품 배포 모델 +자체를 바꾸는** 요구인데 "long-term roadmap"이라는 상태 표시만 있고 수락·거절 +판단이 없다. + +**결정 지점**: 각 항목에 실제 상태(사이클 배정 / 조건부 수락 / 거절)를 부여할 +것인가, `roadmap`을 "언젠가"의 완곡어로 계속 쓸 것인가. ## 사이클 순서 제안 (미확정 — 인터뷰 대상) -비용과 가역성 기준으로 본 초안: +감사 결과를 반영해 재작성했다. 기준: 방치 비용(실측) × 가역성. -1. **F** — 작고 되돌리기 쉬움. 워밍업으로 적합 -2. **E** — #545는 실패가 계속 쌓이는 중 -3. **C** — 코드 변경 없이 기록 정직성 회복. 방치 시 자동 stale 위험 -4. **A** — 기여자 2명이 대기 중, PR이 가장 크게 부패 중 +1. **E** — #545는 실패가 실시간으로 쌓이는 유일한 항목(1,084건). #543/#546은 + 답변 비용이 매우 낮다. 여기에 묶음 밖 소형 항목(#429 리베이스, #526 머지, + #527 리타깃)을 함께 태우면 워밍업이 된다 +2. **A** — 기여자 2명 대기, PR 부패가 가장 크고, 제품 결정 하나로 3건이 풀린다 +3. **C** — 코드 변경 없이 공개 기록의 정직성 회복. 시한 압박은 없음(정정됨) +4. **G** — C와 같은 성격(기록 정직성)이고 함께 처리하면 효율적 5. **B** — 가장 크고 설계 결정이 선행돼야 함 -6. **D** — 급하지 않으나 기준 일관성 문제 +6. **D** — G/C에서 세운 기준을 프로바이더 요청에 적용하는 후속 +7. **F** — 보안 경계. 별도 리뷰 절차이므로 순서보다 **누가 리뷰하는가**가 관건 + +초안 순서에서 바뀐 이유: (a) F는 워밍업이 아니라 최고 위험 묶음이었고, +(b) C의 긴급성 근거였던 stale 워크플로가 실제로 돌지 않으며, +(c) A의 방치 비용이 원장 자체 기록상 가장 크다. diff --git a/devlog/_plan/260727_owner_decision_ledger/002_upstream_tracking.md b/devlog/_plan/260727_owner_decision_ledger/002_upstream_tracking.md index ed013ed44f..391c8f26f8 100644 --- a/devlog/_plan/260727_owner_decision_ledger/002_upstream_tracking.md +++ b/devlog/_plan/260727_owner_decision_ledger/002_upstream_tracking.md @@ -6,35 +6,40 @@ | ocx 이슈 | 업스트림 | 업스트림 상태 | 최종 갱신 | 판단 | | --- | --- | --- | --- | --- | -| #92 (V2 cross-provider NEW_TASK 유실) | openai/codex#32453 | **OPEN** | 2026-07-12 | 유효. 단 아래 주석 참조 | +| #92 (V2 cross-provider NEW_TASK 유실) | openai/codex#32453 | OPEN | 2026-07-12 | **인용 불일치** — 발견 3 참조 | | #417 (한국어 음성 U+FFFD) | openai/codex#35161 | **OPEN** | 2026-07-24 | 유효, 최근 활동 있음 | -| #543 (Claude Code mid-turn queue 무시) | anthropics/claude-code#1124 | **CLOSED** | 2025-08-10 | **인용 무효** — 아래 참조 | +| #543 (Claude Code mid-turn queue 무시) | anthropics/claude-code#1124 | **CLOSED** | closed 2025-05-16 | **인용 무효** — 아래 참조 | | #462 (모델 제거 시 Codex 크래시) | 없음 | — | — | 업스트림 티켓 미제출 | | #401 (voice chat 모델 변경) | 없음 | — | — | 업스트림 티켓 미제출 | | #241 (라우팅 모델 Desktop 피커 미표시) | 없음 | — | — | 업스트림 티켓 미제출 | +실질적으로 유효한 추적은 **#417 하나뿐**이다. + ## 발견 1 — #543이 인용한 업스트림 이슈는 닫혀 있다 `#543`의 메인테이너 코멘트(2026-07-27T08:26Z)는 anthropics/claude-code#1124를 "같은 native 증상의 업스트림 리포트"로 인용하며, 이를 근거로 native-vs-proxy 대조 실험을 요구했다. -실측: **#1124는 2025-08-10에 CLOSED다.** 약 1년 전이고, 리포터가 검증한 -클라이언트(Claude Code 2.1.220)보다 한참 앞선다. +실측(`gh issue view 1124 --repo anthropics/claude-code`): +**state=CLOSED, closedAt=2025-05-16T14:10:06Z.** 1년 이상 전이고, 리포터가 +검증한 클라이언트(Claude Code 2.1.220)보다 한참 앞선다. 게다가 리포터는 그 대조 실험을 이미 수행해 답변했다(08:34Z): native Claude Code 2.1.220에서 mid-turn queue가 **정상 동작**한다고. 즉 우리가 근거로 든 업스트림 티켓은 닫혔고, 우리가 요구한 대조 실험은 우리 쪽에 불리한 결과로 돌아왔다. -**결정 지점**: `needs-info` 라벨을 유지할 것인가. 리포터는 요구한 두 데이터를 -모두 제출했다(native 대조 + 번역 후 상태 grep 결과). 남은 미제출 항목은 -`OCX_QUEUE_543` 마커 실측 하나인데, 리포터가 "지원되는 로깅 스위치를 알려주면 -하겠다"고 되물은 상태다. 이 공을 우리가 쥐고 있다. +**오너 결정 아님 — 지원 업무.** 리포터가 "지원되는 로깅 스위치를 알려주면 +`OCX_QUEUE_543` 캡처를 하겠다"고 되물었는데, 그 스위치는 이미 있다: +`src/lib/debug-settings.ts:7`의 `ocx debug claude on|off|status|reset` 및 +`OCX_CLAUDE_DEBUG=1`. 한국어·중국어 문서에도 기재돼 있다. 답변만 하면 된다. -> 참고: `stale-needs-info.yml` 워크플로가 돌고 있으므로, 방치하면 리포터가 -> 답변을 다 했는데도 자동으로 stale 처리될 수 있다. **결정 회피의 비용이 -> 실재한다.** +> **stale 위험 정정.** 초안은 `stale-needs-info.yml`이 돌고 있다고 가정했으나, +> 실측상 이 워크플로는 `origin/dev`에만 있고 `origin/main`(기본 브랜치)에 +> **없다**. 예약 워크플로는 기본 브랜치에서만 실행되므로 **현재 동작하지 +> 않는다.** 설사 돌더라도 `days-before-issue-stale: 14` + +> `remove-stale-when-updated: true`이고 #543은 07-27에 갱신됐다. 시한 압박 없음. ## 발견 2 — 업스트림 티켓 없이 upstream-tracking 라벨만 붙은 항목 @@ -48,9 +53,8 @@ discoverability"다. 업스트림에 티켓이 없으면 그 fix는 영원히 **결정 지점**: 세 건에 대해 (a) 업스트림 이슈를 우리가 제출할 것인가, (b) 라벨을 `wontfix`/`roadmap`으로 정직하게 바꿀 것인가, (c) 그대로 둘 것인가. -#92는 반대 사례다 — 우리가 직접 upstream 이슈를 제출했고(코멘트 이력에 -"Draft is ready and will be filed separately"), 주기적 상태 점검 -(2026-07-23 코멘트)까지 남겼다. 이게 upstream-tracking의 모범 형태다. +#417이 유일한 모범 사례다: openai/codex#35161이 실재하고 OPEN이며 2026-07-24에 +갱신됐다. ## 발견 3 — #92의 업스트림 인용도 정확하지 않다 diff --git a/devlog/_plan/260727_owner_decision_ledger/003_enhancement_ledger.md b/devlog/_plan/260727_owner_decision_ledger/003_enhancement_ledger.md index a6d7af6d40..8f3bc43e19 100644 --- a/devlog/_plan/260727_owner_decision_ledger/003_enhancement_ledger.md +++ b/devlog/_plan/260727_owner_decision_ledger/003_enhancement_ledger.md @@ -1,6 +1,8 @@ # 003 — enhancement 전수 판단 -대상: `enhancement` 라벨이 붙은 열린 이슈 11건 + 라벨 없는 신규 2건. +대상: `enhancement` 라벨이 붙은 열린 이슈 **13건** +(`[540,476,425,415,414,401,386,294,201,178,177,95,42]`) + 라벨 없는 신규 2건 +(#545, #546). ## 판단 축 @@ -17,7 +19,7 @@ enhancement에 대한 오너 결정은 세 가지 중 하나로 수렴한다: | # | 제목 | 라벨 | 현재 상태 | 결정 필요 지점 | | --- | --- | --- | --- | --- | -| 42 | Storage 페이지 + 세션 정리 정책 | enhancement, roadmap | Phase 1 완료, Phase 2가 PR #529 | Phase 3(자동 정리 정책) 착수 여부 | +| 42 | Storage 페이지 + 세션 정리 정책 | enhancement, roadmap | Phase 1·2 완료 (**#529 MERGED**) | Phase 2.1(복원 UI) / Phase 3(자동 정책) 착수 여부 | | 95 | 멀티유저 프록시 + LiteLLM | enhancement, roadmap | 07-22 "long-term roadmap" 확정 | 프로젝트 사이클 배정 시점. **아래 A 참조** | | 177 | Warp 프로바이더 | enhancement, roadmap | 07-22 roadmap 일괄 라벨 | 인증 경로 실재 여부 미조사 | | 178 | Factory 프로바이더 | enhancement, roadmap | 07-22 roadmap 일괄 라벨 | 동일 | @@ -28,9 +30,20 @@ enhancement에 대한 오너 결정은 세 가지 중 하나로 수렴한다: | 414 | Exa 등 검색 프로바이더 사이드카 | enhancement | 자체 제출 (#398 분할) | 착수 순서 | | 415 | Gemini 등 자체 검색 API 사이드카 | enhancement | 자체 제출 (#398 분할) | 414와 선후 관계. **아래 C 참조** | | 425 | Codex 계정 네임스페이스 | enhancement | PR #512가 foundation 담당 | 나머지 레이어 착수 | -| 476 | 카탈로그 변경이 running app-server 미반영 | enhancement | PR #526/#527 승계 | #527이 잘못된 base를 향함 | +| 476 | 카탈로그 변경이 running app-server 미반영 | enhancement | PR #526/#527 승계 | #527 base 수동 리타깃 (순서 의존 없음) | | 540 | WordPress Studio Code 프로바이더 | enhancement, provider-compat, roadmap | 07-27 ACCEPT-GATED 확정 | **이미 결정 완료** — 모범 사례 | +### 라벨 없는 신규 2건 + +| # | 제목 | 라벨 | 현재 상태 | 결정 필요 지점 | +| --- | --- | --- | --- | --- | +| 545 | Auto Mode 권한 분류기 64토큰 절단 | **없음** | 미분류, 응답 없음 | 라벨 + 조사 착수. bug 후보 | +| 546 | Desktop 3P 모델 피커 미반영 | **없음** | 미분류, 응답 없음 | 라벨 + 문서 수정 경로 채택 여부 | + +> `roadmap` 라벨 7건(`[540,294,201,178,177,95,42]`)은 아래 세 판단 축 중 +> 어디에도 속하지 않는 상태다. 이 자기모순은 묶음 G에서 다룬다 — +> `001_decision_bundles.md` G, `006_corrections.md` S5. + ## A — 프로바이더 요청군의 구조적 문제 (#177 / #178 / #201 / #540) 네 건은 같은 형태다: "X를 프로바이더로 추가해달라". 그런데 #540만 실제로 diff --git a/devlog/_plan/260727_owner_decision_ledger/004_pr_decision_ledger.md b/devlog/_plan/260727_owner_decision_ledger/004_pr_decision_ledger.md index ae9e8006d1..f666dfe489 100644 --- a/devlog/_plan/260727_owner_decision_ledger/004_pr_decision_ledger.md +++ b/devlog/_plan/260727_owner_decision_ledger/004_pr_decision_ledger.md @@ -8,7 +8,7 @@ | --- | --- | --- | --- | --- | --- | --- | --- | | 533 | update: npm 캐시 실패 시 프록시 보존 | bug | ready | MERGEABLE/UNSTABLE | CHANGES_REQ | +2405/-73, 21f | 재리뷰 | | 528 | images: #424 P2 후속 | bug | ready | CLEAN | — | +2770/-60, 21f | **#424 의존** | -| 527 | codex: stale app-server 경고 | bug | ready | UNSTABLE | — | +1162/-45, 15f | **base가 dev 아님** | +| 527 | codex: stale app-server 경고 | bug | ready | MERGEABLE (enforce-target FAILURE) | — | +1162/-45, 15f | **base가 dev 아님** | | 526 | codex: 카탈로그 write 여부 보고 | bug | ready | CLEAN | — | +86/-14, 6f | 머지 가능 | | 512 | codex: 계정 네임스페이스 foundation | enhancement | ready | UNSTABLE | CHANGES_REQ | +1698/-46, 28f | 설계 승인 | | 498 | codex: native subagent 기본값 opt-in | enhancement | draft | **CONFLICTING** | CHANGES_REQ | +2427/-286, 55f | 정책 | @@ -16,13 +16,17 @@ | 493 | quota: Claude 계정별 한도 | enhancement | draft | **CONFLICTING** | CHANGES_REQ | +291/-20, 14f | **#294 승격 여부** | | 491 | oauth: 로그인이 저장된 API 키 삭제 | bug | draft | **CONFLICTING** | CHANGES_REQ | +251/-4, 3f | 소규모, 충돌만 | | 461 | cli: `ocx opencode` 런처 | enhancement | draft | **CONFLICTING** | — | +868/-0, 6f | **표면 확장 여부** | -| 447 | kiro: 브라우저 멀티계정 로그인 | bug | draft | CLEAN | CHANGES_REQ | +1625/-137, 20f | 재리뷰 | +| 447 | kiro: 브라우저 멀티계정 로그인 | bug | draft | CLEAN | CHANGES_REQ | +1625/-137, 20f | **인증 경계 리뷰** | | 429 | cursor: shell-alias 힌트 주입 제거 | bug | draft | **CONFLICTING** | — | +48/-37, 5f | 소규모, 충돌만 | | 424 | images: Grok 이미지 브리지 | enhancement | draft | UNSTABLE | CHANGES_REQ | +2333/-59, 22f | **유료 호출 정책** | | 355 | google: Gemini 인라인 이미지 | enhancement | draft | UNSTABLE | CHANGES_REQ | +1435/-15, 16f | **424와 중복 축** | 합계: ready 4, draft 10. CONFLICTING 5건. CHANGES_REQUESTED 9건. +> #533은 `002_pr_triage_matrix.md:72`에서 `NEEDS-SECURITY-REVIEW`(의존성 설치 +> 경계), #447은 같은 문서 80행에서 인증 경계로 이미 분류돼 있다. 초안이 둘 다 +> "단순 재리뷰"로 낮춰 기록했던 것을 복원했다. + ## 결정 축 1 — 이미지 생성 경로가 두 개다 (#424 / #355 / #528) 이게 가장 큰 미결정이다. 두 PR이 **같은 사용자 문제**를 서로 다른 방식으로 푼다: @@ -36,7 +40,18 @@ | 아키텍처 | `src/web-search/` 사이드카 패턴 복제 | 기존 google 어댑터 확장 | #528은 #424의 P2 후속이고 본문이 "**Depends on #424** — 먼저 #424를 머지하라"고 -명시한다. 즉 #424를 받으면 #528도 따라온다: 합계 +5100줄. +명시한다. 다만 실측상 **#528은 #424의 현재 head를 포함하지 않는다**: + +``` +424 head = a8b769c9 +528 head = 553e9afc +git merge-base --is-ancestor pr424 pr528 → false +``` + +#528 본문이 밝힌 분기점은 `6d6b252`이고 그 뒤 #424가 더 진행됐다. 따라서 +#424를 먼저 머지하면 **#528은 리베이스가 필요하다.** "따라온다"는 초안 서술은 +틀렸다. 두 PR 합계 규모는 +5100줄 수준이지만 자동으로 딸려오지 않는다. +(`002_pr_triage_matrix.md:110`이 이미 같은 결론을 냈고 초안이 놓쳤다.) **결정 지점**: 둘 다 받을 것인가, 하나만 받을 것인가, 어느 쪽을 기본 경로로 삼을 것인가. 둘 다 받으면 "이미지 생성 시 어느 백엔드로 가는가"라는 새 라우팅 @@ -56,8 +71,14 @@ 두 PR은 #476(카탈로그 변경 미반영)을 승계한 쌍이다. #526(신호 보고, +86줄, CLEAN)이 먼저 머지되면 #527의 base를 `dev`로 리타깃할 수 있다. -**결정 지점**: 스택 PR을 허용할 것인가(=워크플로 예외), 아니면 #526 머지 후 -리타깃할 것인가. 후자가 기존 정책과 일관되지만 순서 강제가 필요하다. +**오너 결정 아님 — 실행 순서 문제.** `.github/workflows/enforce-pr-target.yml:26`이 +`ALLOWED_BASES = ["dev","dev2-go"]`를 하드코딩하고 AGENTS.md 브랜치 정책에 +제3의 타깃이 없다. 스택 PR 허용은 이미 배제돼 있다. + +추가 정정: `delete_branch_on_merge=false`이므로 **#526을 머지해도 #527의 base가 +자동으로 옮겨가지 않는다.** 리타깃은 수동이며 #526 머지 여부와 무관하게 지금 +할 수 있다. 초안이 기술한 선후 의존은 존재하지 않는다 +(`002_pr_triage_matrix.md:67`에 같은 지적이 있다). ## 결정 축 3 — 계정 정책 3종 (#512 / #495 / #498) @@ -79,14 +100,22 @@ CLEAN)이 먼저 머지되면 #527의 base를 `dev`로 리타깃할 수 있다. 하나의 사이클로 묶어 일관된 모델을 먼저 정할 것인가. #498은 55파일 CONFLICTING이라 어차피 재작업이 필요하다. +> **묶음 정정.** #498은 `main` 계정 의미 축이 아니다. Codex native `[agents]` +> TOML 편집이고 계정 정체성과 무관하다. "opt-in 플래그"라는 모양만 공유한다. +> 묶음 B에서 분리한다 — `006_corrections.md` S4. + ## 결정 축 4 — 새 클라이언트 표면 (#461) -`ocx opencode`는 `ocx claude`, native Codex injection에 이은 **세 번째 클라이언트 -표면**이다. +868/-0, 순수 추가. +`ocx opencode`는 `ocx claude`, native Codex injection에 이은 **새 런처 표면**이다. ++868/-0, 순수 추가. + +정확히 말하면 "세 번째"는 **런처 명령** 기준이고(native injection, `ocx claude`, +`ocx opencode`), 우리가 지탱하는 **클라이언트 표면**은 그보다 많다: Codex +CLI/App/SDK, Claude Code, Claude Desktop 3P, Cursor, Kiro. 초안이 두 층위를 +섞어 "세 번째"와 "네 번째"를 파일마다 다르게 적었다. -표면이 늘면 유지 비용이 영구적으로 늘어난다. 지금도 Codex CLI/App/SDK, Claude -Code, Claude Desktop 3P, Cursor, Kiro를 지탱하고 있고, 이번 라운드 이슈 중 -상당수(#545, #546, #543)가 Desktop 3P 표면에서 나왔다. +표면이 늘면 유지 비용이 영구적으로 늘어난다. 이번 라운드 이슈 중 +상당수(#545, #546, #543)가 Desktop 3P 표면에서 나왔다는 점이 그 비용의 실례다. **결정 지점**: 표면을 하나 더 받을 것인가. 받는다면 지원 등급(1급/실험적)을 무엇으로 선언할 것인가. 현재 PR은 draft/CONFLICTING이고 리뷰가 없다. @@ -110,6 +139,14 @@ Code, Claude Desktop 3P, Cursor, Kiro를 지탱하고 있고, 이번 라운드 기여자 응답을 기다릴 것인가. 메모리 기준 "우리가 수정할 수 있으면 rework나 얹어서 수정하는 방향"에 해당하는 후보군이다. +> **중대 정정.** #491을 "작으니까 먼저"로 다룬 것은 철회한다. 변경 파일은 +> `src/oauth/index.ts`, `package.json`, 테스트 1건 — **AGENTS.md가 최우선 +> 보안 경계로 규정한 크리덴셜 경로**다. `002_pr_triage_matrix.md:76`도 +> `NEEDS-AUTHOR — 보안 경계`로 분류했다. 크기가 아니라 경계가 기준이다. +> +> #429는 `src/adapters/cursor/*` 3파일 + 테스트 2건으로 보안 경계 밖이다. +> 우리가 리베이스할 수 있는 후보는 **#429뿐**이다. + ## 참고 — #529는 이미 머지됨 `001_issue_triage_matrix.md`가 #42를 "PR #529가 phase 2 담당, IN-FLIGHT"로 diff --git a/devlog/_plan/260727_owner_decision_ledger/005_open_questions.md b/devlog/_plan/260727_owner_decision_ledger/005_open_questions.md index 1c447dd434..58c69e1acd 100644 --- a/devlog/_plan/260727_owner_decision_ledger/005_open_questions.md +++ b/devlog/_plan/260727_owner_decision_ledger/005_open_questions.md @@ -4,6 +4,9 @@ ## 오너만 답할 수 있는 것 (실제 질문) +> 감사 결과 초안 Q의 일부가 "근거로 답할 수 있는 것"으로 판명돼 제거됐다. +> 제거 내역은 `006_corrections.md` F1~F4. + ### Q1 — 이미지 생성 백엔드 (묶음 A) #424(xAI Grok, 유료 호출)와 #355(Gemini/CCA, 기존 계정) 중 무엇이 기본 경로인가. @@ -11,59 +14,83 @@ 선택지: - (a) #355 먼저 — 사용자 추가 비용 없음, 규모 작음(+1435). #424는 보류 -- (b) #424+#528 먼저 — 이미 후속 PR까지 준비됨. 대신 +5100줄, 유료 호출 노출 +- (b) #424+#528 먼저 — 후속 PR이 이미 있음(단 리베이스 필요). +5100줄, 유료 호출 노출 - (c) 둘 다 수용 + 백엔드 선택 라우팅 규칙 신설 — 가장 크지만 완전 ### Q2 — 계정 정책 묶음 처리 (묶음 B) -#512/#495/#498을 개별 승인할 것인가, 계정 모델을 먼저 정하는 한 사이클로 묶을 -것인가. 그리고 #493을 #294의 첫 조각으로 승격할 것인가. +#512/#495를 개별 승인할 것인가, `main` 계정 의미를 먼저 정하는 한 사이클로 +묶을 것인가. 그리고 #493을 #294(Claude 계정 풀)의 첫 조각으로 승격할 것인가. ### Q3 — 프로바이더 기준 소급 (묶음 D) #540 기준(공식 인증 경로 없으면 미구현)을 #177/#178/#201에 소급할 것인가. 소급하면 일부는 DECLINE으로 닫힌다. -### Q4 — 표면 확장 (#461) +### Q4 — 로드맵 정직성 (묶음 G) + +`roadmap` 7건에 실제 상태(사이클 배정 / 조건부 수락 / 거절)를 부여할 것인가, +현행 유지할 것인가. 특히 #95(멀티유저 프록시)는 제품 배포 모델을 바꾸는 요구다. + +### Q5 — 표면 확장 (#461) -`ocx opencode`를 네 번째 클라이언트 표면으로 받을 것인가. 받는다면 지원 등급은. +`ocx opencode`를 새 런처 표면으로 받을 것인가. 받는다면 지원 등급은. -### Q5 — 이번 라운드의 실행 범위 +### Q6 — 다음 사이클의 실행 권한 -기록만 하고 끝낼 것인가, 아니면 이어지는 PABCD 사이클에서 어디까지 실제로 -손댈 것인가. 특히 GitHub 코멘트/라벨 변경 권한을 이번 라운드에 줄 것인가. +이번 기록 라운드는 읽기 전용으로 끝난다(`000_scope.md` 원칙 1). 이어지는 +PABCD 사이클에서 GitHub 상태 변경(코멘트, 라벨, 리타깃, 머지) 권한을 어디까지 +줄 것인가. 특히 보안 경계 묶음 F는 별도 취급이 필요하다. ## 근거로 판정 가능한 것 (질문 불필요) | 항목 | 판정 | 근거 | | --- | --- | --- | -| #543의 업스트림 인용 무효 | 확정 | anthropics/claude-code#1124 = CLOSED 2025-08-10 | +| #543의 업스트림 인용 무효 | 확정 | anthropics/claude-code#1124 = CLOSED, `closedAt=2025-05-16` | +| #543 마커 캡처용 스위치 존재 | 확정 | `ocx debug claude on` / `OCX_CLAUDE_DEBUG=1` (`src/lib/debug-settings.ts:7`) | +| stale 워크플로 미동작 | 확정 | `stale-needs-info.yml`이 `origin/main`에 없음. 기본 브랜치=`main` | | #529 머지 완료 | 확정 | `gh pr view 529` state=MERGED | | #527 base 위반 | 확정 | base=`codex/catalog-written-signal`, enforce-target=FAILURE | -| #528의 #424 의존 | 확정 | PR 본문이 명시 | +| #527 리타깃은 #526과 무관 | 확정 | `delete_branch_on_merge=false`, 수동 리타깃 | +| #528이 #424 head를 미포함 | 확정 | `git merge-base --is-ancestor` false (424=`a8b769c9`, 528=`553e9afc`) | +| 스택 PR 불허 | 확정 | `enforce-pr-target.yml:26` `ALLOWED_BASES=["dev","dev2-go"]` | +| #491/#533/#447이 보안 경계 | 확정 | `src/oauth/index.ts` 등 + AGENTS.md 최우선 규칙 | +| #545의 502 표기는 우리 코드 | 확정 | `src/server/request-log.ts:610` | | #462/#401/#241 업스트림 티켓 부재 | 확정 | 본문+코멘트 전문 grep 결과 0건 | +| #418에 담당 PR 없음 | 확정 | `gh pr list --state all --search "418 in:body"` = 빈 결과 | | #545/#546 라벨 없음 | 확정 | `gh issue list` 라벨 필드 공란 | -## 인터뷰 차원 점수 (I 단계 시작 시점) +## 인터뷰 차원 점수 (감사 후) | 차원 | 점수 | 근거 | | --- | --- | --- | -| Goal | 3 | "결정할 것을 전수 기록하고 묶는다"는 명확. 이후 실행 범위는 미정 | -| Constraint | 2 | 기록만 하는지, GitHub 상태를 바꿔도 되는지 미확정 | -| Success criteria | 2 | 묶음 6개가 산출물인지, 각 묶음의 결정까지가 산출물인지 미확정 | +| Goal | 4 | 전수 기록 + 묶음화 완료. 남은 것은 각 묶음의 방향 결정 | +| Constraint | 3 | 이번 라운드 읽기 전용은 확정. 다음 사이클 권한만 미정(Q6) | +| Success criteria | 3 | 커버리지는 충족(잔여 항목까지 명시 배정). 각 묶음 결정이 done | | Ontology | 4 | 이슈/PR/업스트림/묶음 용어가 기존 devlog와 일관 | ## 루프 아키타입 -**spec-satisfaction.** 검증자가 done을 정의한다: "열린 이슈 23 + 열린 PR 14 -전부가 최소 한 묶음에 배정되었고, 각 항목마다 결정 지점이 명시되었는가"는 -기계적으로 확인 가능하다. open-ended 최적화가 아니다. +**decision-elicitation** (초안의 spec-satisfaction에서 정정). + +초안은 커버리지("전 항목이 묶음에 배정되었는가")를 done 기준으로 삼았으나, +그 검증자를 초안 자신이 위반했다. 더 근본적으로 커버리지는 **전제 조건**이지 +done이 아니다. 이 작업의 done은 "각 묶음에 대해 오너가 방향을 정했는가"다. + +커버리지 (전제 조건 — 충족): + +- **이슈 23건**: B(#425, #294) · C(#92, #417, #462, #401, #241, #418) · + D(#177, #178, #201) · E(#545, #546, #543) · G(#95, #386, #414, #415, #42) + + 결정 완료(#540) + PR 담당(#476) + 리포터 대기(#509, #521) + = 5+2+6+3+3 … 23건 전부 배정됨 +- **PR 14건**: A(#424, #355, #528) · B(#512, #495, #493) · F(#491, #533, #447) + + 묶음 외 명시 처리(#429, #526, #527, #461, #498) = 14건 전부 배정됨 -커버리지 확인: +`001_decision_bundles.md`의 "묶음에 들지 않는 잔여" 표가 미배정 항목을 명시 +처리한다. 초안처럼 각주로 면제하지 않는다. -- 이슈 23건 → A(0) B(2: #425,#294) C(6) D(3) E(2) F(0) + 이미 결정됨(#540) - + IN-FLIGHT PR 담당(#476, #42, #418) + roadmap 대기(#95, #386, #414, #415, #509, #521) -- PR 14건 → A(3) B(4) F(4: #491,#429,#526,#527) + 개별(#533, #447, #461) +## 범위 밖으로 남기는 것 -> 미배정 잔여: 이슈 #509/#521(needs-info 자동 처리), #95/#386/#414/#415(roadmap), -> PR #533/#447(단순 재리뷰). 이들은 결정 축이 아니라 대기 상태이므로 묶지 않았다. +MAINTAINERS.md의 거버넌스 미결(브랜치 보호 설정, @Wibias 추가 요건 2)은 +오너 전용 결정이지만 **열린 이슈·PR이 아니다.** 이 원장의 범위는 GitHub +이슈/PR이므로 여기서 다루지 않고 별도 추적으로 남긴다. diff --git a/devlog/_plan/260727_owner_decision_ledger/006_corrections.md b/devlog/_plan/260727_owner_decision_ledger/006_corrections.md new file mode 100644 index 0000000000..1b8b6ba268 --- /dev/null +++ b/devlog/_plan/260727_owner_decision_ledger/006_corrections.md @@ -0,0 +1,126 @@ +# 006 — 정정 기록 + +초안(`0a230e0d`) 작성 후 독립 감사 2건(coverage lens / decision-framing lens, +read-only)을 돌렸다. 30건의 모순 지적 중 실측으로 확인된 것을 반영한다. + +## 사실 오류 — 정정 완료 + +| # | 초안 주장 | 실측 | 반영 | +| --- | --- | --- | --- | +| C1 | anthropics/claude-code#1124 CLOSED **2025-08-10** | `closedAt=2025-05-16T14:10:06Z` | 002, 001, 005 정정 | +| C2 | enhancement 라벨 이슈 **11**건 | **13**건 | 000, 003 정정 | +| C3 | #509/#521이 roadmap 대기 | 둘 다 `needs-info` 단독. roadmap은 `[540,294,201,178,177,95,42]` | 005 정정 | +| C4 | #418이 IN-FLIGHT PR 담당 | #418에 연결된 PR **없음** | 005 정정, 묶음 C에 편입 | +| C5 | #42가 IN-FLIGHT | #529 MERGED. Phase 2 완료 | 003, 005 정정 | +| C6 | #424 머지하면 #528 따라옴 (+5100) | `git merge-base --is-ancestor` 실측: **528은 424의 현재 head(`a8b769c9`)를 포함하지 않음**. 528 head=`553e9afc` | 004 정정 | +| C7 | #526 머지되면 #527 리타깃 가능 | `delete_branch_on_merge=false`. 리타깃은 **수동**이며 #526 머지와 무관 | 004 정정 | +| C8 | #461이 "세 번째" / "네 번째" 표면 | 파일 간 불일치. 현재 표면 6개 열거와도 불일치 | 004, 005 통일 | +| C9 | #543이 `upstream-tracking` 묶음 | #543 라벨 = `bug, provider-compatibility, needs-info` | 001 정정 | + +## 프레이밍 오류 — 질문에서 제거 + +감사가 "오너에게 물을 필요 없다"고 지적했고 실측으로 동의한 것들. 질문을 +줄이는 것이 인터뷰 품질을 올린다. + +### F1 — stale 워크플로 위험은 과장 + +초안은 "#543이 stale 워크플로에 잘못 걸린다"를 묶음 C의 긴급성 근거로 삼았다. + +실측: + +- `stale-needs-info.yml`은 `origin/dev`에만 있고 **`origin/main`에 없다**. + 기본 브랜치는 `main`이고, 예약 워크플로는 기본 브랜치에서만 돈다. + → **현재 이 워크플로는 아예 실행되지 않는다.** +- 설사 돌더라도 `days-before-issue-stale: 14` + `remove-stale-when-updated: true`, + #543 `updatedAt=2026-07-27T08:34Z`. 최소 14일 여유. + +묶음 C의 긴급성 근거는 철회한다. 정직성 문제는 남지만 시한은 없다. + +### F2 — #543에 답할 로깅 스위치는 이미 존재한다 + +리포터가 "지원되는 로깅 스위치를 알려주면 마커 캡처를 하겠다"고 되물었고, +초안은 이를 오너 결정으로 분류했다. 실제로는 `src/lib/debug-settings.ts:7`에 +`ocx debug claude on|off|status|reset` / `OCX_CLAUDE_DEBUG=1`이 있고 한국어·중국어 +문서에도 나와 있다. 답변만 하면 되는 지원 업무다. + +### F3 — 스택 PR 허용 여부는 이미 정해져 있다 + +초안은 #527에 대해 "스택 PR을 허용할 것인가(워크플로 예외)"를 물으려 했다. +`.github/workflows/enforce-pr-target.yml:26`이 `ALLOWED_BASES = ["dev","dev2-go"]`를 +하드코딩하고, AGENTS.md 브랜치 정책에 제3의 타깃이 없다. 정해진 정책이므로 +질문이 아니라 실행 순서 문제다. + +### F4 — #545가 우리 결함인지는 코드로 판정 가능 + +초안은 이를 오너 질문으로 뒀으나 실측으로 좁혀진다: + +- `src/claude/inbound.ts:436` — `raw.max_tokens`를 `body.max_output_tokens`로 + **클램프 없이** 그대로 전달 +- `src/server/claude-messages.ts:596` — native ChatGPT passthrough + (`openai-responses` 어댑터) 경로에서만 `max_output_tokens`를 **삭제**. + 라우팅된 프로바이더는 유지 +- `src/claude/outbound.ts:411` — `incomplete_details.reason === "max_output_tokens"`를 + `finish("max_tokens")`로 정상 종료 처리 +- `src/server/request-log.ts:610` — `httpStatusForTerminalStatus`가 + `completed`가 아닌 모든 terminal 상태를 **502**로 기록 + +즉 제보자가 본 "502 대량 누적"은 최소한 **로그 표기 층**에서 우리 코드가 만든다. +`max_tokens: 64`가 어디서 오는지(클라이언트가 보낸 값인지)는 추가 확인이 +필요하지만, "우리 결함인가"는 조사로 답할 문제이지 오너가 정할 문제가 아니다. + +남는 오너 결정은 라벨과 우선순위뿐이다. + +## 구조 오류 — 묶음 재편 + +### S1 — 묶음 F가 두 축을 섞었다 + +초안 F는 "우리가 리베이스할 소형 PR(#491, #429)"과 "#526→#527 머지 순서"를 +같이 넣었다. 후자는 리베이스 문제가 아니라 base 리타깃 문제다. 분리한다. + +### S2 — #491을 워밍업으로 둔 것은 리포지토리 정책 위반 + +`gh pr view 491 --json files`: `src/oauth/index.ts`, `package.json`, 테스트 1건. +AGENTS.md는 인증·크리덴셜 경로 변경을 **최우선 보안 경계**로 규정하고 명시적 +보안 리뷰를 요구한다. `002_pr_triage_matrix.md:76`도 이미 #491을 +`NEEDS-AUTHOR — 보안 경계`로 분류했다. + +#491을 "작으니까 먼저"로 다룬 것은 철회한다. 크기가 아니라 경계가 기준이다. +#429는 `src/adapters/cursor/*` 3파일 + 테스트 2건으로 보안 경계 밖이다. + +### S3 — #533/#447을 "단순 재리뷰"로 낮춘 것도 철회 + +`002_pr_triage_matrix.md:72,80`이 #533을 `NEEDS-SECURITY-REVIEW`(의존성 설치 +경계), #447을 인증 경계로 분류해 뒀다. 초안이 이를 "재리뷰"로 낮춰 기록했다. +원래 분류를 복원한다. + +### S4 — #498은 묶음 B의 축이 아니다 + +B의 축은 "`main` 계정의 의미"다. #498(`syncCodexSubagentDefaults`)은 Codex native +`[agents]` TOML 편집이고 `main` 계정 의미와 무관하다. "opt-in 플래그"라는 +모양만 같다. B에서 분리한다. + +### S5 — roadmap 방치를 예외로 둔 것이 자기모순 + +`003`이 "ACCEPT-CYCLE/ACCEPT-GATED/DECLINE 중 어느 것도 아닌 채 열려 있는 것이 +가장 나쁘다"고 써 놓고, `005`가 #95/#386/#414/#415를 "대기 상태이므로 묶지 +않았다"로 면제했다. roadmap은 세 상태 중 어느 것도 아니다. + +→ 묶음 G(로드맵 정직성)를 신설해 편입한다. + +## 아키타입 재분류 + +초안은 **spec-satisfaction**이라 했다. 근거로 든 검증자("열린 이슈 23 + PR 14 +전부가 묶음에 배정")를 초안 자신이 위반했다(#509/#521/#95/#386/#414/#415, +PR #533/#447 미배정). + +실제 성격은 **decision-elicitation**이다. done을 정의하는 것은 커버리지가 아니라 +"각 묶음에 대해 오너가 방향을 정했는가"다. 커버리지는 전제 조건일 뿐이다. +묶음 G 신설로 커버리지는 채우되, 아키타입은 정정한다. + +## 반영하지 않은 지적 + +| 지적 | 사유 | +| --- | --- | +| #95를 "숨은 미결정"으로 봐야 한다 | 동의하나 묶음 G로 흡수되므로 별도 항목 불필요 | +| MAINTAINERS.md 거버넌스(브랜치 보호, @Wibias 요건 2)가 누락 | 타당한 지적이나 **이슈/PR 범위 밖**이다. 이번 원장은 "열린 이슈·PR 중 결정할 것"이 범위이므로 별도 추적으로 남긴다 — `005` 참조 | +| #462가 needs-info인데 묶음 C에 있다 | C에 유지한다. `upstream-tracking` 라벨을 달고 업스트림 티켓이 없다는 사실은 needs-info 여부와 독립이다 | From 7fcdc77065c902ae8c525dc3bd766eaa6eb27fec Mon Sep 17 00:00:00 2001 From: bitkyc08-arch Date: Mon, 27 Jul 2026 19:45:45 +0900 Subject: [PATCH 03/10] docs(devlog): record live unfinished triage manifest --- .../goalplan.json | 269 ++++++++++++++++++ .../ledger.jsonl | 1 + .../260727_live_unfinished_triage/000_plan.md | 57 ++++ .../001_live_pr_manifest.md | 35 +++ .../002_live_issue_manifest.md | 42 +++ .../003_priority_table.md | 31 ++ .../010_pr526_catalog_write_signal.md | 41 +++ .../020_pr528_image_bridge_p2.md | 36 +++ 8 files changed, 512 insertions(+) create mode 100644 .codexclaw/goalplans/opencodex-live-unfinished-issues-and-prs-triage/goalplan.json create mode 100644 .codexclaw/goalplans/opencodex-live-unfinished-issues-and-prs-triage/ledger.jsonl create mode 100644 devlog/_plan/260727_live_unfinished_triage/000_plan.md create mode 100644 devlog/_plan/260727_live_unfinished_triage/001_live_pr_manifest.md create mode 100644 devlog/_plan/260727_live_unfinished_triage/002_live_issue_manifest.md create mode 100644 devlog/_plan/260727_live_unfinished_triage/003_priority_table.md create mode 100644 devlog/_plan/260727_live_unfinished_triage/010_pr526_catalog_write_signal.md create mode 100644 devlog/_plan/260727_live_unfinished_triage/020_pr528_image_bridge_p2.md diff --git a/.codexclaw/goalplans/opencodex-live-unfinished-issues-and-prs-triage/goalplan.json b/.codexclaw/goalplans/opencodex-live-unfinished-issues-and-prs-triage/goalplan.json new file mode 100644 index 0000000000..5ba46a4aac --- /dev/null +++ b/.codexclaw/goalplans/opencodex-live-unfinished-issues-and-prs-triage/goalplan.json @@ -0,0 +1,269 @@ +{ + "objective": "OpenCodex live unfinished issues and PRs triage against current GitHub state. Scope: repository lidge-jun/opencodex, dev target only, worktree /Users/jun/.codex/worktrees/260727-live-triage/opencodex. First work-phase is docs-only manifest/devlog: fetch live open PRs and open issues via gh, record current number/title/state/base/head/mergeable/checks/reviews/labels, classify every item into merge now, takeover-fix, comment/request-changes, needs-author-rebase, needs-human/security, later/enhancement, upstream-tracking, or close, and produce a priority table. Later work-phases process exactly one PR or one issue per full PABCD cycle. Allowed: directly fix bugfix/simple safe items on dev, create PRs, wait for CI, squash merge, and close linked issues when live evidence proves safety. Out of scope: main/preview/release branches, automatic merge of security/auth/permission/data-migration/privilege-boundary work, and unapproved GUI/UX decisions except the approved OpenRouter Free separate-provider direction. Terminal outcomes: DONE when all safe live items are processed and manifest evidence is current; NOOP when an item needs no action after live check; NEEDS_HUMAN or UNSAFE for risk-bound/security/UX-decision items; BLOCKED for external author/rebase/CI or upstream dependency; BUDGET_EXHAUSTED only if explicit runtime bounds are hit. Verification: gh live snapshots, code/diff review for candidate PRs, CI/check URLs where actions occur, comments/merge/close URLs for external state changes, and devlog evidence committed locally before completion.", + "slug": "opencodex-live-unfinished-issues-and-prs-triage", + "createdAt": "2026-07-27T10:35:02.724Z", + "updatedAt": "2026-07-27T10:43:20.000Z", + "activeWorkPhaseId": "WP0", + "workPhases": [ + { + "id": "WP0", + "title": "Live GitHub triage manifest and priority table", + "status": "pending", + "tasks": [ + { + "id": "WP0-T1", + "title": "Refresh dev branch/worktree state from origin/dev without touching main/preview/release branches", + "status": "done" + }, + { + "id": "WP0-T2", + "title": "Query all current open PRs and issues via gh with state/base/head/mergeable/checks/reviews/labels", + "status": "done" + }, + { + "id": "WP0-T3", + "title": "Write numbered docs-first devlog manifest and priority table", + "status": "done" + }, + { + "id": "WP0-T4", + "title": "Append follow-up one-item work-phases for safe merge/fix/close candidates discovered by the manifest", + "status": "done" + } + ], + "criteriaIds": [ + "C-WP0-LIVE-MANIFEST" + ] + }, + { + "id": "WP1", + "title": "PR #526 catalog write signal rebase and coverage decision", + "status": "pending", + "tasks": [ + { + "id": "WP1-T1", + "title": "Re-check PR #526 live head, checks, diff, and independent review", + "status": "pending" + }, + { + "id": "WP1-T2", + "title": "Take over rebase/tests or leave a documented blocker for PR #526 only", + "status": "pending" + } + ], + "criteriaIds": [ + "C-WP1-PR526" + ] + }, + { + "id": "WP2", + "title": "PR #528 image bridge credential-origin request changes", + "status": "pending", + "tasks": [ + { + "id": "WP2-T1", + "title": "Re-check PR #528 live head, checks, diff, and independent review", + "status": "pending" + }, + { + "id": "WP2-T2", + "title": "Post request-changes comment for credential-origin binding and stale checks", + "status": "pending" + } + ], + "criteriaIds": [ + "C-WP2-PR528" + ] + }, + { + "id": "WP3", + "title": "Issue #543 Claude queued_command support reply", + "status": "pending", + "tasks": [ + { + "id": "WP3-T1", + "title": "Verify existing debug capture switch and issue context", + "status": "pending" + }, + { + "id": "WP3-T2", + "title": "Post one maintainer comment requesting marker frames", + "status": "pending" + } + ], + "criteriaIds": [ + "C-WP3-ISSUE543" + ] + }, + { + "id": "WP4", + "title": "Issue #547 Claude Desktop custom-model visibility reply", + "status": "pending", + "tasks": [ + { + "id": "WP4-T1", + "title": "Verify issue #547 context and likely evidence gaps", + "status": "pending" + }, + { + "id": "WP4-T2", + "title": "Post one maintainer comment requesting generated config/log/profile evidence", + "status": "pending" + } + ], + "criteriaIds": [ + "C-WP4-ISSUE547" + ] + }, + { + "id": "WP5", + "title": "Issue #545 64-token classifier investigation", + "status": "pending", + "tasks": [ + { + "id": "WP5-T1", + "title": "Trace max_tokens/max_output_tokens path for Desktop 3P classifier requests", + "status": "pending" + }, + { + "id": "WP5-T2", + "title": "Fix if local bug is proven; otherwise comment with exact evidence needed", + "status": "pending" + } + ], + "criteriaIds": [ + "C-WP5-ISSUE545" + ] + }, + { + "id": "WP6", + "title": "PR #527 wrong-base handling", + "status": "pending", + "tasks": [ + { + "id": "WP6-T1", + "title": "Re-check #527 after #526 decision", + "status": "pending" + }, + { + "id": "WP6-T2", + "title": "Retarget/request rebase or leave blocker; do not merge in same phase as #526", + "status": "pending" + } + ], + "criteriaIds": [ + "C-WP6-PR527" + ] + }, + { + "id": "WP7", + "title": "Issue #418 V2 custom delegation investigation", + "status": "pending", + "tasks": [ + { + "id": "WP7-T1", + "title": "Read custom-parent/custom-child delegation code path and issue evidence", + "status": "pending" + }, + { + "id": "WP7-T2", + "title": "Fix or comment with code pointers and required reproduction evidence", + "status": "pending" + } + ], + "criteriaIds": [ + "C-WP7-ISSUE418" + ] + }, + { + "id": "WP8", + "title": "Issue #509 JS heap watchdog investigation", + "status": "pending", + "tasks": [ + { + "id": "WP8-T1", + "title": "Trace RSS/heap watchdog logic and issue evidence", + "status": "pending" + }, + { + "id": "WP8-T2", + "title": "Fix or comment with code pointers and blocker", + "status": "pending" + } + ], + "criteriaIds": [ + "C-WP8-ISSUE509" + ] + } + ], + "criteria": [ + { + "id": "C-WP0-LIVE-MANIFEST", + "scenario": "Live GitHub state has been refreshed and every open PR/open issue is classified into the requested triage buckets.", + "expectedEvidence": "devlog/_plan/260727_live_unfinished_triage/ numbered manifest files with gh snapshot timestamp, PR/issue lists, classification rationale, and priority table.", + "capturedEvidence": "", + "status": "open" + }, + { + "id": "C-WP1-PR526", + "scenario": "PR #526 is either taken over with rebase/direct coverage or left open with a fresh blocker comment.", + "expectedEvidence": "live PR head/checks, independent review verdict, test evidence or comment URL.", + "capturedEvidence": "", + "status": "open" + }, + { + "id": "C-WP2-PR528", + "scenario": "PR #528 receives a request-changes comment for credential-origin binding and stale checks.", + "expectedEvidence": "live PR head/checks, independent review verdict, and comment/review URL.", + "capturedEvidence": "", + "status": "open" + }, + { + "id": "C-WP3-ISSUE543", + "scenario": "Issue #543 receives a concrete support reply naming the existing debug capture switch and requested evidence.", + "expectedEvidence": "comment URL plus code/docs pointers for `ocx debug claude` or `OCX_CLAUDE_DEBUG=1`.", + "capturedEvidence": "", + "status": "open" + }, + { + "id": "C-WP4-ISSUE547", + "scenario": "Issue #547 receives a concrete evidence request for Claude Desktop custom-model visibility.", + "expectedEvidence": "comment URL plus requested config/log/profile evidence.", + "capturedEvidence": "", + "status": "open" + }, + { + "id": "C-WP5-ISSUE545", + "scenario": "Issue #545 is narrowed to a proven local fix or exact remaining evidence need.", + "expectedEvidence": "code pointers, test/command output if fixed, PR/comment URL.", + "capturedEvidence": "", + "status": "open" + }, + { + "id": "C-WP6-PR527", + "scenario": "PR #527 wrong-base state is resolved or documented after PR #526 decision.", + "expectedEvidence": "live base/check state and retarget/request-rebase/comment URL.", + "capturedEvidence": "", + "status": "open" + }, + { + "id": "C-WP7-ISSUE418", + "scenario": "Issue #418 has a proven fix or a fresh investigation comment with code pointers.", + "expectedEvidence": "test/command output if fixed, otherwise comment URL plus code pointers.", + "capturedEvidence": "", + "status": "open" + }, + { + "id": "C-WP8-ISSUE509", + "scenario": "Issue #509 heap watchdog gap has a proven fix or a fresh investigation comment with code pointers.", + "expectedEvidence": "test/command output if fixed, otherwise comment URL plus code pointers.", + "capturedEvidence": "", + "status": "open" + } + ], + "host": { + "armed": true, + "armedAt": "2026-07-27T10:35:02.725Z", + "source": "freeze" + } +} diff --git a/.codexclaw/goalplans/opencodex-live-unfinished-issues-and-prs-triage/ledger.jsonl b/.codexclaw/goalplans/opencodex-live-unfinished-issues-and-prs-triage/ledger.jsonl new file mode 100644 index 0000000000..332c4d80f5 --- /dev/null +++ b/.codexclaw/goalplans/opencodex-live-unfinished-issues-and-prs-triage/ledger.jsonl @@ -0,0 +1 @@ +{"ts":"2026-07-27T10:35:02.725Z","slug":"opencodex-live-unfinished-issues-and-prs-triage","event":"created","detail":"init objective=\"OpenCodex live unfinished issues and PRs triage against current GitHub state. Scope: repository lidge-jun/opencodex, dev target only, worktree /Users/jun/.codex/worktrees/260727-live-triage/opencodex. First work-phase is docs-only manifest/devlog: fetch live open PRs and open issues via gh, record current number/title/state/base/head/mergeable/checks/reviews/labels, classify every item into merge now, takeover-fix, comment/request-changes, needs-author-rebase, needs-human/security, later/enhancement, upstream-tracking, or close, and produce a priority table. Later work-phases process exactly one PR or one issue per full PABCD cycle. Allowed: directly fix bugfix/simple safe items on dev, create PRs, wait for CI, squash merge, and close linked issues when live evidence proves safety. Out of scope: main/preview/release branches, automatic merge of security/auth/permission/data-migration/privilege-boundary work, and unapproved GUI/UX decisions except the approved OpenRouter Free separate-provider direction. Terminal outcomes: DONE when all safe live items are processed and manifest evidence is current; NOOP when an item needs no action after live check; NEEDS_HUMAN or UNSAFE for risk-bound/security/UX-decision items; BLOCKED for external author/rebase/CI or upstream dependency; BUDGET_EXHAUSTED only if explicit runtime bounds are hit. Verification: gh live snapshots, code/diff review for candidate PRs, CI/check URLs where actions occur, comments/merge/close URLs for external state changes, and devlog evidence committed locally before completion.\" criteria=0"} diff --git a/devlog/_plan/260727_live_unfinished_triage/000_plan.md b/devlog/_plan/260727_live_unfinished_triage/000_plan.md new file mode 100644 index 0000000000..a2476e62a8 --- /dev/null +++ b/devlog/_plan/260727_live_unfinished_triage/000_plan.md @@ -0,0 +1,57 @@ +# 000 — live unfinished triage plan + +Snapshot time: 2026-07-27T10:41:30Z. +Repository: `lidge-jun/opencodex`. +Working branch: `codex/260727-live-triage`, based on local `dev` after rebase onto +`origin/dev@7fcaa9119`. + +## Loop spec + +- Archetype: spec-satisfaction triage and repair loop. +- Trigger: maintainer requested live re-triage of every unfinished issue and PR, then + safe processing one item per PABCD cycle. +- Goal: produce a current manifest and process safe bugfix/simple items against `dev`. +- Non-goals: no `main`, `preview`, or release branch changes; no automatic merge of + auth/security/permission/data-migration/privilege-boundary changes; no new UX + decisions except the already approved `OpenRouter Free` separate-provider direction. +- Verifier: `gh` live state, PR diff/check/review inspection, and action URLs for any + GitHub comments/merges/closes. +- Stop condition: all live items are either processed or left in a justified + terminal bucket. +- Memory artifact: this numbered devlog folder plus the cxc goalplan ledger. +- Resource bounds: local filesystem and GitHub via `gh`; no provider/model settings + changes; no production release. +- Escalation condition: security/auth/credential/process-kill/paid-provider routing + surfaces stay `needs-human/security` unless a maintainer explicitly narrows the + decision. + +## Current branch evidence + +`git fetch --prune origin` moved `origin/dev` from `c05e88fdc` to `7fcaa9119`. +Local `dev` had two existing devlog commits and was rebased on top of `origin/dev`. + +Current bases: + +| ref | sha | +| --- | --- | +| `origin/dev` | `7fcaa9119` | +| local `dev` after rebase | `5bef68f5` | +| worktree branch | `5bef68f5` | + +## Work-phase map + +This first work-phase is docs-only. It creates the live manifest and appends concrete +follow-up work-phases. Later phases must process exactly one PR or one issue each. + +Initial next candidates from the live manifest: + +| next WP | item | planned bucket | action | +| --- | --- | --- | --- | +| WP1 | PR #526 | takeover-fix/rebase+tests | independent review found stale checks and missing direct write-path coverage | +| WP2 | PR #528 | needs-human/security + request-changes | credential-origin binding blocker in image bridge; do not merge | +| WP3 | issue #543 | comment/request-changes | answer with existing `ocx debug claude` capture switch and request marker frames | +| WP4 | issue #547 | comment/request-changes | new Claude Desktop custom-model visibility report; request exact config/log evidence | +| WP5 | issue #545 | takeover-fix/investigate | continue the 64-token classifier investigation; logging sub-bug already fixed in `7fcaa9119` | +| WP6 | PR #527 | needs-human/security + needs-author-rebase | wrong base plus process-termination/restart boundary | +| WP7 | issue #418 | takeover-fix/investigate | investigate V2 custom-parent to custom-child delegation failure | +| WP8 | issue #509 | takeover-fix/investigate | investigate JS heap watchdog gap | diff --git a/devlog/_plan/260727_live_unfinished_triage/001_live_pr_manifest.md b/devlog/_plan/260727_live_unfinished_triage/001_live_pr_manifest.md new file mode 100644 index 0000000000..790bd7fcf7 --- /dev/null +++ b/devlog/_plan/260727_live_unfinished_triage/001_live_pr_manifest.md @@ -0,0 +1,35 @@ +# 001 — live PR manifest + +Snapshot command: `gh pr list --repo lidge-jun/opencodex --state open --limit 200` +plus per-PR `gh pr view`. + +Open PR count: 14. + +| # | state | base | head | head sha | merge | review | checks | labels | bucket | rationale | +| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | +| 533 | ready | dev | fix/gui-update-install-failure-recovery | `9de10c7a` | MERGEABLE/UNSTABLE | CHANGES_REQUESTED | target/label success; CodeRabbit pending | bug | needs-human/security | dependency/npm cache ownership and update/install recovery; do not auto-merge with requested changes | +| 528 | ready | dev | fix/image-bridge-p2 | `553e9afc` | MERGEABLE/CLEAN | none | 8 CI jobs success + CodeRabbit success | bug | needs-human/security + request-changes | independent review found stale checks and credential-origin binding risk in `src/images/plan.ts` | +| 527 | ready | codex/catalog-written-signal | codex/app-server-restart | `a64aa585` | MERGEABLE/UNSTABLE | none | enforce-target failure | bug | needs-human/security + needs-author-rebase | wrong base plus stale app-server restart/process-termination boundary | +| 526 | ready | dev | codex/catalog-written-signal | `1ba588ef` | MERGEABLE/CLEAN | none | 10 CI jobs success + CodeRabbit success | bug | takeover-fix/rebase+tests | independent review found stale checks and missing direct filesystem write-path coverage | +| 512 | ready | dev | split/426-01-namespace-foundation | `aef5628f` | MERGEABLE/CLEAN | CHANGES_REQUESTED | CI success | enhancement | needs-human/security | account namespace/auth identity model; requested changes still active | +| 498 | draft | dev | agent/sync-native-subagent-defaults | `c2330797` | CONFLICTING/DIRTY | CHANGES_REQUESTED | label/target success only | enhancement | later/enhancement | draft, 55 files, config/default policy surface | +| 495 | draft | dev | agent/main-account-last-resort | `7e0351d0` | MERGEABLE/UNSTABLE | CHANGES_REQUESTED | label/target success only | enhancement | needs-human/security | account routing policy for main account; draft and requested changes | +| 493 | draft | dev | fix/anthropic-per-account-rate-limits | `5e466a79` | CONFLICTING/DIRTY | CHANGES_REQUESTED | mixed/cancelled historical checks | enhancement | needs-human/security | Claude OAuth quota/account policy surface; draft/conflicting | +| 491 | draft | dev | fix/oauth-login-preserves-api-key | `e0debe22` | CONFLICTING/DIRTY | CHANGES_REQUESTED | target/label success only | bug | needs-human/security | OAuth/API-key preservation touches credential storage | +| 461 | draft | dev | feat/ocx-opencode | `5ef84f48` | CONFLICTING/DIRTY | none | CI success on old head | enhancement | later/enhancement | new launcher/client support surface; draft and conflicting | +| 447 | draft | dev | fix/kiro-multiauth | `48adb2b6` | MERGEABLE/CLEAN | CHANGES_REQUESTED | CI success | bug | needs-human/security | browser-based multi-account login touches auth/credential boundary | +| 429 | draft | dev | fix/cursor-shell-alias-hint | `f408f348` | CONFLICTING/DIRTY | none | CI success on old head | bug | takeover-fix | small Cursor prompt/empty command bug but conflicting and draft | +| 424 | draft | dev | feat/image-bridge | `a8b769c9` | MERGEABLE/UNSTABLE | CHANGES_REQUESTED | target/label success only | enhancement | needs-human/security | image bridge introduces paid xAI calls/artifact/download security surface | +| 355 | draft | dev | feat/gemini-inline-image | `d3c876e6` | MERGEABLE/UNSTABLE | CHANGES_REQUESTED | target/label success only | enhancement | later/enhancement | competing image-output route; draft and requested changes | + +## Immediate PR read + +- `merge now`: none after independent audit. +- `takeover-fix/rebase+tests`: #526, because checks are stale and direct + write-path coverage is missing. +- `takeover-fix`: #429 only, because it is small and outside auth/security boundaries. +- `needs-author-rebase` as prerequisite: #527 and conflicting draft PRs #498, + #493, #491, #461, #429. +- `needs-human/security + request-changes`: #528. +- `needs-human/security`: #533, #527, #512, #495, #493, #491, #447, #424. +- `later/enhancement`: #498, #461, #355. diff --git a/devlog/_plan/260727_live_unfinished_triage/002_live_issue_manifest.md b/devlog/_plan/260727_live_unfinished_triage/002_live_issue_manifest.md new file mode 100644 index 0000000000..5e6b9a8f42 --- /dev/null +++ b/devlog/_plan/260727_live_unfinished_triage/002_live_issue_manifest.md @@ -0,0 +1,42 @@ +# 002 — live issue manifest + +Snapshot command: `gh issue list --repo lidge-jun/opencodex --state open --limit 200` +plus per-issue `gh issue view` comment tail. + +Open issue count: 23. Issue #546 is not listed because it is already closed: +`closedAt=2026-07-27T10:17:08Z`. + +| # | labels | last update | bucket | rationale | +| --- | --- | --- | --- | --- | +| 547 | bug | 2026-07-27T10:38:50Z | comment/request-changes | new Windows Claude Desktop custom-model visibility report; needs exact generated config/profile path, `ocx claude desktop` output, and Desktop dev-mode/API-key shape | +| 545 | bug, needs-info | 2026-07-27T10:22:15Z | takeover-fix/investigate | logging sub-bug fixed in `7fcaa9119`; remaining 64-token classifier behavior needs redacted request/frames or code isolation | +| 543 | bug, provider-compatibility, needs-info | 2026-07-27T08:34:24Z | comment/request-changes | reporter supplied native control; we can answer with existing `ocx debug claude` capture switch | +| 540 | enhancement, provider-compatibility, roadmap | 2026-07-27T08:09:26Z | later/enhancement | valid provider request but gated on official Automattic/OpenCodex auth contract | +| 521 | needs-info | 2026-07-27T01:15:52Z | comment/request-changes | reporter/update pending for web-search 499 reproduction details | +| 509 | needs-info | 2026-07-27T10:17:48Z | takeover-fix/investigate | JS heap memory warning gap is concrete enough for investigation but not merge/close | +| 476 | enhancement | 2026-07-27T05:08:51Z | needs-human/security | PR #526 signal half is safe to repair, but #527 restart/process-termination half crosses the process boundary | +| 462 | upstream-tracking, needs-info | 2026-07-26T21:40:30Z | upstream-tracking | session restore/model removal crash attributed upstream; keep tracking/needs-info | +| 425 | enhancement | 2026-07-26T02:31:52Z | later/enhancement | PR #512 foundation exists but account namespace policy needs decision | +| 418 | bug | 2026-07-26T21:44:52Z | takeover-fix/investigate | V2 custom-parent to custom-child delegation remains open with no linked PR | +| 417 | bug, upstream-tracking | 2026-07-24T18:31:11Z | upstream-tracking | Korean realtime transcript corruption tracked upstream; not an ocx relay fix yet | +| 415 | enhancement | 2026-07-24T11:29:04Z | later/enhancement | Gemini/search-capable provider sidecar follow-up from #398 | +| 414 | enhancement | 2026-07-24T11:28:50Z | later/enhancement | Exa/other search provider sidecar follow-up from #398 | +| 401 | enhancement, upstream-tracking | 2026-07-26T02:28:28Z | upstream-tracking | voice model route depends on realtime/Codex voice transport constraints | +| 386 | enhancement | 2026-07-24T12:27:26Z | later/enhancement | macOS menu bar companion remains release/packaging roadmap | +| 294 | enhancement, roadmap | 2026-07-27T00:41:58Z | later/enhancement | Claude account pool requires account-policy design; related PR #493 is draft/conflicting | +| 241 | bug, upstream-tracking | 2026-07-23T18:54:39Z | upstream-tracking | Desktop model picker limitation remains upstream-facing | +| 201 | enhancement, roadmap | 2026-07-22T11:40:05Z | later/enhancement | TRAE provider needs official auth/transport contract | +| 178 | enhancement, roadmap | 2026-07-22T11:40:06Z | later/enhancement | Factory is agent backend, not plain model API; needs product decision | +| 177 | enhancement, roadmap | 2026-07-22T11:40:08Z | later/enhancement | Warp/Oz agent API is not plain model API; needs product decision | +| 95 | enhancement, roadmap | 2026-07-22T11:40:11Z | later/enhancement | multi-user proxy/LiteLLM changes deployment model | +| 92 | bug, upstream-tracking | 2026-07-26T21:44:26Z | upstream-tracking | V2 encrypted_content/NEW_TASK body loss is upstream-facing and still open | +| 42 | enhancement, roadmap | 2026-07-27T02:40:56Z | later/enhancement | storage page roadmap has landed pieces but remaining restore/auto-policy phases | + +## Immediate issue read + +- `takeover-fix/investigate`: #545, #509, #418. +- `needs-human/security`: #476 for the process restart half. +- `comment/request-changes`: #547, #543, #521. +- `upstream-tracking`: #462, #417, #401, #241, #92. +- `later/enhancement`: #540, #425, #415, #414, #386, #294, #201, #178, #177, #95, #42. +- `close`: none from the current open list. diff --git a/devlog/_plan/260727_live_unfinished_triage/003_priority_table.md b/devlog/_plan/260727_live_unfinished_triage/003_priority_table.md new file mode 100644 index 0000000000..67809c8ba1 --- /dev/null +++ b/devlog/_plan/260727_live_unfinished_triage/003_priority_table.md @@ -0,0 +1,31 @@ +# 003 — priority table + +This table is live-state based, not copied from the older owner-decision ledger. + +| tier | item | bucket | why now | next PABCD action | +| --- | --- | --- | --- | --- | +| P0 | PR #526 | takeover-fix/rebase+tests | clean/green at old head, but independent review found stale checks and shallow direct write-path coverage | rebase/take over or request author update | +| P0 | PR #528 | needs-human/security + request-changes | credential-origin disclosure risk in image bridge, plus stale checks | request changes; no merge | +| P0 | issue #543 | comment/request-changes | reporter has already answered the control question; debug switch exists | comment with capture instructions | +| P0 | issue #547 | comment/request-changes | new Windows Claude Desktop custom-model visibility bug; likely config/profile evidence gap | comment with exact evidence request | +| P0 | issue #545 | takeover-fix/investigate | user-facing repeated classifier failures; one logging layer fixed but root still open | investigate request token path | +| P1 | issue #521 | comment/request-changes | still `needs-info`; web-search 499 needs reporter/update evidence before another fix claim | request exact reproduction delta if stale | +| P1 | issue #476 | needs-human/security | signal half can be handled by #526, but restart/process-termination half is a human/security boundary | split safe signal work from restart policy | +| P1 | PR #527 | needs-author-rebase | wrong base blocks CI policy and #476 closure | after #526, ask/retarget to `dev` | +| P1 | issue #418 | takeover-fix/investigate | real V2 delegation bug with no linked PR | inspect delegation path and repro | +| P1 | issue #509 | takeover-fix/investigate | concrete JS-heap memory growth gap | inspect watchdog heap/RSS logic | +| P2 | PR #429 | takeover-fix | small Cursor bug but draft/conflicting; user previously deprioritized Cursor-class work | rebase/fix only after P0/P1 | +| P2 | conflicting drafts #429/#461/#491/#493/#498 | dual bucket | conflicts are a prerequisite blocker; each retains its own primary bucket based on surface risk | rebase only inside that item’s PABCD | +| P2 | PR #533 | needs-human/security | install/update ownership boundary with requested changes | security review only | +| P2 | PR #447 | needs-human/security | auth/browser multi-account boundary | security review only | +| P2 | PR #491 | needs-human/security | OAuth/API-key preservation touches credential store | security review only | +| P3 | PR #512/#495/#493 | needs-human/security | account identity/quota policy must be unified | product/security design cycle | +| P3 | PR #424/#355 | later/enhancement / needs-human | competing image-generation routes; UX/backend choice absent | human decision before merge | +| P3 | PR #498/#461 | later/enhancement | draft/conflicting new policy/surface work | wait/replan | +| P3 | roadmap issues #42/#95/#177/#178/#201/#294/#386/#414/#415/#425/#540 | later/enhancement | not immediate bug cleanup | scheduled roadmap cycles | +| P3 | upstream issues #92/#241/#401/#417/#462 | upstream-tracking | external dependency or upstream attribution | keep tracking; do not close without upstream proof | + +## Rule for next phases + +Each item above gets its own full PABCD cycle before GitHub state changes. The only +exception is read-only live refresh inside the P phase of that same item. diff --git a/devlog/_plan/260727_live_unfinished_triage/010_pr526_catalog_write_signal.md b/devlog/_plan/260727_live_unfinished_triage/010_pr526_catalog_write_signal.md new file mode 100644 index 0000000000..cc193eb99e --- /dev/null +++ b/devlog/_plan/260727_live_unfinished_triage/010_pr526_catalog_write_signal.md @@ -0,0 +1,41 @@ +# 010 — PR #526 processing plan + +Item: PR #526, `fix(codex): report whether a sync actually wrote the catalog or cache`. + +Planned bucket: `takeover-fix/rebase+tests` after independent review. + +Audit update: + +- Sol review checked head `1ba588eff663a5be846a8723b90a452dca8cd04c`. +- GitHub reported `MERGEABLE/CLEAN` with passed checks, but the PR branch is far + behind current `dev`; the checks predate `origin/dev@7fcaa9119`. +- The review also found that `tests/codex-refresh.test.ts` mocks the new boolean + outcomes, while the real filesystem write/missing/malformed/unwritable paths in + `src/codex/catalog/sync.ts` still need direct regression coverage. + +Scope IN: + +- Re-read PR #526 diff against current `dev`. +- Confirm head `1ba588ef`, base `dev`, merge state `MERGEABLE/CLEAN`. +- Confirm CI/checks are success on the current head. +- Inspect changed paths: + - `src/codex/catalog/sync.ts` + - `src/codex/refresh.ts` + - `src/codex/sync.ts` + - `tests/codex-refresh.test.ts` + - `tests/codex-sync-api.test.ts` + - `tests/injection-model-api.test.ts` +- Rebase/refresh tests first; merge only after current-head checks and direct + write-path coverage are present. + +Scope OUT: + +- Do not merge PR #527 in the same work-phase. +- Do not add restart/process-kill behavior here. +- Do not close issue #476 until #527 is handled or the issue scope is narrowed. + +Verification: + +- `gh pr view 526 --json ...` +- independent Sol review verdict +- merge URL/commit SHA if merged diff --git a/devlog/_plan/260727_live_unfinished_triage/020_pr528_image_bridge_p2.md b/devlog/_plan/260727_live_unfinished_triage/020_pr528_image_bridge_p2.md new file mode 100644 index 0000000000..a74445ada5 --- /dev/null +++ b/devlog/_plan/260727_live_unfinished_triage/020_pr528_image_bridge_p2.md @@ -0,0 +1,36 @@ +# 020 — PR #528 processing plan + +Item: PR #528, `fix(images): Codex P2 follow-ups for image bridge (#424)`. + +Planned bucket: `needs-human/security + request-changes`. + +Audit update: + +- Sol review checked head `553e9afc6f16135d83d8ab2d3ab0cb309675b81b`. +- GitHub reported `MERGEABLE/CLEAN` with passed checks, but the tested merge ref used + an older base than current `origin/dev@7fcaa9119`. +- High blocker: `src/images/plan.ts` accepts credentials associated with + custom/overridden hosts, then pins the outbound request destination to `api.x.ai`. + That can disclose a custom proxy/API credential to another origin. The tests + currently reinforce this path instead of rejecting it. + +Scope IN: + +- Confirm head `553e9afc`, base `dev`, merge state `MERGEABLE/CLEAN`. +- Compare actual diff and decide whether it can be merged independently. +- Identify paid-provider, download/SSRF, artifact, and routing surfaces. +- Leave a request-changes comment requiring credential-origin binding, negative tests + for overridden `xai` and `cli-chat-proxy.grok.com`, rebase onto current `dev`, and + fresh checks. + +Scope OUT: + +- Do not accept a new image-generation default or paid xAI routing decision. +- Do not merge #424/#528 automatically. +- Do not resolve the #424 vs #355 product choice in this phase. + +Verification: + +- `gh pr view 528 --json ...` +- independent Sol review verdict +- GitHub comment URL if action is taken From ad8f8d2c75ba081d0b429bd1b5aeb82e3d9ab3cc Mon Sep 17 00:00:00 2001 From: bitkyc08-arch Date: Mon, 27 Jul 2026 19:46:54 +0900 Subject: [PATCH 04/10] docs(devlog): close live triage manifest workphase --- .../goalplan.json | 12 ++++++------ .../ledger.jsonl | 2 ++ 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/.codexclaw/goalplans/opencodex-live-unfinished-issues-and-prs-triage/goalplan.json b/.codexclaw/goalplans/opencodex-live-unfinished-issues-and-prs-triage/goalplan.json index 5ba46a4aac..eccac39038 100644 --- a/.codexclaw/goalplans/opencodex-live-unfinished-issues-and-prs-triage/goalplan.json +++ b/.codexclaw/goalplans/opencodex-live-unfinished-issues-and-prs-triage/goalplan.json @@ -2,13 +2,13 @@ "objective": "OpenCodex live unfinished issues and PRs triage against current GitHub state. Scope: repository lidge-jun/opencodex, dev target only, worktree /Users/jun/.codex/worktrees/260727-live-triage/opencodex. First work-phase is docs-only manifest/devlog: fetch live open PRs and open issues via gh, record current number/title/state/base/head/mergeable/checks/reviews/labels, classify every item into merge now, takeover-fix, comment/request-changes, needs-author-rebase, needs-human/security, later/enhancement, upstream-tracking, or close, and produce a priority table. Later work-phases process exactly one PR or one issue per full PABCD cycle. Allowed: directly fix bugfix/simple safe items on dev, create PRs, wait for CI, squash merge, and close linked issues when live evidence proves safety. Out of scope: main/preview/release branches, automatic merge of security/auth/permission/data-migration/privilege-boundary work, and unapproved GUI/UX decisions except the approved OpenRouter Free separate-provider direction. Terminal outcomes: DONE when all safe live items are processed and manifest evidence is current; NOOP when an item needs no action after live check; NEEDS_HUMAN or UNSAFE for risk-bound/security/UX-decision items; BLOCKED for external author/rebase/CI or upstream dependency; BUDGET_EXHAUSTED only if explicit runtime bounds are hit. Verification: gh live snapshots, code/diff review for candidate PRs, CI/check URLs where actions occur, comments/merge/close URLs for external state changes, and devlog evidence committed locally before completion.", "slug": "opencodex-live-unfinished-issues-and-prs-triage", "createdAt": "2026-07-27T10:35:02.724Z", - "updatedAt": "2026-07-27T10:43:20.000Z", - "activeWorkPhaseId": "WP0", + "updatedAt": "2026-07-27T10:46:14.883Z", + "activeWorkPhaseId": "WP1", "workPhases": [ { "id": "WP0", "title": "Live GitHub triage manifest and priority table", - "status": "pending", + "status": "done", "tasks": [ { "id": "WP0-T1", @@ -38,7 +38,7 @@ { "id": "WP1", "title": "PR #526 catalog write signal rebase and coverage decision", - "status": "pending", + "status": "in_progress", "tasks": [ { "id": "WP1-T1", @@ -201,8 +201,8 @@ "id": "C-WP0-LIVE-MANIFEST", "scenario": "Live GitHub state has been refreshed and every open PR/open issue is classified into the requested triage buckets.", "expectedEvidence": "devlog/_plan/260727_live_unfinished_triage/ numbered manifest files with gh snapshot timestamp, PR/issue lists, classification rationale, and priority table.", - "capturedEvidence": "", - "status": "open" + "capturedEvidence": "WP0 closed by cxc D at 2026-07-27T10:46:14Z. Commit 58b247ac records devlog/_plan/260727_live_unfinished_triage. C check: open PR count 14 with numbers 355,424,429,447,461,491,493,495,498,512,526,527,528,533; open issue count 23 with numbers 42,92,95,177,178,201,241,294,386,401,414,415,417,418,425,462,476,509,521,540,543,545,547; manifest has unsafe merge-now entries 0.", + "status": "met" }, { "id": "C-WP1-PR526", diff --git a/.codexclaw/goalplans/opencodex-live-unfinished-issues-and-prs-triage/ledger.jsonl b/.codexclaw/goalplans/opencodex-live-unfinished-issues-and-prs-triage/ledger.jsonl index 332c4d80f5..de803d9d75 100644 --- a/.codexclaw/goalplans/opencodex-live-unfinished-issues-and-prs-triage/ledger.jsonl +++ b/.codexclaw/goalplans/opencodex-live-unfinished-issues-and-prs-triage/ledger.jsonl @@ -1 +1,3 @@ {"ts":"2026-07-27T10:35:02.725Z","slug":"opencodex-live-unfinished-issues-and-prs-triage","event":"created","detail":"init objective=\"OpenCodex live unfinished issues and PRs triage against current GitHub state. Scope: repository lidge-jun/opencodex, dev target only, worktree /Users/jun/.codex/worktrees/260727-live-triage/opencodex. First work-phase is docs-only manifest/devlog: fetch live open PRs and open issues via gh, record current number/title/state/base/head/mergeable/checks/reviews/labels, classify every item into merge now, takeover-fix, comment/request-changes, needs-author-rebase, needs-human/security, later/enhancement, upstream-tracking, or close, and produce a priority table. Later work-phases process exactly one PR or one issue per full PABCD cycle. Allowed: directly fix bugfix/simple safe items on dev, create PRs, wait for CI, squash merge, and close linked issues when live evidence proves safety. Out of scope: main/preview/release branches, automatic merge of security/auth/permission/data-migration/privilege-boundary work, and unapproved GUI/UX decisions except the approved OpenRouter Free separate-provider direction. Terminal outcomes: DONE when all safe live items are processed and manifest evidence is current; NOOP when an item needs no action after live check; NEEDS_HUMAN or UNSAFE for risk-bound/security/UX-decision items; BLOCKED for external author/rebase/CI or upstream dependency; BUDGET_EXHAUSTED only if explicit runtime bounds are hit. Verification: gh live snapshots, code/diff review for candidate PRs, CI/check URLs where actions occur, comments/merge/close URLs for external state changes, and devlog evidence committed locally before completion.\" criteria=0"} +{"ts":"2026-07-27T10:46:14.884Z","slug":"opencodex-live-unfinished-issues-and-prs-triage","event":"workphase_done","detail":"closed WP0"} +{"ts":"2026-07-27T10:46:14.884Z","slug":"opencodex-live-unfinished-issues-and-prs-triage","event":"workphase_started","detail":"started WP1"} From 7d4d9fbf34ebad983196074ddd93019a1985acd3 Mon Sep 17 00:00:00 2001 From: bitkyc08-arch Date: Mon, 27 Jul 2026 20:02:02 +0900 Subject: [PATCH 05/10] docs(devlog): record pr526 ci blocker --- .../goalplan.json | 42 +++++++++++++++--- .../ledger.jsonl | 2 + .../010_pr526_catalog_write_signal.md | 38 +++++++++++++++- .../015_devlog_gitlink_ci_blocker.md | 43 +++++++++++++++++++ 4 files changed, 119 insertions(+), 6 deletions(-) create mode 100644 devlog/_plan/260727_live_unfinished_triage/015_devlog_gitlink_ci_blocker.md diff --git a/.codexclaw/goalplans/opencodex-live-unfinished-issues-and-prs-triage/goalplan.json b/.codexclaw/goalplans/opencodex-live-unfinished-issues-and-prs-triage/goalplan.json index eccac39038..f10c7bfacf 100644 --- a/.codexclaw/goalplans/opencodex-live-unfinished-issues-and-prs-triage/goalplan.json +++ b/.codexclaw/goalplans/opencodex-live-unfinished-issues-and-prs-triage/goalplan.json @@ -2,8 +2,8 @@ "objective": "OpenCodex live unfinished issues and PRs triage against current GitHub state. Scope: repository lidge-jun/opencodex, dev target only, worktree /Users/jun/.codex/worktrees/260727-live-triage/opencodex. First work-phase is docs-only manifest/devlog: fetch live open PRs and open issues via gh, record current number/title/state/base/head/mergeable/checks/reviews/labels, classify every item into merge now, takeover-fix, comment/request-changes, needs-author-rebase, needs-human/security, later/enhancement, upstream-tracking, or close, and produce a priority table. Later work-phases process exactly one PR or one issue per full PABCD cycle. Allowed: directly fix bugfix/simple safe items on dev, create PRs, wait for CI, squash merge, and close linked issues when live evidence proves safety. Out of scope: main/preview/release branches, automatic merge of security/auth/permission/data-migration/privilege-boundary work, and unapproved GUI/UX decisions except the approved OpenRouter Free separate-provider direction. Terminal outcomes: DONE when all safe live items are processed and manifest evidence is current; NOOP when an item needs no action after live check; NEEDS_HUMAN or UNSAFE for risk-bound/security/UX-decision items; BLOCKED for external author/rebase/CI or upstream dependency; BUDGET_EXHAUSTED only if explicit runtime bounds are hit. Verification: gh live snapshots, code/diff review for candidate PRs, CI/check URLs where actions occur, comments/merge/close URLs for external state changes, and devlog evidence committed locally before completion.", "slug": "opencodex-live-unfinished-issues-and-prs-triage", "createdAt": "2026-07-27T10:35:02.724Z", - "updatedAt": "2026-07-27T10:46:14.883Z", - "activeWorkPhaseId": "WP1", + "updatedAt": "2026-07-27T11:01:22.413Z", + "activeWorkPhaseId": "WP9", "workPhases": [ { "id": "WP0", @@ -38,23 +38,48 @@ { "id": "WP1", "title": "PR #526 catalog write signal rebase and coverage decision", - "status": "in_progress", + "status": "done", "tasks": [ { "id": "WP1-T1", "title": "Re-check PR #526 live head, checks, diff, and independent review", - "status": "pending" + "status": "done" }, { "id": "WP1-T2", "title": "Take over rebase/tests or leave a documented blocker for PR #526 only", - "status": "pending" + "status": "done" } ], "criteriaIds": [ "C-WP1-PR526" ] }, + { + "id": "WP9", + "title": "Dev baseline checkout blocker from devlog gitlinks", + "status": "in_progress", + "tasks": [ + { + "id": "WP9-T1", + "title": "Confirm current dev gitlink/.gitmodules mismatch and whether devlog chase checkouts are required tracked inputs", + "status": "pending" + }, + { + "id": "WP9-T2", + "title": "Remove or repair only the accidental devlog gitlinks on a dev-target branch", + "status": "pending" + }, + { + "id": "WP9-T3", + "title": "Verify checkout/submodule commands and open/merge the dev baseline CI fix before rerunning PR #526", + "status": "pending" + } + ], + "criteriaIds": [ + "C-WP9-DEVLOG-GITLINK" + ] + }, { "id": "WP2", "title": "PR #528 image bridge credential-origin request changes", @@ -208,6 +233,13 @@ "id": "C-WP1-PR526", "scenario": "PR #526 is either taken over with rebase/direct coverage or left open with a fresh blocker comment.", "expectedEvidence": "live PR head/checks, independent review verdict, test evidence or comment URL.", + "capturedEvidence": "WP1 closed by cxc D at 2026-07-27T11:01:22Z. Same-repo branch codex/catalog-written-signal was rebased onto origin/dev@7fcaa9119253d010393cb457427a2868cd935718 and pushed at 43d0efff4569711ed192e09d4d87b62fc803153c. Local pre-push passed typecheck, lint:gui, bun test 5051 pass/0 fail/24881 assertions, privacy scan, and React Doctor. Hosted Issue quality tests/test failed before tests during checkout with fatal missing .gitmodules mapping for devlog/_chase/_cca; origin/dev has the same gitlink mismatch. Merge held pending WP9 baseline checkout repair.", + "status": "met" + }, + { + "id": "C-WP9-DEVLOG-GITLINK", + "scenario": "The dev baseline checkout blocker caused by tracked devlog gitlinks without .gitmodules mappings is fixed or proven to require human direction.", + "expectedEvidence": "git tree/submodule evidence, commit/PR URL, local checkout/submodule verification, and hosted CI rerun evidence if pushed.", "capturedEvidence": "", "status": "open" }, diff --git a/.codexclaw/goalplans/opencodex-live-unfinished-issues-and-prs-triage/ledger.jsonl b/.codexclaw/goalplans/opencodex-live-unfinished-issues-and-prs-triage/ledger.jsonl index de803d9d75..eaebf7fe49 100644 --- a/.codexclaw/goalplans/opencodex-live-unfinished-issues-and-prs-triage/ledger.jsonl +++ b/.codexclaw/goalplans/opencodex-live-unfinished-issues-and-prs-triage/ledger.jsonl @@ -1,3 +1,5 @@ {"ts":"2026-07-27T10:35:02.725Z","slug":"opencodex-live-unfinished-issues-and-prs-triage","event":"created","detail":"init objective=\"OpenCodex live unfinished issues and PRs triage against current GitHub state. Scope: repository lidge-jun/opencodex, dev target only, worktree /Users/jun/.codex/worktrees/260727-live-triage/opencodex. First work-phase is docs-only manifest/devlog: fetch live open PRs and open issues via gh, record current number/title/state/base/head/mergeable/checks/reviews/labels, classify every item into merge now, takeover-fix, comment/request-changes, needs-author-rebase, needs-human/security, later/enhancement, upstream-tracking, or close, and produce a priority table. Later work-phases process exactly one PR or one issue per full PABCD cycle. Allowed: directly fix bugfix/simple safe items on dev, create PRs, wait for CI, squash merge, and close linked issues when live evidence proves safety. Out of scope: main/preview/release branches, automatic merge of security/auth/permission/data-migration/privilege-boundary work, and unapproved GUI/UX decisions except the approved OpenRouter Free separate-provider direction. Terminal outcomes: DONE when all safe live items are processed and manifest evidence is current; NOOP when an item needs no action after live check; NEEDS_HUMAN or UNSAFE for risk-bound/security/UX-decision items; BLOCKED for external author/rebase/CI or upstream dependency; BUDGET_EXHAUSTED only if explicit runtime bounds are hit. Verification: gh live snapshots, code/diff review for candidate PRs, CI/check URLs where actions occur, comments/merge/close URLs for external state changes, and devlog evidence committed locally before completion.\" criteria=0"} {"ts":"2026-07-27T10:46:14.884Z","slug":"opencodex-live-unfinished-issues-and-prs-triage","event":"workphase_done","detail":"closed WP0"} {"ts":"2026-07-27T10:46:14.884Z","slug":"opencodex-live-unfinished-issues-and-prs-triage","event":"workphase_started","detail":"started WP1"} +{"ts":"2026-07-27T11:01:22.413Z","slug":"opencodex-live-unfinished-issues-and-prs-triage","event":"workphase_done","detail":"closed WP1"} +{"ts":"2026-07-27T11:01:22.413Z","slug":"opencodex-live-unfinished-issues-and-prs-triage","event":"workphase_started","detail":"started WP2"} diff --git a/devlog/_plan/260727_live_unfinished_triage/010_pr526_catalog_write_signal.md b/devlog/_plan/260727_live_unfinished_triage/010_pr526_catalog_write_signal.md index cc193eb99e..17c58447c0 100644 --- a/devlog/_plan/260727_live_unfinished_triage/010_pr526_catalog_write_signal.md +++ b/devlog/_plan/260727_live_unfinished_triage/010_pr526_catalog_write_signal.md @@ -28,6 +28,19 @@ Scope IN: - Rebase/refresh tests first; merge only after current-head checks and direct write-path coverage are present. +Planned takeover delta: + +- Rebase same-repo branch `codex/catalog-written-signal` onto `origin/dev@7fcaa9119`. +- Add direct real-filesystem coverage in `tests/codex-refresh.test.ts` only: + - valid `opencodex-catalog.json` is rewritten by `syncCatalogModels()` and reports + `catalogWritten: true`; + - malformed default catalog is recovered from the runtime catalog and reports + `catalogWritten: true`; + - `invalidateCodexModelsCache()` writes the real `models_cache.json` wrapper shape; + - missing/malformed catalog and unwritable cache-file destination return + `cacheSynced`/cache write false without creating a false success. +- Keep production code unchanged unless the new tests reveal a real defect. + Scope OUT: - Do not merge PR #527 in the same work-phase. @@ -38,4 +51,27 @@ Verification: - `gh pr view 526 --json ...` - independent Sol review verdict -- merge URL/commit SHA if merged +- targeted test: + `bun test tests/codex-refresh.test.ts tests/codex-sync-api.test.ts tests/injection-model-api.test.ts` +- push/check/merge URL or blocker comment URL + +Execution update: + +- Rebased same-repo branch `codex/catalog-written-signal` onto + `origin/dev@7fcaa9119253d010393cb457427a2868cd935718`. +- Added direct real-filesystem coverage in `tests/codex-refresh.test.ts`. +- Pushed `43d0efff4569711ed192e09d4d87b62fc803153c` to + `origin/codex/catalog-written-signal`. +- Local pre-push passed: + - `bun run typecheck` + - `bun run lint:gui` + - `bun run test` — 5051 pass, 0 fail, 24881 assertions + - `bun scripts/privacy-scan.ts` + - `bun scripts/doctor-gui-if-changed.ts` +- Hosted blocker: GitHub `Issue quality tests / test` failed before tests during + checkout cleanup: + `fatal: No url found for submodule path 'devlog/_chase/_cca' in .gitmodules`. +- Live dev baseline has the same inconsistent gitlinks: + `origin/dev@7fcaa9119` tracks `devlog/_chase/_cca` and + `devlog/_chase/_litellm` as `160000` gitlinks while `.gitmodules` has no + mapping. Treat this as a separate dev baseline CI repair before merging #526. diff --git a/devlog/_plan/260727_live_unfinished_triage/015_devlog_gitlink_ci_blocker.md b/devlog/_plan/260727_live_unfinished_triage/015_devlog_gitlink_ci_blocker.md new file mode 100644 index 0000000000..9290446ff3 --- /dev/null +++ b/devlog/_plan/260727_live_unfinished_triage/015_devlog_gitlink_ci_blocker.md @@ -0,0 +1,43 @@ +# 015 — devlog gitlink checkout blocker + +Item: dev baseline CI blocker discovered while processing PR #526. + +Finding: + +- `origin/dev@7fcaa9119253d010393cb457427a2868cd935718` contains gitlinks at: + - `devlog/_chase/_cca` + - `devlog/_chase/_litellm` +- `.gitmodules` does not contain mappings for those paths. +- GitHub Actions checkout fails before the issue-quality script can run because + `actions/checkout` executes submodule cleanup and Git exits with: + `fatal: No url found for submodule path 'devlog/_chase/_cca' in .gitmodules`. +- This failure reproduced on PR #526 head + `43d0efff4569711ed192e09d4d87b62fc803153c`; recent `dev` runs are also red. + +Planned bucket: `takeover-fix`. + +Scope IN: + +- On current `dev`, remove accidental devlog `_chase` gitlinks from the tracked + tree or repair their metadata only if evidence shows they are intentionally + required build inputs. +- Prefer removal because `devlog/` is planning/audit material and the referenced + checkouts are not runtime source. +- Verify checkout/submodule state locally: + - `git ls-tree HEAD devlog/_chase/_cca devlog/_chase/_litellm .gitmodules` + - `git submodule status --recursive` +- Verify no source behavior changed: + - `git diff --check` + - targeted CI-facing check that previously failed locally, if available. + +Scope OUT: + +- Do not change main/preview/release branches. +- Do not rewrite unrelated devlog history. +- Do not merge #526 in the same work-phase; return to #526 after this blocker is + fixed and hosted checks are rerun. + +Expected outcome: + +- A dev-target PR or direct dev commit removes the checkout-time failure. +- PR #526 can be rerun/merged after this baseline fix lands. From 8e19c9c43a2a47308370edee68b4c56d8e943b77 Mon Sep 17 00:00:00 2001 From: bitkyc08-arch Date: Mon, 27 Jul 2026 20:47:18 +0900 Subject: [PATCH 06/10] docs(devlog): record dev baseline CI fixes --- .../goalplan.json | 80 +++++++++++++++++-- .../ledger.jsonl | 4 + .../000_plan.md | 60 ++++++++++++++ .../010_phase1.md | 64 +++++++++++++++ .../011_phase1_evidence.md | 69 ++++++++++++++++ .../000_plan.md | 42 ++++++++++ .../010_phase1.md | 43 ++++++++++ .../000_plan.md | 61 ++++++++++++++ .../010_phase1.md | 46 +++++++++++ 9 files changed, 461 insertions(+), 8 deletions(-) create mode 100644 devlog/_plan/260727_wp10-desktop3p-path-windows-ci/000_plan.md create mode 100644 devlog/_plan/260727_wp10-desktop3p-path-windows-ci/010_phase1.md create mode 100644 devlog/_plan/260727_wp10-desktop3p-path-windows-ci/011_phase1_evidence.md create mode 100644 devlog/_plan/260727_wp11-pr526-final-rerun-merge/000_plan.md create mode 100644 devlog/_plan/260727_wp11-pr526-final-rerun-merge/010_phase1.md create mode 100644 devlog/_plan/260727_wp9-devlog-gitlink-ci-fix/000_plan.md create mode 100644 devlog/_plan/260727_wp9-devlog-gitlink-ci-fix/010_phase1.md diff --git a/.codexclaw/goalplans/opencodex-live-unfinished-issues-and-prs-triage/goalplan.json b/.codexclaw/goalplans/opencodex-live-unfinished-issues-and-prs-triage/goalplan.json index f10c7bfacf..9909b78aae 100644 --- a/.codexclaw/goalplans/opencodex-live-unfinished-issues-and-prs-triage/goalplan.json +++ b/.codexclaw/goalplans/opencodex-live-unfinished-issues-and-prs-triage/goalplan.json @@ -2,8 +2,8 @@ "objective": "OpenCodex live unfinished issues and PRs triage against current GitHub state. Scope: repository lidge-jun/opencodex, dev target only, worktree /Users/jun/.codex/worktrees/260727-live-triage/opencodex. First work-phase is docs-only manifest/devlog: fetch live open PRs and open issues via gh, record current number/title/state/base/head/mergeable/checks/reviews/labels, classify every item into merge now, takeover-fix, comment/request-changes, needs-author-rebase, needs-human/security, later/enhancement, upstream-tracking, or close, and produce a priority table. Later work-phases process exactly one PR or one issue per full PABCD cycle. Allowed: directly fix bugfix/simple safe items on dev, create PRs, wait for CI, squash merge, and close linked issues when live evidence proves safety. Out of scope: main/preview/release branches, automatic merge of security/auth/permission/data-migration/privilege-boundary work, and unapproved GUI/UX decisions except the approved OpenRouter Free separate-provider direction. Terminal outcomes: DONE when all safe live items are processed and manifest evidence is current; NOOP when an item needs no action after live check; NEEDS_HUMAN or UNSAFE for risk-bound/security/UX-decision items; BLOCKED for external author/rebase/CI or upstream dependency; BUDGET_EXHAUSTED only if explicit runtime bounds are hit. Verification: gh live snapshots, code/diff review for candidate PRs, CI/check URLs where actions occur, comments/merge/close URLs for external state changes, and devlog evidence committed locally before completion.", "slug": "opencodex-live-unfinished-issues-and-prs-triage", "createdAt": "2026-07-27T10:35:02.724Z", - "updatedAt": "2026-07-27T11:01:22.413Z", - "activeWorkPhaseId": "WP9", + "updatedAt": "2026-07-27T11:46:27.213Z", + "activeWorkPhaseId": "WP11", "workPhases": [ { "id": "WP0", @@ -58,28 +58,53 @@ { "id": "WP9", "title": "Dev baseline checkout blocker from devlog gitlinks", - "status": "in_progress", + "status": "done", "tasks": [ { "id": "WP9-T1", "title": "Confirm current dev gitlink/.gitmodules mismatch and whether devlog chase checkouts are required tracked inputs", - "status": "pending" + "status": "done" }, { "id": "WP9-T2", "title": "Remove or repair only the accidental devlog gitlinks on a dev-target branch", - "status": "pending" + "status": "done" }, { "id": "WP9-T3", "title": "Verify checkout/submodule commands and open/merge the dev baseline CI fix before rerunning PR #526", - "status": "pending" + "status": "done" } ], "criteriaIds": [ "C-WP9-DEVLOG-GITLINK" ] }, + { + "id": "WP10", + "title": "Dev baseline Desktop 3P target-platform path fix", + "status": "done", + "tasks": [ + { + "id": "WP10-T1", + "title": "Confirm hosted Windows failure and local Desktop 3P path resolver cause", + "status": "done" + }, + { + "id": "WP10-T2", + "title": "Patch target-platform-specific path joining and regression tests on a dev-target branch", + "status": "done" + }, + { + "id": "WP10-T3", + "title": "Verify locally, push PR, wait for hosted checks, and squash merge if green", + "status": "done" + } + ], + "criteriaIds": [ + "C-WP10-DESKTOP3P-PATH" + ] + }, { "id": "WP2", "title": "PR #528 image bridge credential-origin request changes", @@ -100,6 +125,31 @@ "C-WP2-PR528" ] }, + { + "id": "WP11", + "title": "PR #526 final rerun and merge after dev baseline fixes", + "status": "in_progress", + "tasks": [ + { + "id": "WP11-T1", + "title": "Rebase PR #526 branch onto current origin/dev after WP9/WP10", + "status": "pending" + }, + { + "id": "WP11-T2", + "title": "Run local targeted verification and push the rebased PR head", + "status": "pending" + }, + { + "id": "WP11-T3", + "title": "Wait for hosted checks and squash merge PR #526 if the latest head is clean", + "status": "pending" + } + ], + "criteriaIds": [ + "C-WP11-PR526-FINAL" + ] + }, { "id": "WP3", "title": "Issue #543 Claude queued_command support reply", @@ -240,8 +290,15 @@ "id": "C-WP9-DEVLOG-GITLINK", "scenario": "The dev baseline checkout blocker caused by tracked devlog gitlinks without .gitmodules mappings is fixed or proven to require human direction.", "expectedEvidence": "git tree/submodule evidence, commit/PR URL, local checkout/submodule verification, and hosted CI rerun evidence if pushed.", - "capturedEvidence": "", - "status": "open" + "capturedEvidence": "WP9 closed by cxc D at 2026-07-27T11:11:58Z. Branch codex/devlog-gitlink-ci-fix removed exactly three accidental 160000 gitlinks without .gitmodules mappings: devlog/_chase/_cca, devlog/_chase/_litellm, and devlog/_fin/opencode-cursor. Local verification: git ls-files -s found no 160000 entries; git submodule status --recursive exit 0; git diff --check origin/dev exit 0. PR #550 https://github.com/lidge-jun/opencodex/pull/550 passed hosted checks and was squash-merged into origin/dev at ff831858388179d3f76f4dd7c119d84470214fa6.", + "status": "met" + }, + { + "id": "C-WP10-DESKTOP3P-PATH", + "scenario": "The dev baseline Desktop 3P path resolver returns target-platform separators across hosted OSes and unblocks PR #526 CI.", + "expectedEvidence": "code pointers, targeted Bun test output, TypeScript output, diff-check output, PR URL, hosted check evidence, and merge SHA if green.", + "capturedEvidence": "WP10 branch codex/desktop3p-path-windows-ci commit f6d2881dd422830eece502e0ba8de493205fe9d1 patched src/claude/desktop-3p-paths.ts to use target-platform posix/win32 joins and updated tests/desktop-3p.test.ts plus tests/claude-desktop-config-path.test.ts. Local verification: bun test tests/desktop-3p.test.ts tests/claude-desktop-config-path.test.ts = 30 pass/0 fail; bun x tsc --noEmit exit 0; git diff --check origin/dev exit 0. Prepush full gate: 5047 pass/0 fail, privacy scan passed, GUI doctor skipped. Independent C review Aquinas PASS. PR #552 https://github.com/lidge-jun/opencodex/pull/552 passed hosted CodeRabbit, enforce-target, label, react-doctor, ubuntu-latest, macos-latest, windows-latest, and all npm-global matrix jobs; squash-merged at 2026-07-27T11:43:58Z as origin/dev 7c74e0a22ec96dd5849d3d7253758f0ab15d9737. Remote topic branch deleted.", + "status": "met" }, { "id": "C-WP2-PR528", @@ -250,6 +307,13 @@ "capturedEvidence": "", "status": "open" }, + { + "id": "C-WP11-PR526-FINAL", + "scenario": "PR #526 is rebased after dev baseline blockers, verified on the latest head, and merged if clean.", + "expectedEvidence": "rebase head SHA, local test/typecheck/diff-check output, pushed head SHA, hosted check list, merge commit URL/SHA or blocker evidence.", + "capturedEvidence": "", + "status": "open" + }, { "id": "C-WP3-ISSUE543", "scenario": "Issue #543 receives a concrete support reply naming the existing debug capture switch and requested evidence.", diff --git a/.codexclaw/goalplans/opencodex-live-unfinished-issues-and-prs-triage/ledger.jsonl b/.codexclaw/goalplans/opencodex-live-unfinished-issues-and-prs-triage/ledger.jsonl index eaebf7fe49..6b3b4dffa3 100644 --- a/.codexclaw/goalplans/opencodex-live-unfinished-issues-and-prs-triage/ledger.jsonl +++ b/.codexclaw/goalplans/opencodex-live-unfinished-issues-and-prs-triage/ledger.jsonl @@ -3,3 +3,7 @@ {"ts":"2026-07-27T10:46:14.884Z","slug":"opencodex-live-unfinished-issues-and-prs-triage","event":"workphase_started","detail":"started WP1"} {"ts":"2026-07-27T11:01:22.413Z","slug":"opencodex-live-unfinished-issues-and-prs-triage","event":"workphase_done","detail":"closed WP1"} {"ts":"2026-07-27T11:01:22.413Z","slug":"opencodex-live-unfinished-issues-and-prs-triage","event":"workphase_started","detail":"started WP2"} +{"ts":"2026-07-27T11:11:58.894Z","slug":"opencodex-live-unfinished-issues-and-prs-triage","event":"workphase_done","detail":"closed WP9"} +{"ts":"2026-07-27T11:11:58.894Z","slug":"opencodex-live-unfinished-issues-and-prs-triage","event":"workphase_started","detail":"started WP2"} +{"ts":"2026-07-27T11:46:27.214Z","slug":"opencodex-live-unfinished-issues-and-prs-triage","event":"workphase_done","detail":"closed WP10"} +{"ts":"2026-07-27T11:46:27.214Z","slug":"opencodex-live-unfinished-issues-and-prs-triage","event":"workphase_started","detail":"started WP2"} diff --git a/devlog/_plan/260727_wp10-desktop3p-path-windows-ci/000_plan.md b/devlog/_plan/260727_wp10-desktop3p-path-windows-ci/000_plan.md new file mode 100644 index 0000000000..e1e8afa1c8 --- /dev/null +++ b/devlog/_plan/260727_wp10-desktop3p-path-windows-ci/000_plan.md @@ -0,0 +1,60 @@ +# 000 — wp10-desktop3p-path-windows-ci: Plan + +> DIFFLEVEL-ROADMAP-01: write this doc to full diff-level precision (exact paths, +> NEW/MODIFY/DELETE, before/after diffs) BEFORE P -> A. An empty scaffold does not +> satisfy the rule; the A-phase reviewer FAILS outline-only phase docs. + +## Objective + +Fix the current `origin/dev` Windows CI regression that blocks safe rerun/merge +of PR #526. Hosted CI for PR #526 head +`64624712aaf5fd1ef5a18167bee373c5fed63457` failed only on `windows-latest` in +`Claude Desktop 3P models > resolves the actual cross-platform Claude Desktop +config library (#539)`: expected `/profiles/claude/configLibrary`, received +`\profiles\claude\configLibrary`. + +Evidence base: + +- PR #526 diff is catalog sync/write-signal only and does not touch Desktop 3P + path logic. +- `origin/dev@ff831858388179d3f76f4dd7c119d84470214fa6` contains + `src/claude/desktop-3p-paths.ts`, whose resolver imports host + `node:path.join`. On a Windows host, that converts POSIX profile override + inputs used for `platform: "darwin"` / `platform: "linux"` test cases into + backslash paths. +- `tests/desktop-3p.test.ts` expects target-platform behavior for non-Windows + platforms, but the implementation currently follows the host OS separator. + +## Loop-spec + +- Loop archetype: spec-satisfaction repair. The verifier is the targeted Bun + test suite plus hosted GitHub Actions on a fresh PR. +- Write scope: `src/claude/desktop-3p-paths.ts`, + `tests/desktop-3p.test.ts`, and `tests/claude-desktop-config-path.test.ts` + only. Devlog/goalplan updates are recorded in the live-triage worktree. +- Out-of-scope: provider behavior, auth/security, GUI/UX, main/preview/release + branches, and any changes to PR #526's catalog-write semantics. +- Budget / bounds: one focused implementation attempt, one reviewer audit + round unless the reviewer finds a high blocker, local targeted tests + + `bun x tsc --noEmit`, then push a dev-target PR and wait for hosted CI. + +## Work-phase map (one phase = one full PABCD cycle) + +| WP | Doc | Slice | Depends on | +|----|-----|-------|------------| +| WP10 | `010_phase1.md` | Make Desktop 3P config-library path resolution target-platform-specific instead of host-OS-specific, and lock the CI failure with regression tests. | WP9 gitlink checkout fix merged to `origin/dev@ff831858` | + +## Accept criteria + +- `resolveClaudeDesktop3PConfigLibraryDir` trims explicit + `OPENCODEX_CLAUDE_DESKTOP_CONFIG_DIR` surrounding whitespace, then preserves + the override without joining or target-platform normalization. +- With `CLAUDE_USER_DATA_DIR: "/profiles/claude"` and `platform: "darwin"` or + `"linux"`, the resolver returns `/profiles/claude/configLibrary` even when + executed on Windows. +- With `platform: "win32"` and Windows environment paths, the resolver returns + Windows-style paths. +- Local checks pass: + `bun test tests/desktop-3p.test.ts tests/claude-desktop-config-path.test.ts`, + `bun x tsc --noEmit`, and `git diff --check origin/dev`. +- A dev-target PR is pushed and the latest hosted checks are green before merge. diff --git a/devlog/_plan/260727_wp10-desktop3p-path-windows-ci/010_phase1.md b/devlog/_plan/260727_wp10-desktop3p-path-windows-ci/010_phase1.md new file mode 100644 index 0000000000..e2388f9370 --- /dev/null +++ b/devlog/_plan/260727_wp10-desktop3p-path-windows-ci/010_phase1.md @@ -0,0 +1,64 @@ +# 010 — Phase 1 (wp10-desktop3p-path-windows-ci) + +> DIFFLEVEL-ROADMAP-01: write this doc to full diff-level precision (exact paths, +> NEW/MODIFY/DELETE, before/after diffs) BEFORE P -> A. An empty scaffold does not +> satisfy the rule; the A-phase reviewer FAILS outline-only phase docs. + +## MODIFY / NEW / DELETE map + +MODIFY `src/claude/desktop-3p-paths.ts` + +- Replace host-OS `join` import: + - before: `import { join } from "node:path";` + - after: `import { posix, win32 } from "node:path";` +- Add one local helper, next to the existing platform helper: + - `joinForPlatform(platform, ...parts)` returns `win32.join(...)` when + `platform === "win32"`, otherwise `posix.join(...)`. +- Change `resolveElectronUserData(inputs)`: + - darwin branch keeps `/Users/test/Library/Application Support/...` semantics + with `posix.join`. + - win32 branch keeps `C:\...\AppData\Local\...` semantics with `win32.join`. + - linux branch keeps `/home/test/.config/...` semantics with `posix.join`. +- Change `resolveConfigLibraryDir(inputs)`: +- keep `OPENCODEX_CLAUDE_DESKTOP_CONFIG_DIR` semantics unchanged: trim + surrounding whitespace, then return the override without joining or + target-platform normalization. + - append `configLibrary` with `joinForPlatform(inputs.platform, userDataDir, + "configLibrary")` so the separator follows the target platform, not the + CI host. + +MODIFY `tests/desktop-3p.test.ts` + +- Import `{ posix, win32 }` from `node:path`. +- Replace host `join(...)` assertions with target-platform assertions: + - POSIX expected paths use `posix.join(...)` or exact POSIX strings. + - Windows expected paths use `win32.join(...)`. +- Keep the failing CI scenario covered: + `platform: "darwin"` + `CLAUDE_USER_DATA_DIR: "/profiles/claude"` must return + `/profiles/claude/configLibrary`. + +MODIFY `tests/claude-desktop-config-path.test.ts` + +- Import `{ posix, win32 }` from `node:path`. +- Replace host `join(...)` expectations with target-platform expectations for + generated profile roots. +- Preserve tests that explicit overrides are returned verbatim. + +## TESTS + +- `tests/desktop-3p.test.ts` + - existing Desktop 3P resolver table continues to cover override, Darwin, + Windows, Linux XDG, and Linux HOME branches. + - Windows-host regression is protected by asserting POSIX separator for + non-Windows target platforms. +- `tests/claude-desktop-config-path.test.ts` + - existing runtime wrapper tests continue to verify each platform's default + root shape with deterministic environment/home inputs. + +## Verification (C) + +- `bun test tests/desktop-3p.test.ts tests/claude-desktop-config-path.test.ts` + exits 0. +- `bun x tsc --noEmit` exits 0. +- `git diff --check origin/dev` exits 0. +- Hosted GitHub checks on the WP10 PR latest head are green before squash merge. diff --git a/devlog/_plan/260727_wp10-desktop3p-path-windows-ci/011_phase1_evidence.md b/devlog/_plan/260727_wp10-desktop3p-path-windows-ci/011_phase1_evidence.md new file mode 100644 index 0000000000..57f2434fdd --- /dev/null +++ b/devlog/_plan/260727_wp10-desktop3p-path-windows-ci/011_phase1_evidence.md @@ -0,0 +1,69 @@ +# 011 — WP10 Phase 1 evidence + +## Local branch + +- Worktree: `/Users/jun/.codex/worktrees/260727-desktop3p-path/opencodex` +- Branch: `codex/desktop3p-path-windows-ci` +- Base: `origin/dev@ff831858388179d3f76f4dd7c119d84470214fa6` +- Commit: `f6d2881dd422830eece502e0ba8de493205fe9d1` +- PR: https://github.com/lidge-jun/opencodex/pull/552 + +## Code delta + +- `src/claude/desktop-3p-paths.ts` + - Replaced host `node:path.join` use in Desktop 3P resolver with target-platform + `posix` / `win32` joins. + - Preserved `OPENCODEX_CLAUDE_DESKTOP_CONFIG_DIR` trim-then-verbatim override. +- `tests/desktop-3p.test.ts` + - Corrected public entry-point expectations so Darwin/Linux use POSIX and + Windows uses `win32.join`. +- `tests/claude-desktop-config-path.test.ts` + - Corrected pure resolver expectations by target platform while keeping host + `join` for temp-file fixture paths. + +## Local verification + +- `bun test tests/desktop-3p.test.ts tests/claude-desktop-config-path.test.ts` + - Result: 30 pass, 0 fail, 84 assertions. +- `bun x tsc --noEmit` + - Result: exit 0. +- `git diff --check origin/dev` + - Result: exit 0. +- Pre-push hook for remote branch creation: + - `bun run typecheck` + - `bun run lint:gui` + - `bun run test` + - `bun run privacy:scan` + - `bun run doctor:gui:if-changed` + - Result: 5047 pass, 0 fail, 24858 assertions; privacy scan passed; GUI + doctor skipped because no `gui/` files changed. + +## Independent review + +- A-gate reviewer Huygens: `GO-WITH-FIXES (blockers=1)`. + - Blocker: plan named wrong override env var. Folded into `000_plan.md` and + `010_phase1.md` before B. +- C-gate reviewer Aquinas: `PASS`. + - Confirmed override semantics preserved, target-platform joins are consistent, + host temp-file joins remain host joins, and test assertions were corrected + rather than weakened. + +## Hosted verification + +- PR #552 created at 2026-07-27T11:35Z. +- Latest head: `f6d2881dd422830eece502e0ba8de493205fe9d1`. +- Hosted checks: + - CodeRabbit: success. + - Enforce PR target branch: success. + - PR Labeler: success. + - React Doctor: success. + - Cross-platform CI: + - `ubuntu-latest`: success. + - `macos-latest`: success. + - `windows-latest`: success. + - `npm-global ubuntu-latest`: success. + - `npm-global macos-latest`: success. + - `npm-global windows-latest`: success. +- PR #552 was squash-merged at 2026-07-27T11:43:58Z. +- Merge commit on `origin/dev`: `7c74e0a22ec96dd5849d3d7253758f0ab15d9737`. +- Remote branch `codex/desktop3p-path-windows-ci` was deleted after merge. diff --git a/devlog/_plan/260727_wp11-pr526-final-rerun-merge/000_plan.md b/devlog/_plan/260727_wp11-pr526-final-rerun-merge/000_plan.md new file mode 100644 index 0000000000..431ca2f3ee --- /dev/null +++ b/devlog/_plan/260727_wp11-pr526-final-rerun-merge/000_plan.md @@ -0,0 +1,42 @@ +# 000 — WP11 PR #526 final rerun and merge plan + +## Objective + +Finish PR #526 after its two dev-baseline blockers were repaired: + +- WP9 merged PR #550 to remove broken devlog gitlinks that prevented checkout. +- WP10 merged PR #552 to fix the unrelated Windows Desktop 3P path regression + that made the rebased PR #526 hosted `windows-latest` job red. + +This work-phase targets PR #526 only. It must rebase the existing +`codex/catalog-written-signal` branch onto the current `origin/dev`, push the +current head, wait for the latest hosted checks on PR #526, and squash merge only +if the latest head is clean. + +## Loop-spec + +- Loop archetype: spec-satisfaction repair; verifier is PR #526 latest hosted + checks plus already-audited local targeted tests from WP1. +- Write scope: existing PR #526 branch `codex/catalog-written-signal`; no new + production code unless the rebase exposes a conflict or direct regression. +- Out-of-scope: PR #527 process restart behavior, PR #528 image bridge, + security/auth/permission/data migration, main/preview/release branches. +- Remote branch handling: do not delete `codex/catalog-written-signal` on merge + because PR #527 may depend on it. + +## Work-phase map + +| WP | Doc | Slice | Depends on | +|----|-----|-------|------------| +| WP11 | `010_phase1.md` | Rebase PR #526 onto current dev, rerun local smoke, push, wait hosted checks, squash merge if green. | WP9 + WP10 merged to `origin/dev` | + +## Accept criteria + +- PR #526 branch is rebased onto current `origin/dev@7c74e0a22ec96dd5849d3d7253758f0ab15d9737` or newer current dev. +- Local targeted verification still passes after rebase: + `bun test tests/codex-refresh.test.ts tests/codex-sync-api.test.ts tests/injection-model-api.test.ts`, + `bun x tsc --noEmit`, and `git diff --check origin/dev`. +- PR #526 latest hosted checks all pass on the latest head after push. +- PR #526 is squash-merged to `dev` only if latest head/checks remain clean at + merge time. +- The remote `codex/catalog-written-signal` branch is preserved. diff --git a/devlog/_plan/260727_wp11-pr526-final-rerun-merge/010_phase1.md b/devlog/_plan/260727_wp11-pr526-final-rerun-merge/010_phase1.md new file mode 100644 index 0000000000..0ce1e38e7b --- /dev/null +++ b/devlog/_plan/260727_wp11-pr526-final-rerun-merge/010_phase1.md @@ -0,0 +1,43 @@ +# 010 — Phase 1: PR #526 final rerun and merge + +## MODIFY / NEW / DELETE map + +Expected code delta: no new source changes beyond rebasing existing PR #526 commits. + +Existing PR #526 commits to preserve: + +- `fix(codex): report whether a sync actually wrote the catalog or cache` +- `test(codex): cover real catalog sync write signals` + +Branch operation: + +- In `/Users/jun/.codex/worktrees/260727-pr526/opencodex`, fetch `origin/dev`. +- Rebase `codex/catalog-written-signal` onto current `origin/dev`. +- If conflict-free, run local verification. +- Push with `--force-with-lease` only after local verification. +- Wait for latest hosted checks on PR #526. +- Squash merge PR #526 with branch deletion disabled if and only if all latest + checks are green and merge state is clean. + +## TESTS + +- `tests/codex-refresh.test.ts` +- `tests/codex-sync-api.test.ts` +- `tests/injection-model-api.test.ts` + +These are the PR #526 affected tests from WP1 and cover: + +- real filesystem catalog write signal; +- cache invalidation success/failure signals; +- sync API and injection-model API compatibility around the new booleans. + +## Verification (C) + +- `bun test tests/codex-refresh.test.ts tests/codex-sync-api.test.ts tests/injection-model-api.test.ts` + exits 0. +- `bun x tsc --noEmit` exits 0. +- `git diff --check origin/dev` exits 0. +- `gh pr view 526 --json headRefOid,baseRefOid,mergeStateStatus,statusCheckRollup` + shows the pushed head and no failed/pending checks before merge. +- After merge, `gh pr view 526 --json state,mergedAt,mergeCommit` shows merged + commit on `dev`. diff --git a/devlog/_plan/260727_wp9-devlog-gitlink-ci-fix/000_plan.md b/devlog/_plan/260727_wp9-devlog-gitlink-ci-fix/000_plan.md new file mode 100644 index 0000000000..9bde2a514b --- /dev/null +++ b/devlog/_plan/260727_wp9-devlog-gitlink-ci-fix/000_plan.md @@ -0,0 +1,61 @@ +# 000 — wp9-devlog-gitlink-ci-fix: Plan + +> DIFFLEVEL-ROADMAP-01: write this doc to full diff-level precision (exact paths, +> NEW/MODIFY/DELETE, before/after diffs) BEFORE P -> A. An empty scaffold does not +> satisfy the rule; the A-phase reviewer FAILS outline-only phase docs. + +## Objective + +Fix the dev baseline checkout failure discovered while processing PR #526. + +Observed failure: + +- PR #526 head `43d0efff4569711ed192e09d4d87b62fc803153c` failed + `Issue quality tests / test` before tests during GitHub Actions checkout. +- Failure text: + `fatal: No url found for submodule path 'devlog/_chase/_cca' in .gitmodules`. +- Current `origin/dev@7fcaa9119253d010393cb457427a2868cd935718` has the same + broken metadata: + - `devlog/_chase/_cca` is a `160000` gitlink. + - `devlog/_chase/_litellm` is a `160000` gitlink. + - `devlog/_fin/opencode-cursor` is also a `160000` gitlink and appears after + the first two are removed. + - `.gitmodules` is absent/no mapping for those paths. +- The gitlinks were introduced by docs-only commit + `43fd06bc3 docs(devlog): close the docs-only roadmap cycle for the governance intake`. + +Outcome: + +- Remove only the accidental devlog `_chase` gitlinks from a dev-target branch. +- Restore checkout/submodule commands to non-erroring state. +- Land this baseline CI repair before rerunning/merging PR #526. + +## Loop-spec + +- Loop archetype: verifier-defined. +- Write scope: + - DELETE gitlink `devlog/_chase/_cca`. + - DELETE gitlink `devlog/_chase/_litellm`. + - DELETE gitlink `devlog/_fin/opencode-cursor`. +- Out of scope: + - No runtime source changes. + - No `main`, `preview`, or release-branch changes. + - No rewrite of historical devlog content under `devlog/_chase/_model`. + - No #526 merge in this work-phase. +- Budget / bounds: + - One small branch from `origin/dev`. + - Push and open/merge a dev-target PR if local verification passes. + +## Work-phase map (one phase = one full PABCD cycle) + +| WP | Doc | Slice | Depends on | +|----|-----|-------|------------| +| WP9 | `010_phase1.md` | remove broken devlog gitlinks and verify checkout/submodule state | PR #526 hosted checkout failure | + +## Accept criteria + +- `git ls-tree HEAD devlog/_chase/_cca devlog/_chase/_litellm devlog/_fin/opencode-cursor` + prints no `160000` entries after the fix. +- `git submodule status --recursive` exits 0. +- `git diff --check origin/dev` exits 0. +- A dev-target PR/merge or documented blocker exists for the baseline repair. diff --git a/devlog/_plan/260727_wp9-devlog-gitlink-ci-fix/010_phase1.md b/devlog/_plan/260727_wp9-devlog-gitlink-ci-fix/010_phase1.md new file mode 100644 index 0000000000..7459cd795c --- /dev/null +++ b/devlog/_plan/260727_wp9-devlog-gitlink-ci-fix/010_phase1.md @@ -0,0 +1,46 @@ +# 010 — Phase 1 (wp9-devlog-gitlink-ci-fix) + +> DIFFLEVEL-ROADMAP-01: write this doc to full diff-level precision (exact paths, +> NEW/MODIFY/DELETE, before/after diffs) BEFORE P -> A. An empty scaffold does not +> satisfy the rule; the A-phase reviewer FAILS outline-only phase docs. + +## MODIFY / NEW / DELETE map + +DELETE gitlink: + +- `devlog/_chase/_cca` + - before: `160000 commit 00114bec1b76d985fd33a8a19f91c22ffed88580` + - after: path absent from tracked tree +- `devlog/_chase/_litellm` + - before: `160000 commit be4d0d8439ad6bea5b7a310824c74f2df0c73884` + - after: path absent from tracked tree +- `devlog/_fin/opencode-cursor` + - before: `160000 commit 6ab2c913e71b21cb660f7692e05c3c458a6c67f1` + - after: path absent from tracked tree + +No `.gitmodules` file should be added because these devlog chase checkouts are +not runtime/build inputs and were tracked by docs/devlog commits without +submodule metadata. + +No source files should change. + +## TESTS + +No code tests are required because this is repository metadata cleanup only. + +Metadata checks: + +- Confirm pre-fix mismatch: + `git ls-tree -r origin/dev | awk '$1 == "160000" {print $3, $4}'`. +- Confirm post-fix removal: + `git ls-files -s | awk '$1 == "160000" {print $2, $4}'`. +- Confirm submodule enumeration no longer fails: + `git submodule status --recursive`. + +## Verification (C) + +- `git diff --check origin/dev` — exit 0. +- `git submodule status --recursive` — exit 0. +- `git ls-files -s | awk '$1 == "160000" {print $2, $4}'` — no output. +- `gh pr checks --watch` or, if direct merge is blocked, record hosted + checkout status URL. From 3f8d0bce37135d94ee0a0d79895c558d42e86ec2 Mon Sep 17 00:00:00 2001 From: bitkyc08-arch Date: Mon, 27 Jul 2026 21:09:40 +0900 Subject: [PATCH 07/10] docs(devlog): record pr526 final merge evidence --- .../goalplan.json | 18 ++--- .../ledger.jsonl | 11 ++++ .../000_plan.md | 16 ++++- .../010_phase1.md | 28 +++++++- .../011_phase1_evidence.md | 65 +++++++++++++++++++ 5 files changed, 123 insertions(+), 15 deletions(-) create mode 100644 devlog/_plan/260727_wp11-pr526-final-rerun-merge/011_phase1_evidence.md diff --git a/.codexclaw/goalplans/opencodex-live-unfinished-issues-and-prs-triage/goalplan.json b/.codexclaw/goalplans/opencodex-live-unfinished-issues-and-prs-triage/goalplan.json index 9909b78aae..d04df7993d 100644 --- a/.codexclaw/goalplans/opencodex-live-unfinished-issues-and-prs-triage/goalplan.json +++ b/.codexclaw/goalplans/opencodex-live-unfinished-issues-and-prs-triage/goalplan.json @@ -2,8 +2,8 @@ "objective": "OpenCodex live unfinished issues and PRs triage against current GitHub state. Scope: repository lidge-jun/opencodex, dev target only, worktree /Users/jun/.codex/worktrees/260727-live-triage/opencodex. First work-phase is docs-only manifest/devlog: fetch live open PRs and open issues via gh, record current number/title/state/base/head/mergeable/checks/reviews/labels, classify every item into merge now, takeover-fix, comment/request-changes, needs-author-rebase, needs-human/security, later/enhancement, upstream-tracking, or close, and produce a priority table. Later work-phases process exactly one PR or one issue per full PABCD cycle. Allowed: directly fix bugfix/simple safe items on dev, create PRs, wait for CI, squash merge, and close linked issues when live evidence proves safety. Out of scope: main/preview/release branches, automatic merge of security/auth/permission/data-migration/privilege-boundary work, and unapproved GUI/UX decisions except the approved OpenRouter Free separate-provider direction. Terminal outcomes: DONE when all safe live items are processed and manifest evidence is current; NOOP when an item needs no action after live check; NEEDS_HUMAN or UNSAFE for risk-bound/security/UX-decision items; BLOCKED for external author/rebase/CI or upstream dependency; BUDGET_EXHAUSTED only if explicit runtime bounds are hit. Verification: gh live snapshots, code/diff review for candidate PRs, CI/check URLs where actions occur, comments/merge/close URLs for external state changes, and devlog evidence committed locally before completion.", "slug": "opencodex-live-unfinished-issues-and-prs-triage", "createdAt": "2026-07-27T10:35:02.724Z", - "updatedAt": "2026-07-27T11:46:27.213Z", - "activeWorkPhaseId": "WP11", + "updatedAt": "2026-07-27T12:08:50.093Z", + "activeWorkPhaseId": "WP6", "workPhases": [ { "id": "WP0", @@ -128,22 +128,22 @@ { "id": "WP11", "title": "PR #526 final rerun and merge after dev baseline fixes", - "status": "in_progress", + "status": "done", "tasks": [ { "id": "WP11-T1", "title": "Rebase PR #526 branch onto current origin/dev after WP9/WP10", - "status": "pending" + "status": "done" }, { "id": "WP11-T2", "title": "Run local targeted verification and push the rebased PR head", - "status": "pending" + "status": "done" }, { "id": "WP11-T3", "title": "Wait for hosted checks and squash merge PR #526 if the latest head is clean", - "status": "pending" + "status": "done" } ], "criteriaIds": [ @@ -213,7 +213,7 @@ { "id": "WP6", "title": "PR #527 wrong-base handling", - "status": "pending", + "status": "in_progress", "tasks": [ { "id": "WP6-T1", @@ -311,8 +311,8 @@ "id": "C-WP11-PR526-FINAL", "scenario": "PR #526 is rebased after dev baseline blockers, verified on the latest head, and merged if clean.", "expectedEvidence": "rebase head SHA, local test/typecheck/diff-check output, pushed head SHA, hosted check list, merge commit URL/SHA or blocker evidence.", - "capturedEvidence": "", - "status": "open" + "capturedEvidence": "WP11 closed by cxc D at 2026-07-27T12:05:44Z merge evidence. Branch codex/catalog-written-signal was rebased on origin/dev@7c74e0a22ec96dd5849d3d7253758f0ab15d9737 and pushed at ce716cc117ab23e4420c8c9fe860959968f66cdc. Local targeted verification passed: bun test tests/codex-refresh.test.ts tests/codex-sync-api.test.ts tests/injection-model-api.test.ts = 24 pass/0 fail/112 assertions; bun x tsc --noEmit exit 0; git diff --check origin/dev exit 0. Pre-push full gate passed: bun run test = 5051 pass/0 fail/24881 assertions, privacy scan passed, GUI doctor skipped. Hosted checks on PR #526 head ce716cc117ab23e4420c8c9fe860959968f66cdc all succeeded: CodeRabbit, label, react-doctor, ubuntu-latest, macos-latest, windows-latest, and npm-global ubuntu/macos/windows. Pre-merge gate passed: remote dev stayed 7c74e0a22ec96dd5849d3d7253758f0ab15d9737, PR head matched ce716cc117ab23e4420c8c9fe860959968f66cdc, mergeable MERGEABLE/CLEAN and REST mergeable_state clean. PR #526 https://github.com/lidge-jun/opencodex/pull/526 was squash-merged into dev at 2026-07-27T12:05:44Z as 9dd3c42dae2e7feda3581c6d477cf5a0d6e646bf. Remote codex/catalog-written-signal branch was preserved at ce716cc117ab23e4420c8c9fe860959968f66cdc.", + "status": "met" }, { "id": "C-WP3-ISSUE543", diff --git a/.codexclaw/goalplans/opencodex-live-unfinished-issues-and-prs-triage/ledger.jsonl b/.codexclaw/goalplans/opencodex-live-unfinished-issues-and-prs-triage/ledger.jsonl index 6b3b4dffa3..d1d6920d1c 100644 --- a/.codexclaw/goalplans/opencodex-live-unfinished-issues-and-prs-triage/ledger.jsonl +++ b/.codexclaw/goalplans/opencodex-live-unfinished-issues-and-prs-triage/ledger.jsonl @@ -7,3 +7,14 @@ {"ts":"2026-07-27T11:11:58.894Z","slug":"opencodex-live-unfinished-issues-and-prs-triage","event":"workphase_started","detail":"started WP2"} {"ts":"2026-07-27T11:46:27.214Z","slug":"opencodex-live-unfinished-issues-and-prs-triage","event":"workphase_done","detail":"closed WP10"} {"ts":"2026-07-27T11:46:27.214Z","slug":"opencodex-live-unfinished-issues-and-prs-triage","event":"workphase_started","detail":"started WP2"} +{"ts":"2026-07-27T11:47:00.000Z","slug":"opencodex-live-unfinished-issues-and-prs-triage","event":"workphase_started","detail":"started WP11"} +{"ts":"2026-07-27T12:05:44.000Z","slug":"opencodex-live-unfinished-issues-and-prs-triage","event":"task_done","detail":"WP11-T1 rebased codex/catalog-written-signal onto origin/dev@7c74e0a22ec96dd5849d3d7253758f0ab15d9737"} +{"ts":"2026-07-27T12:05:44.000Z","slug":"opencodex-live-unfinished-issues-and-prs-triage","event":"task_done","detail":"WP11-T2 verified locally and pushed ce716cc117ab23e4420c8c9fe860959968f66cdc"} +{"ts":"2026-07-27T12:05:44.000Z","slug":"opencodex-live-unfinished-issues-and-prs-triage","event":"task_done","detail":"WP11-T3 hosted checks passed and PR #526 squash-merged as 9dd3c42dae2e7feda3581c6d477cf5a0d6e646bf"} +{"ts":"2026-07-27T12:05:44.000Z","slug":"opencodex-live-unfinished-issues-and-prs-triage","event":"criterion_met","detail":"C-WP11-PR526-FINAL evidence in devlog/_plan/260727_wp11-pr526-final-rerun-merge/011_phase1_evidence.md"} +{"ts":"2026-07-27T12:05:44.000Z","slug":"opencodex-live-unfinished-issues-and-prs-triage","event":"workphase_done","detail":"closed WP11"} +{"ts":"2026-07-27T12:05:44.000Z","slug":"opencodex-live-unfinished-issues-and-prs-triage","event":"workphase_started","detail":"started WP2"} +{"ts":"2026-07-27T12:08:50.093Z","slug":"opencodex-live-unfinished-issues-and-prs-triage","event":"workphase_done","detail":"closed WP11"} +{"ts":"2026-07-27T12:08:50.093Z","slug":"opencodex-live-unfinished-issues-and-prs-triage","event":"workphase_started","detail":"started WP3"} +{"ts":"2026-07-27T12:09:30.000Z","slug":"opencodex-live-unfinished-issues-and-prs-triage","event":"steering","detail":"corrected next active work-phase from WP3 to WP6 because PR #527 explicitly depends on the PR #526 decision; WP2 and WP3 remain pending"} +{"ts":"2026-07-27T12:09:30.000Z","slug":"opencodex-live-unfinished-issues-and-prs-triage","event":"workphase_started","detail":"started WP6"} diff --git a/devlog/_plan/260727_wp11-pr526-final-rerun-merge/000_plan.md b/devlog/_plan/260727_wp11-pr526-final-rerun-merge/000_plan.md index 431ca2f3ee..4f9f8c5822 100644 --- a/devlog/_plan/260727_wp11-pr526-final-rerun-merge/000_plan.md +++ b/devlog/_plan/260727_wp11-pr526-final-rerun-merge/000_plan.md @@ -9,9 +9,10 @@ Finish PR #526 after its two dev-baseline blockers were repaired: that made the rebased PR #526 hosted `windows-latest` job red. This work-phase targets PR #526 only. It must rebase the existing -`codex/catalog-written-signal` branch onto the current `origin/dev`, push the -current head, wait for the latest hosted checks on PR #526, and squash merge only -if the latest head is clean. +`codex/catalog-written-signal` branch onto the current `origin/dev`, repair any +direct PR #526 test-contract drift exposed by audit, push the current head, wait +for the latest hosted checks on PR #526, and squash merge only if the latest head +is clean. ## Loop-spec @@ -19,6 +20,9 @@ if the latest head is clean. checks plus already-audited local targeted tests from WP1. - Write scope: existing PR #526 branch `codex/catalog-written-signal`; no new production code unless the rebase exposes a conflict or direct regression. + Test-only repair is allowed for direct #526 contract drift. Current allowed + repair: add required `comboOmissions: []` to the + `tests/injection-model-api.test.ts` `syncCatalogModels` mock return. - Out-of-scope: PR #527 process restart behavior, PR #528 image bridge, security/auth/permission/data migration, main/preview/release branches. - Remote branch handling: do not delete `codex/catalog-written-signal` on merge @@ -40,3 +44,9 @@ if the latest head is clean. - PR #526 is squash-merged to `dev` only if latest head/checks remain clean at merge time. - The remote `codex/catalog-written-signal` branch is preserved. +- Immediate pre-merge stale-base gate passes: + - PR head equals the pushed rebased SHA. + - remote `refs/heads/dev` still equals the rebase base SHA. + - all checks are completed successfully for that exact head. + - `mergeable` is `MERGEABLE` and `mergeStateStatus` is `CLEAN`. + - If dev advanced, restart from fetch/rebase/local tests/push/hosted checks. diff --git a/devlog/_plan/260727_wp11-pr526-final-rerun-merge/010_phase1.md b/devlog/_plan/260727_wp11-pr526-final-rerun-merge/010_phase1.md index 0ce1e38e7b..e2f0021ab5 100644 --- a/devlog/_plan/260727_wp11-pr526-final-rerun-merge/010_phase1.md +++ b/devlog/_plan/260727_wp11-pr526-final-rerun-merge/010_phase1.md @@ -2,13 +2,25 @@ ## MODIFY / NEW / DELETE map -Expected code delta: no new source changes beyond rebasing existing PR #526 commits. +Expected code delta: no new source changes beyond rebasing existing PR #526 +commits, except one direct test-contract repair accepted by A-gate. Existing PR #526 commits to preserve: - `fix(codex): report whether a sync actually wrote the catalog or cache` - `test(codex): cover real catalog sync write signals` +Additional allowed repair: + +- MODIFY `tests/injection-model-api.test.ts` + - Existing mock return: + `return { added: 0, path: join(tempHome!, "missing-catalog.json"), catalogWritten: false };` + - Required mock return: + `return { added: 0, path: join(tempHome!, "missing-catalog.json"), catalogWritten: false, comboOmissions: [] };` + - Rationale: `syncCatalogModels` now returns a structure whose refresh result + contract includes `comboOmissions`; tests are outside `tsconfig.json`, so + `bun x tsc --noEmit` does not detect this mock drift. + Branch operation: - In `/Users/jun/.codex/worktrees/260727-pr526/opencodex`, fetch `origin/dev`. @@ -16,8 +28,14 @@ Branch operation: - If conflict-free, run local verification. - Push with `--force-with-lease` only after local verification. - Wait for latest hosted checks on PR #526. -- Squash merge PR #526 with branch deletion disabled if and only if all latest - checks are green and merge state is clean. +- Immediately before merge, re-fetch `origin/dev` and re-read PR #526. Squash + merge PR #526 with branch deletion disabled if and only if: + - PR head equals the pushed rebased SHA. + - `origin/dev` equals the exact SHA used as the rebase base. + - every check attached to that head is completed successfully. + - `mergeable` is `MERGEABLE` and `mergeStateStatus` is `CLEAN`. +- If any stale-base condition fails, restart from fetch/rebase/local tests/push + and hosted checks. ## TESTS @@ -39,5 +57,9 @@ These are the PR #526 affected tests from WP1 and cover: - `git diff --check origin/dev` exits 0. - `gh pr view 526 --json headRefOid,baseRefOid,mergeStateStatus,statusCheckRollup` shows the pushed head and no failed/pending checks before merge. +- `git rev-parse origin/dev` still equals the rebase base SHA immediately before + merge. +- `gh pr view 526 --json mergeable,mergeStateStatus` reports `MERGEABLE` and + `CLEAN` immediately before merge. - After merge, `gh pr view 526 --json state,mergedAt,mergeCommit` shows merged commit on `dev`. diff --git a/devlog/_plan/260727_wp11-pr526-final-rerun-merge/011_phase1_evidence.md b/devlog/_plan/260727_wp11-pr526-final-rerun-merge/011_phase1_evidence.md new file mode 100644 index 0000000000..0c28100476 --- /dev/null +++ b/devlog/_plan/260727_wp11-pr526-final-rerun-merge/011_phase1_evidence.md @@ -0,0 +1,65 @@ +# 011 — WP11 PR #526 final rerun evidence + +## Branch and rebase state + +- Worktree: `/Users/jun/.codex/worktrees/260727-pr526/opencodex` +- Branch: `codex/catalog-written-signal` +- Rebase base: `origin/dev@7c74e0a22ec96dd5849d3d7253758f0ab15d9737` +- Pushed head: `ce716cc117ab23e4420c8c9fe860959968f66cdc` +- Preserved remote branch after merge: + `refs/heads/codex/catalog-written-signal@ce716cc117ab23e4420c8c9fe860959968f66cdc` + +## Local verification + +- `bun test tests/codex-refresh.test.ts tests/codex-sync-api.test.ts tests/injection-model-api.test.ts` + - Result: 24 pass, 0 fail, 112 assertions. +- `bun x tsc --noEmit` + - Result: exit 0. +- `git diff --check origin/dev` + - Result: exit 0. +- Pre-push gate from `git push --force-with-lease origin codex/catalog-written-signal` + - `bun run typecheck`: pass. + - `bun run lint:gui`: pass. + - `bun run test`: 5051 pass, 0 fail, 24881 assertions. + - `bun run privacy:scan`: pass. + - `bun run doctor:gui:if-changed`: skipped because no `gui/` changes in push range. + +## Hosted checks + +PR: https://github.com/lidge-jun/opencodex/pull/526 + +Latest PR head at check time: +`ce716cc117ab23e4420c8c9fe860959968f66cdc` + +All hosted checks completed successfully for that head: + +- CodeRabbit: success. +- label: success. +- react-doctor: success. +- ubuntu-latest: success. +- macos-latest: success. +- windows-latest: success. +- npm-global ubuntu-latest: success. +- npm-global macos-latest: success. +- npm-global windows-latest: success. + +## Pre-merge stale-base gate + +- Remote `refs/heads/dev` immediately before merge: + `7c74e0a22ec96dd5849d3d7253758f0ab15d9737`. +- PR head immediately before merge: + `ce716cc117ab23e4420c8c9fe860959968f66cdc`. +- `gh pr view 526 --json mergeable,mergeStateStatus`: + `MERGEABLE` / `CLEAN`. +- REST merge state: + `mergeable: true`, `mergeable_state: clean`. + +## Merge result + +- Squash merge was executed through GraphQL `mergePullRequest` with + `expectedHeadOid=ce716cc117ab23e4420c8c9fe860959968f66cdc`. +- Merged at: `2026-07-27T12:05:44Z`. +- Merge commit on `dev`: + `9dd3c42dae2e7feda3581c6d477cf5a0d6e646bf`. +- Remote `codex/catalog-written-signal` branch was not deleted and still points to + `ce716cc117ab23e4420c8c9fe860959968f66cdc`. From 7a62cef41979193e0ab656b1ec2121773773a47e Mon Sep 17 00:00:00 2001 From: bitkyc08-arch Date: Mon, 27 Jul 2026 21:17:04 +0900 Subject: [PATCH 08/10] docs(devlog): record pr527 rebuild request --- .../goalplan.json | 12 +-- .../ledger.jsonl | 2 + .../260727_wp6-pr527-wrong-base/000_plan.md | 92 +++++++++++++++++++ .../260727_wp6-pr527-wrong-base/010_phase1.md | 45 +++++++++ .../011_comment_body.md | 19 ++++ .../012_phase1_evidence.md | 52 +++++++++++ 6 files changed, 216 insertions(+), 6 deletions(-) create mode 100644 devlog/_plan/260727_wp6-pr527-wrong-base/000_plan.md create mode 100644 devlog/_plan/260727_wp6-pr527-wrong-base/010_phase1.md create mode 100644 devlog/_plan/260727_wp6-pr527-wrong-base/011_comment_body.md create mode 100644 devlog/_plan/260727_wp6-pr527-wrong-base/012_phase1_evidence.md diff --git a/.codexclaw/goalplans/opencodex-live-unfinished-issues-and-prs-triage/goalplan.json b/.codexclaw/goalplans/opencodex-live-unfinished-issues-and-prs-triage/goalplan.json index d04df7993d..d798f9e44f 100644 --- a/.codexclaw/goalplans/opencodex-live-unfinished-issues-and-prs-triage/goalplan.json +++ b/.codexclaw/goalplans/opencodex-live-unfinished-issues-and-prs-triage/goalplan.json @@ -2,8 +2,8 @@ "objective": "OpenCodex live unfinished issues and PRs triage against current GitHub state. Scope: repository lidge-jun/opencodex, dev target only, worktree /Users/jun/.codex/worktrees/260727-live-triage/opencodex. First work-phase is docs-only manifest/devlog: fetch live open PRs and open issues via gh, record current number/title/state/base/head/mergeable/checks/reviews/labels, classify every item into merge now, takeover-fix, comment/request-changes, needs-author-rebase, needs-human/security, later/enhancement, upstream-tracking, or close, and produce a priority table. Later work-phases process exactly one PR or one issue per full PABCD cycle. Allowed: directly fix bugfix/simple safe items on dev, create PRs, wait for CI, squash merge, and close linked issues when live evidence proves safety. Out of scope: main/preview/release branches, automatic merge of security/auth/permission/data-migration/privilege-boundary work, and unapproved GUI/UX decisions except the approved OpenRouter Free separate-provider direction. Terminal outcomes: DONE when all safe live items are processed and manifest evidence is current; NOOP when an item needs no action after live check; NEEDS_HUMAN or UNSAFE for risk-bound/security/UX-decision items; BLOCKED for external author/rebase/CI or upstream dependency; BUDGET_EXHAUSTED only if explicit runtime bounds are hit. Verification: gh live snapshots, code/diff review for candidate PRs, CI/check URLs where actions occur, comments/merge/close URLs for external state changes, and devlog evidence committed locally before completion.", "slug": "opencodex-live-unfinished-issues-and-prs-triage", "createdAt": "2026-07-27T10:35:02.724Z", - "updatedAt": "2026-07-27T12:08:50.093Z", - "activeWorkPhaseId": "WP6", + "updatedAt": "2026-07-27T12:16:35.216Z", + "activeWorkPhaseId": "WP7", "workPhases": [ { "id": "WP0", @@ -213,7 +213,7 @@ { "id": "WP6", "title": "PR #527 wrong-base handling", - "status": "in_progress", + "status": "done", "tasks": [ { "id": "WP6-T1", @@ -233,7 +233,7 @@ { "id": "WP7", "title": "Issue #418 V2 custom delegation investigation", - "status": "pending", + "status": "in_progress", "tasks": [ { "id": "WP7-T1", @@ -339,8 +339,8 @@ "id": "C-WP6-PR527", "scenario": "PR #527 wrong-base state is resolved or documented after PR #526 decision.", "expectedEvidence": "live base/check state and retarget/request-rebase/comment URL.", - "capturedEvidence": "", - "status": "open" + "capturedEvidence": "WP6 closed by cxc D at 2026-07-27T12:16:35Z. Live PR #527 remained OPEN on base codex/catalog-written-signal@ce716cc117ab23e4420c8c9fe860959968f66cdc with head codex/app-server-restart@a64aa585630f664a83c25253497a62810133e832, mergeable CONFLICTING and mergeStateStatus DIRTY. PR #526 had merged to dev as 9dd3c42dae2e7feda3581c6d477cf5a0d6e646bf. Topology showed #527 still carried duplicate old #526 commit 1ba588eff663a5be846a8723b90a452dca8cd04c; merge-tree against current dev conflicted in tests/codex-refresh.test.ts and tests/injection-model-api.test.ts. Independent A review returned GO-WITH-FIXES and the blocker was folded into the comment plan. Posted maintainer comment https://github.com/lidge-jun/opencodex/pull/527#issuecomment-5091163284 requesting a clean rebuild from current dev, dropping 1ba588e, preserving the Grok diagnostic, and retargeting after rebuild. No retarget, push, merge, or branch deletion was performed.", + "status": "met" }, { "id": "C-WP7-ISSUE418", diff --git a/.codexclaw/goalplans/opencodex-live-unfinished-issues-and-prs-triage/ledger.jsonl b/.codexclaw/goalplans/opencodex-live-unfinished-issues-and-prs-triage/ledger.jsonl index d1d6920d1c..bdc3c51645 100644 --- a/.codexclaw/goalplans/opencodex-live-unfinished-issues-and-prs-triage/ledger.jsonl +++ b/.codexclaw/goalplans/opencodex-live-unfinished-issues-and-prs-triage/ledger.jsonl @@ -18,3 +18,5 @@ {"ts":"2026-07-27T12:08:50.093Z","slug":"opencodex-live-unfinished-issues-and-prs-triage","event":"workphase_started","detail":"started WP3"} {"ts":"2026-07-27T12:09:30.000Z","slug":"opencodex-live-unfinished-issues-and-prs-triage","event":"steering","detail":"corrected next active work-phase from WP3 to WP6 because PR #527 explicitly depends on the PR #526 decision; WP2 and WP3 remain pending"} {"ts":"2026-07-27T12:09:30.000Z","slug":"opencodex-live-unfinished-issues-and-prs-triage","event":"workphase_started","detail":"started WP6"} +{"ts":"2026-07-27T12:16:35.216Z","slug":"opencodex-live-unfinished-issues-and-prs-triage","event":"workphase_done","detail":"closed WP6"} +{"ts":"2026-07-27T12:16:35.216Z","slug":"opencodex-live-unfinished-issues-and-prs-triage","event":"workphase_started","detail":"started WP7"} diff --git a/devlog/_plan/260727_wp6-pr527-wrong-base/000_plan.md b/devlog/_plan/260727_wp6-pr527-wrong-base/000_plan.md new file mode 100644 index 0000000000..62f90a83b0 --- /dev/null +++ b/devlog/_plan/260727_wp6-pr527-wrong-base/000_plan.md @@ -0,0 +1,92 @@ +# 000 — WP6 PR #527 wrong-base handling plan + +## Objective + +Resolve the triage action for PR #527 only: + +- Live PR #527 currently targets `codex/catalog-written-signal`, not `dev`. +- PR #526 has now been squash-merged to `dev` as + `9dd3c42dae2e7feda3581c6d477cf5a0d6e646bf`. +- PR #527 head `codex/app-server-restart@a64aa585630f664a83c25253497a62810133e832` + still contains the pre-squash PR #526 commit + `1ba588eff663a5be846a8723b90a452dca8cd04c`. + +This work-phase decides the safe maintainer action for #527 after #526 landed. + +## Loop-spec + +- Loop archetype: spec-satisfaction triage, not implementation. +- Trigger: wrong-base PR with stale stacked history after its dependency merged. +- Goal: leave PR #527 in a clear, actionable state without merging unsafe or stale + code. +- Non-goals: do not merge #527; do not delete `codex/catalog-written-signal`; do + not implement or approve the process-termination behavior in this phase. +- Verifier: live `gh pr view 527`, commit topology commands, merge-tree + conflict output, and resulting PR comment URL if a comment is posted. +- Stop condition: PR #527 is classified as `needs-author-rebase` or otherwise + documented with a fresh maintainer comment. +- Memory artifact: this devlog unit plus goalplan criterion `C-WP6-PR527`. +- Terminal outcomes: + - `DONE`: fresh comment/request-rebase URL recorded. + - `NOOP`: live state already changed to dev/green before action. + - `NEEDS_HUMAN`: if retargeting/merging requires accepting process termination + or UX/security implications. + - `BLOCKED`: GitHub mutation fails or author branch permissions prevent action. +- Resource bounds: GitHub PR #527 metadata/comments only; no main/preview/release + branch mutations. + +## Live facts + +- PR: https://github.com/lidge-jun/opencodex/pull/527 +- Current base: `codex/catalog-written-signal@ce716cc117ab23e4420c8c9fe860959968f66cdc` +- Current head: `codex/app-server-restart@a64aa585630f664a83c25253497a62810133e832` +- Current status: + - title: `[WRONG BRANCH] fix(codex): warn about stale Codex app-servers after a catalog write` + - `mergeable`: `CONFLICTING` + - `mergeStateStatus`: `DIRTY` + - `enforce-target`: failure because target is not `dev`. + - CodeRabbit skipped review because base is not `dev` or `preview`. +- Topology: + - `origin/dev...origin/codex/app-server-restart`: `134 2` + - PR commit list includes: + - `1ba588eff663a5be846a8723b90a452dca8cd04c` — old unsquashed PR #526 commit. + - `a64aa585630f664a83c25253497a62810133e832` — #527 app-server restart commit. +- Merge-tree against current `origin/dev` reports conflicts in: + - `tests/codex-refresh.test.ts` + - `tests/injection-model-api.test.ts` + +## Classification + +`needs-maintainer-rebuild` / `needs-author-rebase` + +Reason: retargeting alone is not enough. The branch contains an old copy of the +already-merged #526 change and conflicts with current `dev`. In addition, the +actual #527 feature introduces process discovery/optional SIGTERM behavior and a +large new matching module, so it is not a safe automatic maintainer merge in this +wrong-base cleanup phase. + +Audit correction: the comment must explicitly supersede the previous maintainer +guidance that said simple retargeting was enough. #527 is not cleanly stacked on +#526; both #526 and #527 forked from `4618c931`, and the old #526 commit +`1ba588eff663a5be846a8723b90a452dca8cd04c` has a different stable patch-id from +the landed squash `9dd3c42dae2e7feda3581c6d477cf5a0d6e646bf`. A generic rebase +may replay already-landed work. + +## Planned maintainer action + +Post one maintainer comment on PR #527: + +1. State that #526 is merged to `dev`. +2. Ask the author to rebase/cherry-pick only the app-server restart commit onto + current `dev`. +3. Explicitly request dropping duplicate commit + `1ba588eff663a5be846a8723b90a452dca8cd04c`. +4. Name the two known conflict files from merge-tree. +5. Ask them to preserve the existing Grok sync failure diagnostic instead of + reverting it while rebasing. +6. State that process termination/PID matching behavior still needs review after + the branch is clean; this request does not mean the PR is merge-ready. +7. Explain that after rebuilding and retargeting to `dev`, normal review/checks + can run. + +No branch retarget, force-push, or merge in this work-phase. diff --git a/devlog/_plan/260727_wp6-pr527-wrong-base/010_phase1.md b/devlog/_plan/260727_wp6-pr527-wrong-base/010_phase1.md new file mode 100644 index 0000000000..b4c848f188 --- /dev/null +++ b/devlog/_plan/260727_wp6-pr527-wrong-base/010_phase1.md @@ -0,0 +1,45 @@ +# 010 — Phase 1: PR #527 request author rebase + +## MODIFY / NEW / DELETE map + +No production code changes. + +External GitHub action only: + +- NEW PR comment on https://github.com/lidge-jun/opencodex/pull/527. + +## Comment content requirements + +The comment must include: + +- `#526` has been merged to `dev` as + `9dd3c42dae2e7feda3581c6d477cf5a0d6e646bf`. +- Current #527 base/head: + - base `codex/catalog-written-signal@ce716cc117ab23e4420c8c9fe860959968f66cdc` + - head `codex/app-server-restart@a64aa585630f664a83c25253497a62810133e832` +- Why retarget alone is insufficient: + - branch still carries old PR #526 commit + `1ba588eff663a5be846a8723b90a452dca8cd04c`; + - merge-tree against current `dev` conflicts in + `tests/codex-refresh.test.ts` and `tests/injection-model-api.test.ts`. +- Requested author action: + - rebuild #527 on current `dev`; + - keep only the app-server restart change on top of `dev`; + - drop duplicate commit `1ba588eff663a5be846a8723b90a452dca8cd04c`; + - retarget the PR to `dev` after that. +- Review boundary: + - supersede the prior maintainer note that said simple retargeting was enough; + - do not remove the existing Grok sync failure diagnostic in `src/cli/index.ts` + during the rebase; + - process termination/restart behavior will be reviewed after the branch is + clean and checks run on `dev`. + - unresolved process/PID review findings mean this request is not merge + approval. + +## Verification + +- `gh pr view 527 --json ...` captures live pre-comment state. +- `git merge-tree origin/dev origin/codex/app-server-restart` captures conflict + evidence. +- `gh pr comment 527 --body-file ` returns a comment URL. +- `gh pr view 527 --json comments` confirms the new maintainer comment exists. diff --git a/devlog/_plan/260727_wp6-pr527-wrong-base/011_comment_body.md b/devlog/_plan/260727_wp6-pr527-wrong-base/011_comment_body.md new file mode 100644 index 0000000000..2fc8bcfa33 --- /dev/null +++ b/devlog/_plan/260727_wp6-pr527-wrong-base/011_comment_body.md @@ -0,0 +1,19 @@ +#527 follow-up after #526 landed: + +Please do not just retarget this PR to `dev`. My earlier note saying retargeting would be enough is now superseded. + +Current live state: + +- #526 is merged into `dev` as `9dd3c42dae2e7feda3581c6d477cf5a0d6e646bf`. +- #527 is still based on `codex/catalog-written-signal@ce716cc117ab23e4420c8c9fe860959968f66cdc`. +- #527 head is `codex/app-server-restart@a64aa585630f664a83c25253497a62810133e832`. +- The branch still carries the old pre-squash #526 commit `1ba588eff663a5be846a8723b90a452dca8cd04c`, so a generic rebase can replay already-landed work. +- A read-only merge-tree against current `dev` conflicts in: + - `tests/codex-refresh.test.ts` + - `tests/injection-model-api.test.ts` + +Requested next step: please rebuild this PR from current `dev`, porting only the app-server stale-process warning / optional restart behavior from `a64aa585`, and drop the duplicate `1ba588e` catalog-write-signal commit. Then retarget the PR to `dev` so the normal checks and reviews run on the right base. + +One rebase-specific note: please preserve the current Grok sync failure diagnostic in `src/cli/index.ts`; the existing #527 head appears to revert that back to silent best-effort catches. + +This is not merge approval yet. The process detection / optional SIGTERM path still needs review on a clean `dev`-based branch. diff --git a/devlog/_plan/260727_wp6-pr527-wrong-base/012_phase1_evidence.md b/devlog/_plan/260727_wp6-pr527-wrong-base/012_phase1_evidence.md new file mode 100644 index 0000000000..eba324f47e --- /dev/null +++ b/devlog/_plan/260727_wp6-pr527-wrong-base/012_phase1_evidence.md @@ -0,0 +1,52 @@ +# 012 — WP6 PR #527 evidence + +## Live PR state + +- PR: https://github.com/lidge-jun/opencodex/pull/527 +- State: `OPEN` +- Base: `codex/catalog-written-signal@ce716cc117ab23e4420c8c9fe860959968f66cdc` +- Head: `codex/app-server-restart@a64aa585630f664a83c25253497a62810133e832` +- Merge state: `CONFLICTING` / `DIRTY` +- Checks: + - `enforce-target`: failure because the PR target is not `dev`. + - CodeRabbit status is success but review was skipped because the base branch + is not `dev` or `preview`. + +## Topology evidence + +- PR #526 is merged into `dev` as + `9dd3c42dae2e7feda3581c6d477cf5a0d6e646bf`. +- PR #527 still includes the old pre-squash #526 commit + `1ba588eff663a5be846a8723b90a452dca8cd04c`. +- `origin/dev...origin/codex/app-server-restart` count: `134 2`. +- Read-only `git merge-tree origin/dev origin/codex/app-server-restart` + reported conflicts in: + - `tests/codex-refresh.test.ts` + - `tests/injection-model-api.test.ts` + +## Audit result + +Independent A-gate reviewer Ramanujan returned: + +`VERDICT: GO-WITH-FIXES (blockers=1)` + +The blocker was folded into the comment plan: do not request a generic rebase or +simple retarget; request a clean rebuild from current `dev`, drop duplicate +commit `1ba588eff663a5be846a8723b90a452dca8cd04c`, and port only the +app-server restart behavior from `a64aa585630f664a83c25253497a62810133e832`. + +## Maintainer comment + +Posted: + +https://github.com/lidge-jun/opencodex/pull/527#issuecomment-5091163284 + +Live verification confirmed that the comment body exists on PR #527 and was +authored by `lidge-jun` at `2026-07-27T12:15:43Z`. + +## Terminal outcome + +`DONE` for WP6. + +#527 remains open as `needs-maintainer-rebuild` / `needs-author-rebase`; it is +not merge-ready and was not retargeted, pushed, or merged in this work-phase. From 929745e8c9f80f6e78d80af83ca2c42852332805 Mon Sep 17 00:00:00 2001 From: bitkyc08-arch Date: Mon, 27 Jul 2026 21:26:00 +0900 Subject: [PATCH 09/10] docs(devlog): record issue418 noop triage --- .../goalplan.json | 18 +-- .../ledger.jsonl | 4 +- .../000_plan.md | 106 ++++++++++++++++++ .../010_phase1.md | 50 +++++++++ .../011_noop_evidence.md | 72 ++++++++++++ 5 files changed, 240 insertions(+), 10 deletions(-) create mode 100644 devlog/_plan/260727_wp7-issue418-v2-custom-delegation/000_plan.md create mode 100644 devlog/_plan/260727_wp7-issue418-v2-custom-delegation/010_phase1.md create mode 100644 devlog/_plan/260727_wp7-issue418-v2-custom-delegation/011_noop_evidence.md diff --git a/.codexclaw/goalplans/opencodex-live-unfinished-issues-and-prs-triage/goalplan.json b/.codexclaw/goalplans/opencodex-live-unfinished-issues-and-prs-triage/goalplan.json index d798f9e44f..bf89c77b1f 100644 --- a/.codexclaw/goalplans/opencodex-live-unfinished-issues-and-prs-triage/goalplan.json +++ b/.codexclaw/goalplans/opencodex-live-unfinished-issues-and-prs-triage/goalplan.json @@ -1,9 +1,9 @@ { - "objective": "OpenCodex live unfinished issues and PRs triage against current GitHub state. Scope: repository lidge-jun/opencodex, dev target only, worktree /Users/jun/.codex/worktrees/260727-live-triage/opencodex. First work-phase is docs-only manifest/devlog: fetch live open PRs and open issues via gh, record current number/title/state/base/head/mergeable/checks/reviews/labels, classify every item into merge now, takeover-fix, comment/request-changes, needs-author-rebase, needs-human/security, later/enhancement, upstream-tracking, or close, and produce a priority table. Later work-phases process exactly one PR or one issue per full PABCD cycle. Allowed: directly fix bugfix/simple safe items on dev, create PRs, wait for CI, squash merge, and close linked issues when live evidence proves safety. Out of scope: main/preview/release branches, automatic merge of security/auth/permission/data-migration/privilege-boundary work, and unapproved GUI/UX decisions except the approved OpenRouter Free separate-provider direction. Terminal outcomes: DONE when all safe live items are processed and manifest evidence is current; NOOP when an item needs no action after live check; NEEDS_HUMAN or UNSAFE for risk-bound/security/UX-decision items; BLOCKED for external author/rebase/CI or upstream dependency; BUDGET_EXHAUSTED only if explicit runtime bounds are hit. Verification: gh live snapshots, code/diff review for candidate PRs, CI/check URLs where actions occur, comments/merge/close URLs for external state changes, and devlog evidence committed locally before completion.", + "objective": "OpenCodex live unfinished issues and PRs triage against current GitHub state. Scope: repository lidge-jun/opencodex, dev target only, worktree . First work-phase is docs-only manifest/devlog: fetch live open PRs and open issues via gh, record current number/title/state/base/head/mergeable/checks/reviews/labels, classify every item into merge now, takeover-fix, comment/request-changes, needs-author-rebase, needs-human/security, later/enhancement, upstream-tracking, or close, and produce a priority table. Later work-phases process exactly one PR or one issue per full PABCD cycle. Allowed: directly fix bugfix/simple safe items on dev, create PRs, wait for CI, squash merge, and close linked issues when live evidence proves safety. Out of scope: main/preview/release branches, automatic merge of security/auth/permission/data-migration/privilege-boundary work, and unapproved GUI/UX decisions except the approved OpenRouter Free separate-provider direction. Terminal outcomes: DONE when all safe live items are processed and manifest evidence is current; NOOP when an item needs no action after live check; NEEDS_HUMAN or UNSAFE for risk-bound/security/UX-decision items; BLOCKED for external author/rebase/CI or upstream dependency; BUDGET_EXHAUSTED only if explicit runtime bounds are hit. Verification: gh live snapshots, code/diff review for candidate PRs, CI/check URLs where actions occur, comments/merge/close URLs for external state changes, and devlog evidence committed locally before completion.", "slug": "opencodex-live-unfinished-issues-and-prs-triage", "createdAt": "2026-07-27T10:35:02.724Z", - "updatedAt": "2026-07-27T12:16:35.216Z", - "activeWorkPhaseId": "WP7", + "updatedAt": "2026-07-27T12:25:15.899Z", + "activeWorkPhaseId": "WP8", "workPhases": [ { "id": "WP0", @@ -233,17 +233,17 @@ { "id": "WP7", "title": "Issue #418 V2 custom delegation investigation", - "status": "in_progress", + "status": "done", "tasks": [ { "id": "WP7-T1", "title": "Read custom-parent/custom-child delegation code path and issue evidence", - "status": "pending" + "status": "done" }, { "id": "WP7-T2", "title": "Fix or comment with code pointers and required reproduction evidence", - "status": "pending" + "status": "done" } ], "criteriaIds": [ @@ -253,7 +253,7 @@ { "id": "WP8", "title": "Issue #509 JS heap watchdog investigation", - "status": "pending", + "status": "in_progress", "tasks": [ { "id": "WP8-T1", @@ -346,8 +346,8 @@ "id": "C-WP7-ISSUE418", "scenario": "Issue #418 has a proven fix or a fresh investigation comment with code pointers.", "expectedEvidence": "test/command output if fixed, otherwise comment URL plus code pointers.", - "capturedEvidence": "", - "status": "open" + "capturedEvidence": "WP7 closed as NOOP/comment-request-changes at 2026-07-27. Live issue #418 remains OPEN with bug label. Existing comments already satisfy this phase's intended maintainer action: owner request https://github.com/lidge-jun/opencodex/issues/418#issuecomment-5069836945 asks for raw provider tool-call, Responses event, and child lifecycle trace; reporter acknowledgement https://github.com/lidge-jun/opencodex/issues/418#issuecomment-5070272410 says same-run failing spawn_agent trace is still unavailable until usage limit clears; collaborator cross-link https://github.com/lidge-jun/opencodex/issues/418#issuecomment-5085535548 keeps #418 separate from #92 pending the three-boundary capture. Code review found no proven local argument-drop path: parser copies tool parameters at src/responses/parser.ts:134-139; OpenAI-compatible adapter forwards parameters at src/adapters/openai-chat.ts:432-445; provider function.arguments fragments are accumulated at src/adapters/openai-chat.ts:749-768; bridge forwards deltas at src/bridge.ts:610-617 and materializes {} only when accumulated bytes are empty at src/bridge.ts:366-375. No GitHub comment, close, merge, or code change was performed.", + "status": "met" }, { "id": "C-WP8-ISSUE509", diff --git a/.codexclaw/goalplans/opencodex-live-unfinished-issues-and-prs-triage/ledger.jsonl b/.codexclaw/goalplans/opencodex-live-unfinished-issues-and-prs-triage/ledger.jsonl index bdc3c51645..07d4aeae88 100644 --- a/.codexclaw/goalplans/opencodex-live-unfinished-issues-and-prs-triage/ledger.jsonl +++ b/.codexclaw/goalplans/opencodex-live-unfinished-issues-and-prs-triage/ledger.jsonl @@ -1,4 +1,4 @@ -{"ts":"2026-07-27T10:35:02.725Z","slug":"opencodex-live-unfinished-issues-and-prs-triage","event":"created","detail":"init objective=\"OpenCodex live unfinished issues and PRs triage against current GitHub state. Scope: repository lidge-jun/opencodex, dev target only, worktree /Users/jun/.codex/worktrees/260727-live-triage/opencodex. First work-phase is docs-only manifest/devlog: fetch live open PRs and open issues via gh, record current number/title/state/base/head/mergeable/checks/reviews/labels, classify every item into merge now, takeover-fix, comment/request-changes, needs-author-rebase, needs-human/security, later/enhancement, upstream-tracking, or close, and produce a priority table. Later work-phases process exactly one PR or one issue per full PABCD cycle. Allowed: directly fix bugfix/simple safe items on dev, create PRs, wait for CI, squash merge, and close linked issues when live evidence proves safety. Out of scope: main/preview/release branches, automatic merge of security/auth/permission/data-migration/privilege-boundary work, and unapproved GUI/UX decisions except the approved OpenRouter Free separate-provider direction. Terminal outcomes: DONE when all safe live items are processed and manifest evidence is current; NOOP when an item needs no action after live check; NEEDS_HUMAN or UNSAFE for risk-bound/security/UX-decision items; BLOCKED for external author/rebase/CI or upstream dependency; BUDGET_EXHAUSTED only if explicit runtime bounds are hit. Verification: gh live snapshots, code/diff review for candidate PRs, CI/check URLs where actions occur, comments/merge/close URLs for external state changes, and devlog evidence committed locally before completion.\" criteria=0"} +{"ts":"2026-07-27T10:35:02.725Z","slug":"opencodex-live-unfinished-issues-and-prs-triage","event":"created","detail":"init objective=\"OpenCodex live unfinished issues and PRs triage against current GitHub state. Scope: repository lidge-jun/opencodex, dev target only, worktree . First work-phase is docs-only manifest/devlog: fetch live open PRs and open issues via gh, record current number/title/state/base/head/mergeable/checks/reviews/labels, classify every item into merge now, takeover-fix, comment/request-changes, needs-author-rebase, needs-human/security, later/enhancement, upstream-tracking, or close, and produce a priority table. Later work-phases process exactly one PR or one issue per full PABCD cycle. Allowed: directly fix bugfix/simple safe items on dev, create PRs, wait for CI, squash merge, and close linked issues when live evidence proves safety. Out of scope: main/preview/release branches, automatic merge of security/auth/permission/data-migration/privilege-boundary work, and unapproved GUI/UX decisions except the approved OpenRouter Free separate-provider direction. Terminal outcomes: DONE when all safe live items are processed and manifest evidence is current; NOOP when an item needs no action after live check; NEEDS_HUMAN or UNSAFE for risk-bound/security/UX-decision items; BLOCKED for external author/rebase/CI or upstream dependency; BUDGET_EXHAUSTED only if explicit runtime bounds are hit. Verification: gh live snapshots, code/diff review for candidate PRs, CI/check URLs where actions occur, comments/merge/close URLs for external state changes, and devlog evidence committed locally before completion.\" criteria=0"} {"ts":"2026-07-27T10:46:14.884Z","slug":"opencodex-live-unfinished-issues-and-prs-triage","event":"workphase_done","detail":"closed WP0"} {"ts":"2026-07-27T10:46:14.884Z","slug":"opencodex-live-unfinished-issues-and-prs-triage","event":"workphase_started","detail":"started WP1"} {"ts":"2026-07-27T11:01:22.413Z","slug":"opencodex-live-unfinished-issues-and-prs-triage","event":"workphase_done","detail":"closed WP1"} @@ -20,3 +20,5 @@ {"ts":"2026-07-27T12:09:30.000Z","slug":"opencodex-live-unfinished-issues-and-prs-triage","event":"workphase_started","detail":"started WP6"} {"ts":"2026-07-27T12:16:35.216Z","slug":"opencodex-live-unfinished-issues-and-prs-triage","event":"workphase_done","detail":"closed WP6"} {"ts":"2026-07-27T12:16:35.216Z","slug":"opencodex-live-unfinished-issues-and-prs-triage","event":"workphase_started","detail":"started WP7"} +{"ts":"2026-07-27T12:25:15.900Z","slug":"opencodex-live-unfinished-issues-and-prs-triage","event":"workphase_done","detail":"closed WP7"} +{"ts":"2026-07-27T12:25:15.900Z","slug":"opencodex-live-unfinished-issues-and-prs-triage","event":"workphase_started","detail":"started WP8"} diff --git a/devlog/_plan/260727_wp7-issue418-v2-custom-delegation/000_plan.md b/devlog/_plan/260727_wp7-issue418-v2-custom-delegation/000_plan.md new file mode 100644 index 0000000000..dd1d681aec --- /dev/null +++ b/devlog/_plan/260727_wp7-issue418-v2-custom-delegation/000_plan.md @@ -0,0 +1,106 @@ +# 000 — WP7 Issue #418 V2 custom delegation investigation plan + +## Objective + +Handle Issue #418 only: + +https://github.com/lidge-jun/opencodex/issues/418 + +The reporter reproduced custom-parent → custom-child V2 delegation failure on +OpenCodex `2.7.39` / Codex CLI `0.145.0`. They later supplied a control trace +showing ordinary structured tool-call arguments are preserved, but they still do +not have a same-run raw `spawn_agent` capture for the failing custom-parent +delegation call. + +## Loop-spec + +- Loop archetype: spec-satisfaction investigation/comment. +- Trigger: bug issue with partial reporter evidence but no same-run failing + boundary trace. +- Goal: either identify a local OpenCodex fix or leave the issue with exact + code-grounded trace requirements and current workaround. +- Non-goals: do not close #418; do not merge it into #92; do not add speculative + instrumentation without a confirmed local defect. +- Verifier: live issue state, existing comment URLs, code pointers, and prior + devlog analysis. +- Stop condition: Issue #418 already has an equivalent maintainer request naming + what is known, what is not proven, and the exact trace needed to classify the + failure; otherwise post one fresh maintainer comment. +- Terminal outcomes: + - `DONE`: investigation state verified; either a fresh comment was posted or an + equivalent existing maintainer request was confirmed. + - `NOOP`: issue already contains an equivalent current maintainer request. + - `BLOCKED`: GitHub comment mutation fails. + - `NEEDS_HUMAN`: if deciding to instrument privacy-sensitive full request + captures is required. + +## Current evidence + +Live issue #418: + +- State: `OPEN` +- Label: `bug` +- Reporter has a 2.7.39 custom-parent → custom-child failure with repeated + `missing field message` and no child lifecycle notification. +- Reporter also has a 2.7.39 ordinary structured tool-call control where + arguments are preserved through provider raw response and Responses events. +- Reporter could not capture the failing `spawn_agent` raw call after + instrumentation because the attempt hit a client usage limit. +- Existing maintainer request: + https://github.com/lidge-jun/opencodex/issues/418#issuecomment-5069836945 + requests the same same-run provider/event/lifecycle boundary trace and keeps + the issue open. +- Reporter acknowledged the limitation and committed to repeating after the + usage limit clears: + https://github.com/lidge-jun/opencodex/issues/418#issuecomment-5070272410. +- Collaborator cross-link keeps #418 separate from #92 and confirms it remains + pending the three-boundary `spawn_agent` capture: + https://github.com/lidge-jun/opencodex/issues/418#issuecomment-5085535548. + +Current code pointers: + +- V2 surface detection and guidance: + `src/server/responses/collaboration.ts:136-154`, + `src/server/responses/collaboration.ts:189-250`. +- Unreadable encrypted child-task guard: + `src/server/responses/core.ts:447-459`, + `src/server/responses/core.ts:870-890`, + `src/server/responses/encrypted-payload.ts:185-231`, + `src/server/responses/encrypted-payload.ts:265-306`. +- Agent-message parsing: + `src/responses/parser.ts:338-360`. +- Prior analysis: + `devlog/_fin/260723_issue_triage/007_investigation_290_288_spawn_agent.md:283-356`, + `devlog/_fin/260723_issue_triage/007_investigation_290_288_spawn_agent.md:430-453`. + +## Classification + +`comment/request-changes` / `needs-info`. + +OpenCodex has known handling for unreadable encrypted V2 child tasks (#92) and +for plaintext agent-message compatibility. The current #418 report still lacks +the one failing `spawn_agent` boundary trace needed to distinguish: + +- inbound Codex/Desktop `additional_tools` schema already empty; +- OpenCodex adapter damaging the outgoing `spawn_agent` schema; +- provider/model emitting empty `{}` arguments for `spawn_agent`; +- OpenCodex bridge losing non-empty provider arguments; +- Codex V2 lifecycle failure after a valid spawn. + +## Planned action + +No new GitHub comment. + +The A-gate review found that the prior owner and collaborator comments already +satisfy this phase's intended external action. Posting another maintainer comment +would be redundant and would add noise without new evidence. + +Record a NOOP triage result that: + +1. Confirms #418 remains separate from #92. +2. Confirms the issue is still `comment/request-changes` / `needs-info`. +3. Records the existing comment URLs that already request the same-run trace. +4. Records current code pointers showing no proven local path that erases a + non-empty `spawn_agent` `message` argument. + +No code change in this work-phase. diff --git a/devlog/_plan/260727_wp7-issue418-v2-custom-delegation/010_phase1.md b/devlog/_plan/260727_wp7-issue418-v2-custom-delegation/010_phase1.md new file mode 100644 index 0000000000..d9d9cc812c --- /dev/null +++ b/devlog/_plan/260727_wp7-issue418-v2-custom-delegation/010_phase1.md @@ -0,0 +1,50 @@ +# 010 — Phase 1: Issue #418 NOOP investigation triage + +## MODIFY / NEW / DELETE map + +No production code changes. + +No external GitHub mutation. + +## NOOP requirements + +The issue must already contain: + +- confirm #418 remains separate from #92; +- explain that current evidence is still not enough to assign the defect to + OpenCodex or upstream/provider structured-tool emission; +- request a same-run failing `spawn_agent` boundary trace; +- keep the workaround as V1 or parent-model inheritance/native child; +- keep the issue open. + +Current live comments satisfy this: + +- owner maintainer request: + https://github.com/lidge-jun/opencodex/issues/418#issuecomment-5069836945 +- reporter acknowledgement and pending retry: + https://github.com/lidge-jun/opencodex/issues/418#issuecomment-5070272410 +- collaborator #92 cross-link / non-duplicate status: + https://github.com/lidge-jun/opencodex/issues/418#issuecomment-5085535548 + +Current code pointers reviewed: + +- `src/server/responses/collaboration.ts:136-154` for V1/V2 tool-surface + detection. +- `src/server/responses/collaboration.ts:189-250` for V2 guidance and + `fork_turns: "none"` override instructions. +- `src/responses/parser.ts:131` copies tool definitions including parameters. +- `src/adapters/openai-chat.ts:421` preserves tool `parameters` when translating + to OpenAI-compatible provider tools. +- `src/adapters/openai-chat.ts:749` appends raw provider argument fragments. +- `src/bridge.ts:610` emits Responses function-call argument events. +- `src/bridge.ts:366` materializes `{}` only when zero argument bytes were + received. +- `src/server/responses/core.ts:870` detects unreadable encrypted child-task + payloads before parsing/body rewriting, and `src/server/responses/core.ts:996` + rejects them after final route selection. + +## Verification + +- `gh issue view 418 --json comments` confirms the existing comments and URLs. +- `gh issue view 418 --json state,labels` confirms the issue remains open bug. +- `git status` remains clean except devlog/goalplan evidence. diff --git a/devlog/_plan/260727_wp7-issue418-v2-custom-delegation/011_noop_evidence.md b/devlog/_plan/260727_wp7-issue418-v2-custom-delegation/011_noop_evidence.md new file mode 100644 index 0000000000..d4e052e9f3 --- /dev/null +++ b/devlog/_plan/260727_wp7-issue418-v2-custom-delegation/011_noop_evidence.md @@ -0,0 +1,72 @@ +# 011 — WP7 Issue #418 NOOP evidence + +## Result + +NOOP external action. + +Issue #418 already has the maintainer/collaborator comments this work-phase +would otherwise add, so no new GitHub comment was posted. + +## Live issue evidence + +Command: + +```bash +gh issue view 418 --repo lidge-jun/opencodex --json number,title,state,labels,comments,url +``` + +Observed: + +- Issue: https://github.com/lidge-jun/opencodex/issues/418 +- State: `OPEN` +- Label: `bug` +- Owner comment requesting the same-run raw provider, Responses event, and child + lifecycle trace: + https://github.com/lidge-jun/opencodex/issues/418#issuecomment-5069836945 +- Reporter acknowledgement that the failing same-run `spawn_agent` trace is + still unavailable until usage limit clears: + https://github.com/lidge-jun/opencodex/issues/418#issuecomment-5070272410 +- Collaborator cross-link confirming #418 is not a duplicate of #92 and remains + open pending the three-boundary `spawn_agent` capture: + https://github.com/lidge-jun/opencodex/issues/418#issuecomment-5085535548 + +## Current code evidence + +Reviewed code still does not prove a local OpenCodex path that erases a non-empty +`spawn_agent` `message` argument: + +- `src/server/responses/collaboration.ts:136-154` detects the active v1/v2 + collaboration tool surface. +- `src/server/responses/collaboration.ts:189-250` emits V2 guidance, including + `fork_turns: "none"` for model/effort overrides. +- `src/responses/parser.ts:134-139` copies incoming function-tool + `parameters`. +- `src/adapters/openai-chat.ts:432-445` forwards translated tool + `parameters` to the provider schema. +- `src/adapters/openai-chat.ts:749-768` accumulates raw provider + `function.arguments` fragments. +- `src/bridge.ts:610-617` forwards tool-call argument deltas as + `response.function_call_arguments.delta`. +- `src/bridge.ts:366-375` finalizes arguments and only materializes `{}` when + accumulated argument bytes are empty. +- `src/server/responses/core.ts:870-874` detects unreadable encrypted child-task + payloads before parsing/body rewriting, and `src/server/responses/core.ts:996-999` + rejects them after final route selection. + +## A-gate disposition + +Independent A review returned `GO-WITH-FIXES`: + +1. Do not post the drafted comment because existing comments already satisfy the + requested maintainer action. +2. If describing #92 handling, state that unreadable encrypted child tasks are + detected before parsing/body rewriting and rejected after final route + selection. + +Both fixes are reflected in `000_plan.md` and `010_phase1.md`. + +## Classification + +`comment/request-changes` / `needs-info`. + +No code change, no issue close, no merge. From 331d94bbe11fda940563328d274179a06e711dbf Mon Sep 17 00:00:00 2001 From: bitkyc08-arch Date: Mon, 27 Jul 2026 21:41:06 +0900 Subject: [PATCH 10/10] fix(memory): observe external Windows retention --- .../000_plan.md | 97 +++++++++++++++++++ .../010_phase1.md | 42 ++++++++ .../011_verification.md | 48 +++++++++ .../docs/troubleshooting/windows-memory.md | 36 ++++--- .../components/MemoryObservabilityCard.tsx | 52 ++++++++-- gui/src/i18n/de.ts | 8 +- gui/src/i18n/en.ts | 8 +- gui/src/i18n/ja.ts | 8 +- gui/src/i18n/ko.ts | 8 +- gui/src/i18n/ru.ts | 8 +- gui/src/i18n/zh.ts | 8 +- gui/tests/memory-observability-card.test.tsx | 16 +++ src/cli/doctor.ts | 53 ++++++++-- src/server/management/system-routes.ts | 42 +++++--- src/server/memory-watchdog.ts | 64 ++++++++++-- structure/05_gui-and-management-api.md | 2 +- tests/doctor.test.ts | 24 ++++- tests/memory-watchdog.test.ts | 97 +++++++++++++++---- 18 files changed, 531 insertions(+), 90 deletions(-) create mode 100644 devlog/_plan/260727_wp8-issue509-heap-watchdog/000_plan.md create mode 100644 devlog/_plan/260727_wp8-issue509-heap-watchdog/010_phase1.md create mode 100644 devlog/_plan/260727_wp8-issue509-heap-watchdog/011_verification.md diff --git a/devlog/_plan/260727_wp8-issue509-heap-watchdog/000_plan.md b/devlog/_plan/260727_wp8-issue509-heap-watchdog/000_plan.md new file mode 100644 index 0000000000..fd802f2b06 --- /dev/null +++ b/devlog/_plan/260727_wp8-issue509-heap-watchdog/000_plan.md @@ -0,0 +1,97 @@ +# 000 — WP8 Issue #509 Windows memory observability plan + +## Objective + +Handle Issue #509 only: + +https://github.com/lidge-jun/opencodex/issues/509 + +The reporter originally classified the Windows memory growth as JS-heap-side, but +the latest controlled follow-up withdraws that conclusion. The remaining +actionable gap is observability: the service endpoint, watchdog sample ring, +doctor output, and dashboard expose `rss`, `heapUsed`, `heapTotal`, `jscHeap`, +and `responseState`, but not `process.memoryUsage().external` or +`arrayBuffers`. On the bundled Bun 1.3.14 Windows runtime those two counters are +the useful signal for reachable ArrayBuffer/external retention, while RSS can be +blind after working-set trimming. + +## Classification + +`takeover-fix/investigate`. + +This is a small additive observability fix. It does not change auth, permission, +storage migration, model routing, release branches, or restart policy. + +## Planned change + +Production code: + +- `src/server/memory-watchdog.ts` + - add `external` and `arrayBuffers` to `MemorySample`; + - sample them from `process.memoryUsage()`; + - add explicit `observedBytes` and `observedMetric`; + - make the rate-limited warn threshold use the largest observed memory counter + among `rss`, `external`, and `arrayBuffers`; + - name the triggering metric in the warning line. +- `src/server/management/system-routes.ts` + - include top-level scalar `external` and `arrayBuffers` in + `GET /api/system/memory`. + - include top-level `observedBytes` and `observedMetric` even when no watchdog + instance is active. +- `src/cli/doctor.ts` + - parse optional `external` and `arrayBuffers`; + - render them in the Memory / runtime section; + - classify high observed memory using the same largest-counter threshold as the + watchdog while keeping the existing high-RSS JS-vs-native language only when + RSS itself is high. +- `gui/src/components/MemoryObservabilityCard.tsx` + - accept optional `external` and `arrayBuffers`; + - render them in the detail area; + - compute drift from observed memory instead of RSS only. +- `gui/src/i18n/{en,ko,de,ja,ru,zh}.ts` + - add labels for external/ArrayBuffers if needed. +- `docs-site/src/content/docs/troubleshooting/windows-memory.md` + - update wording so the attribution rule does not claim RSS alone is a + reliable Windows discriminator. + +Tests: + +- `tests/memory-watchdog.test.ts` + - endpoint includes scalar `external` and `arrayBuffers`; + - watchdog warning fires when RSS is below threshold but external memory crosses + it; + - warning remains scalar and path-free. +- `tests/doctor.test.ts` + - doctor parses/renders the new counters; + - below-RSS but high external memory no longer prints "looks normal". + +## Non-goals + +- Do not auto-restart. +- Do not close #314. +- Do not claim the Windows leak is fixed. +- Do not expose paths, request bodies, account IDs, tokens, or private prompts. +- Do not add Windows OS-specific `PrivatePageCount` in this phase; that needs a + separate platform implementation and review. + +## Verification + +- `bun test tests/memory-watchdog.test.ts tests/doctor.test.ts` +- `bun x tsc --noEmit` +- `bun run privacy:scan` +- A-gate reviewer verdict folded before B. + +## A-gate review + +Reviewer verdict: `GO-WITH-FIXES`. + +Folded blockers: + +- GUI Memory card must render the new counters and stop deriving drift from RSS + only. +- Doctor interpretation must use observed memory and stop saying low RSS means + memory is normal when external/ArrayBuffers are high. +- Watchdog warning text must not be RSS-specific and must name the triggering + metric. +- Docs/SOT must replace RSS-only terminology with observed-memory terminology + and Windows counter caveats. diff --git a/devlog/_plan/260727_wp8-issue509-heap-watchdog/010_phase1.md b/devlog/_plan/260727_wp8-issue509-heap-watchdog/010_phase1.md new file mode 100644 index 0000000000..44d8d16198 --- /dev/null +++ b/devlog/_plan/260727_wp8-issue509-heap-watchdog/010_phase1.md @@ -0,0 +1,42 @@ +# 010 — WP8 implementation record + +## MODIFY map + +- `src/server/memory-watchdog.ts` + - `MemorySampleBase` now carries `rss`, `heapUsed`, `heapTotal`, `external`, + and `arrayBuffers`. + - Normalized `MemorySample` adds `observedBytes` and `observedMetric`, computed + by `observedMemoryCounter()`. + - The warn threshold uses `observedBytes` and the warning names the metric. +- `src/server/management/system-routes.ts` + - `/api/system/memory` returns top-level `external`, `arrayBuffers`, + `observedBytes`, and `observedMetric`. + - Watchdog snapshot also carries `observedBytes` / `observedMetric`. +- `src/cli/doctor.ts` + - Parses optional new counters while remaining compatible with old proxies. + - Renders RSS, external, ArrayBuffers, heap, and observed metric. + - Uses observed memory for the threshold branch. +- `gui/src/components/MemoryObservabilityCard.tsx` + - Accepts optional new counters. + - Shows observed/external/ArrayBuffers in details. + - Uses observed-memory drift per hour. +- `gui/src/i18n/{en,ko,de,ja,ru,zh}.ts` + - Adds the new memory labels and updates the hint/growth wording. +- `docs-site/src/content/docs/troubleshooting/windows-memory.md` + - Replaces RSS-only guidance with observed-memory guidance. +- `structure/05_gui-and-management-api.md` + - Updates the SOT endpoint contract. +- `tests/memory-watchdog.test.ts`, `tests/doctor.test.ts`, + `gui/tests/memory-observability-card.test.tsx` + - Regression coverage for #509. + +## Verification plan + +Run: + +```bash +bun test tests/memory-watchdog.test.ts tests/doctor.test.ts +cd gui && bun test tests/memory-observability-card.test.tsx +bun x tsc --noEmit +bun run privacy:scan +``` diff --git a/devlog/_plan/260727_wp8-issue509-heap-watchdog/011_verification.md b/devlog/_plan/260727_wp8-issue509-heap-watchdog/011_verification.md new file mode 100644 index 0000000000..ddeb96d722 --- /dev/null +++ b/devlog/_plan/260727_wp8-issue509-heap-watchdog/011_verification.md @@ -0,0 +1,48 @@ +# 011 — WP8 verification evidence + +## Local verification + +Commands: + +```bash +bun test tests/memory-watchdog.test.ts tests/doctor.test.ts +cd gui && bun test tests/memory-observability-card.test.tsx +bun x tsc --noEmit +cd gui && bun run lint +cd gui && bun run build +bun run privacy:scan +git diff --check +``` + +Results: + +- `bun test tests/memory-watchdog.test.ts tests/doctor.test.ts` + - 36 pass, 0 fail, 143 assertions. +- `cd gui && bun test tests/memory-observability-card.test.tsx` + - 3 pass, 0 fail, 12 assertions. +- `bun x tsc --noEmit` + - exit 0. +- `cd gui && bun run lint` + - exit 0. +- `cd gui && bun run build` + - exit 0; Vite emitted the existing large chunk warning only. +- `bun run privacy:scan` + - Privacy scan passed. +- `git diff --check` + - exit 0. + +## C-gate review + +Independent reviewer verdict: `PASS`. + +Review confirmed: + +- `/api/system/memory` exposes `external`, `arrayBuffers`, `observedBytes`, and + `observedMetric`. +- Observed memory is `max(rss, external, arrayBuffers)`, not a sum. +- Watchdog warning uses observed memory and names the triggering metric. +- `ocx doctor` no longer treats low RSS as normal when external/ArrayBuffers are + high. +- Dashboard Memory observability uses observed drift and renders + observed/external/ArrayBuffers while keeping older-proxy compatibility. +- Docs and SOT use the observed-memory contract. diff --git a/docs-site/src/content/docs/troubleshooting/windows-memory.md b/docs-site/src/content/docs/troubleshooting/windows-memory.md index 71c8fcb765..53f20242de 100644 --- a/docs-site/src/content/docs/troubleshooting/windows-memory.md +++ b/docs-site/src/content/docs/troubleshooting/windows-memory.md @@ -30,24 +30,28 @@ upstream data in native memory that JavaScript cannot bound. Bounded mitigation and visibility — **not a fix**. On the bundled 1.3.14 runtime the leak itself remains an upstream problem: -- **RSS watchdog** — the proxy samples its own memory every minute and logs a - rate-limited warning when RSS crosses 4 GiB. +- **Memory watchdog** — the proxy samples its own memory every minute and logs a + rate-limited warning when observed memory crosses 4 GiB. Observed memory is + the largest of RSS, `external`, and `arrayBuffers` (not their sum), because + Windows working-set/RSS counters can under-report committed external + retention. - **`ocx doctor`** — a "Memory / runtime" section shows the *service* - process's Bun version, RSS, JS-heap share, and stream-mode decision, and - tells you whether growth looks native-side (the upstream issue) or JS-side - (an opencodex bug you should report). + process's Bun version, RSS, external/ArrayBuffers counters, JS-heap context, + and stream-mode decision. On the bundled Bun 1.3.14 runtime, `heapUsed` / + `jscHeap` alone are not a leak discriminator; compare observed memory with + `responseState` and repeated samples before assigning an app-level leak. - **`GET /api/system/memory`** — the same data over the authenticated - management API for dashboards or scripts. Alongside the RSS/heap numbers it - reports a scalar `responseState` block (entry count, total/largest serialized - bytes, oldest-entry age) for the proxy's in-memory `previous_response_id` - continuation store. This further attributes *JS-heap* growth: a rising - `responseState.totalBytes` under a rising heap points at conversation - retention (long `store:false` chains re-expanding each turn), whereas a flat - `responseState` under a rising RSS points back at the native runtime. The - values are scalar-only — no request bodies, tokens, paths, or account - identifiers — and the read is side-effect free (it never prunes or evicts). - The dashboard's read-only **Memory observability** card renders the same - fields. + management API for dashboards or scripts. Alongside RSS/heap/external counters + it reports a scalar `responseState` block (entry count, total/largest + serialized bytes, oldest-entry age) for the proxy's in-memory + `previous_response_id` continuation store. This further attributes growth: a + rising `responseState.totalBytes` under rising observed memory points at + conversation retention (long `store:false` chains re-expanding each turn), + whereas a flat `responseState` under rising observed memory points away from + that store. The values are scalar-only — no request bodies, tokens, paths, or + account identifiers — and the read is side-effect free (it never prunes or + evicts). The dashboard's read-only **Memory observability** card renders the + same fields. - **A gated alternative stream path** — a bounded single-reader relay that removes the unbounded buffering shape entirely. It becomes the default automatically once a bundled Bun release verifiably carries the #32111 fix; diff --git a/gui/src/components/MemoryObservabilityCard.tsx b/gui/src/components/MemoryObservabilityCard.tsx index 39f65b2f55..4cccbeaab3 100644 --- a/gui/src/components/MemoryObservabilityCard.tsx +++ b/gui/src/components/MemoryObservabilityCard.tsx @@ -6,9 +6,10 @@ import { useI18n, type Locale } from "../i18n/shared"; /** * Read-only Memory observability card. Polls GET /api/system/memory (the #314 WP3 * service-process introspection surface) every 5s and renders scalar diagnostics - * only: no sliders, no restart toggle, no PUT. A flat JS heap under a rising RSS - * points at native runtime memory rather than an app-level leak; a rising - * continuation-store total under a rising heap points at conversation retention. + * only: no sliders, no restart toggle, no PUT. Observed memory is the largest + * of RSS, external, and ArrayBuffers so Windows working-set trimming does not + * hide committed retention; a rising continuation-store total under rising + * observed memory points at conversation retention. */ interface MemorySample { @@ -16,8 +17,14 @@ interface MemorySample { rss: number; heapUsed: number; heapTotal: number; + external?: number; + arrayBuffers?: number; + observedBytes?: number; + observedMetric?: MemoryMetric; } +type MemoryMetric = "rss" | "external" | "arrayBuffers"; + interface ResponseState { count: number; totalBytes: number; @@ -29,10 +36,14 @@ interface SystemMemory { rss: number; heapUsed: number; heapTotal: number; + external?: number; + arrayBuffers?: number; + observedBytes?: number; + observedMetric?: MemoryMetric; jscHeap: { heapSize: number; heapCapacity: number; objectCount: number } | null; /** Absent on older proxies whose /api/system/memory predates the continuation-store metrics. */ responseState?: ResponseState; - watchdog: { warnThresholdBytes: number; lastWarnAt: number | null; samples: MemorySample[] } | null; + watchdog: { warnThresholdBytes: number; lastWarnAt: number | null; observedBytes?: number; observedMetric?: MemoryMetric; samples: MemorySample[] } | null; } /** @@ -80,14 +91,30 @@ function formatAge(ms: number, locale: Locale): string { return formatUptime(ms / 1000, locale); } -/** Derive RSS drift per hour from the bounded watchdog ring (never mutates it). */ -function rssGrowthPerHour(samples: MemorySample[]): number | null { +function observedMemory(sample: Pick): number { + if (typeof sample.observedBytes === "number") return sample.observedBytes; + return Math.max(sample.rss, sample.external ?? 0, sample.arrayBuffers ?? 0); +} + +function observedMetric(data: SystemMemory): MemoryMetric { + if (data.observedMetric) return data.observedMetric; + if (data.watchdog?.observedMetric) return data.watchdog.observedMetric; + const values: Array<{ metric: MemoryMetric; bytes: number }> = [ + { metric: "rss", bytes: data.rss }, + { metric: "external", bytes: data.external ?? 0 }, + { metric: "arrayBuffers", bytes: data.arrayBuffers ?? 0 }, + ]; + return values.reduce((best, next) => next.bytes > best.bytes ? next : best, values[0]).metric; +} + +/** Derive observed-memory drift per hour from the bounded watchdog ring (never mutates it). */ +function observedGrowthPerHour(samples: MemorySample[]): number | null { if (samples.length < 2) return null; const first = samples[0]; const last = samples[samples.length - 1]; const spanMs = last.at - first.at; if (spanMs <= 0) return null; - return ((last.rss - first.rss) / spanMs) * 3_600_000; + return ((observedMemory(last) - observedMemory(first)) / spanMs) * 3_600_000; } /** One labelled monospace metric cell inside a stat-row. */ @@ -164,7 +191,9 @@ export default function MemoryObservabilityCard({ apiBase }: { apiBase: string } ); } - const growth = data?.watchdog ? rssGrowthPerHour(data.watchdog.samples) : null; + const growth = data?.watchdog ? observedGrowthPerHour(data.watchdog.samples) : null; + const observedBytes = data ? data.observedBytes ?? data.watchdog?.observedBytes ?? observedMemory(data) : null; + const observedBy = data ? observedMetric(data) : null; // Optional on purpose: a 200 from an older proxy may lack the responseState field. const responseState = data?.responseState; @@ -190,6 +219,13 @@ export default function MemoryObservabilityCard({ apiBase }: { apiBase: string } {t("dash.mem.details")}
{t("dash.mem.hint")}
+
{t("dash.mem.runtime")}
+
+ + + +
+
{t("dash.mem.store")}
{t("dash.mem.storeHint")}
diff --git a/gui/src/i18n/de.ts b/gui/src/i18n/de.ts index 9b7eafad78..c6ee7cf4e9 100644 --- a/gui/src/i18n/de.ts +++ b/gui/src/i18n/de.ts @@ -115,11 +115,15 @@ export const de: Record = { "dash.tokens30d": "Tokens (30d)", "dash.coverage": "{pct} Abdeckung", "dash.mem.title": "Speicherbeobachtung", - "dash.mem.hint": "Schreibgeschützte Laufzeitdiagnose. Ein flacher JS-Heap bei steigendem RSS deutet auf nativen Laufzeitspeicher hin, nicht auf ein Leck auf App-Ebene.", + "dash.mem.hint": "Schreibgeschützte Laufzeitdiagnose. Beobachteter Speicher ist max(RSS, external, ArrayBuffers), damit Windows-Working-Set-Trimming gebundenen Speicher nicht versteckt.", "dash.mem.rss": "Resident Set (RSS)", "dash.mem.jsHeap": "JS-Heap (belegt / gesamt)", "dash.mem.jscHeap": "JSC-Heap", - "dash.mem.growth": "RSS-Drift / Stunde", + "dash.mem.external": "External", + "dash.mem.arrayBuffers": "ArrayBuffers", + "dash.mem.observed": "Beobachtet", + "dash.mem.runtime": "Laufzeit-Zähler", + "dash.mem.growth": "Beobachtete Drift / Stunde", "dash.mem.perHour": "/Std", "dash.mem.store": "Fortsetzungsspeicher", "dash.mem.storeHint": "Proxy-Cache für previous_response_id. Steigende Gesamtbytes bei steigendem Heap deuten auf Konversationsspeicherung hin, nicht auf den Laufzeit-Allokator.", diff --git a/gui/src/i18n/en.ts b/gui/src/i18n/en.ts index 204240657a..245f1140af 100644 --- a/gui/src/i18n/en.ts +++ b/gui/src/i18n/en.ts @@ -125,11 +125,15 @@ export const en = { "dash.coverage": "{pct} coverage", // memory observability card (read-only /api/system/memory) "dash.mem.title": "Memory observability", - "dash.mem.hint": "Read-only runtime diagnostics. A flat JS heap under a rising RSS points at native runtime memory, not an app-level leak.", + "dash.mem.hint": "Read-only runtime diagnostics. Observed memory is max(RSS, external, ArrayBuffers) so Windows working-set trimming does not hide committed retention.", "dash.mem.rss": "Resident set (RSS)", "dash.mem.jsHeap": "JS heap (used / total)", "dash.mem.jscHeap": "JSC heap", - "dash.mem.growth": "RSS drift / hour", + "dash.mem.external": "External", + "dash.mem.arrayBuffers": "ArrayBuffers", + "dash.mem.observed": "Observed", + "dash.mem.runtime": "Runtime counters", + "dash.mem.growth": "Observed drift / hour", "dash.mem.perHour": "/h", "dash.mem.store": "Continuation store", "dash.mem.storeHint": "Proxy previous_response_id cache. Rising total bytes under a rising heap points at conversation retention rather than the runtime allocator.", diff --git a/gui/src/i18n/ja.ts b/gui/src/i18n/ja.ts index 8b1c70a310..6ba7236642 100644 --- a/gui/src/i18n/ja.ts +++ b/gui/src/i18n/ja.ts @@ -124,11 +124,15 @@ export const ja: Record = { "dash.tokens30d": "トークン (30日)", "dash.coverage": "{pct} カバレッジ", "dash.mem.title": "メモリ可観測性", - "dash.mem.hint": "読み取り専用のランタイム診断。RSS が増加する一方で JS ヒープが平坦な場合、アプリレベルのリークではなくネイティブランタイムメモリを示します。", + "dash.mem.hint": "読み取り専用のランタイム診断。観測メモリは max(RSS, external, ArrayBuffers) で、Windows の working set trimming がコミット済み保持を隠さないようにします。", "dash.mem.rss": "常駐メモリ (RSS)", "dash.mem.jsHeap": "JS ヒープ(使用 / 合計)", "dash.mem.jscHeap": "JSC ヒープ", - "dash.mem.growth": "1時間あたりの RSS 変化", + "dash.mem.external": "External", + "dash.mem.arrayBuffers": "ArrayBuffers", + "dash.mem.observed": "観測値", + "dash.mem.runtime": "ランタイムカウンター", + "dash.mem.growth": "1時間あたりの観測変化", "dash.mem.perHour": "/時間", "dash.mem.store": "継続ストア", "dash.mem.storeHint": "プロキシの previous_response_id キャッシュ。ヒープ増加中に合計バイトが増える場合、ランタイムアロケータではなく会話保持を示します。", diff --git a/gui/src/i18n/ko.ts b/gui/src/i18n/ko.ts index de63905598..3fca1a12bc 100644 --- a/gui/src/i18n/ko.ts +++ b/gui/src/i18n/ko.ts @@ -119,11 +119,15 @@ export const ko: Record = { "dash.tokens30d": "토큰 (30일)", "dash.coverage": "커버리지 {pct}", "dash.mem.title": "메모리 관찰", - "dash.mem.hint": "읽기 전용 런타임 진단. RSS는 증가하는데 JS 힙이 평탄하면 앱 수준 누수가 아니라 네이티브 런타임 메모리를 가리킵니다.", + "dash.mem.hint": "읽기 전용 런타임 진단. 관측 메모리는 max(RSS, external, ArrayBuffers)라 Windows working set trimming이 커밋된 보존 메모리를 숨기지 못합니다.", "dash.mem.rss": "상주 메모리 (RSS)", "dash.mem.jsHeap": "JS 힙 (사용 / 전체)", "dash.mem.jscHeap": "JSC 힙", - "dash.mem.growth": "시간당 RSS 변화", + "dash.mem.external": "External", + "dash.mem.arrayBuffers": "ArrayBuffers", + "dash.mem.observed": "관측값", + "dash.mem.runtime": "런타임 카운터", + "dash.mem.growth": "시간당 관측 변화", "dash.mem.perHour": "/시간", "dash.mem.store": "연속 응답 저장소", "dash.mem.storeHint": "프록시 previous_response_id 캐시. 힙이 증가하는 가운데 총 바이트가 늘면 런타임 할당기보다 대화 보존을 가리킵니다.", diff --git a/gui/src/i18n/ru.ts b/gui/src/i18n/ru.ts index 115b3ae43a..efbaf53a5c 100644 --- a/gui/src/i18n/ru.ts +++ b/gui/src/i18n/ru.ts @@ -124,11 +124,15 @@ export const ru: Record = { "dash.tokens30d": "Токены (30 дн.)", "dash.coverage": "{pct} покрытия", "dash.mem.title": "Наблюдение за памятью", - "dash.mem.hint": "Диагностика среды выполнения только для чтения. Плоская куча JS при растущем RSS указывает на нативную память среды, а не на утечку на уровне приложения.", + "dash.mem.hint": "Диагностика среды выполнения только для чтения. Наблюдаемая память — max(RSS, external, ArrayBuffers), чтобы trimming рабочего набора Windows не скрывал удержанную память.", "dash.mem.rss": "Резидентная память (RSS)", "dash.mem.jsHeap": "Куча JS (занято / всего)", "dash.mem.jscHeap": "Куча JSC", - "dash.mem.growth": "Изменение RSS / час", + "dash.mem.external": "External", + "dash.mem.arrayBuffers": "ArrayBuffers", + "dash.mem.observed": "Наблюдаемая", + "dash.mem.runtime": "Счётчики среды", + "dash.mem.growth": "Изменение наблюдаемой / час", "dash.mem.perHour": "/ч", "dash.mem.store": "Хранилище продолжений", "dash.mem.storeHint": "Кэш прокси previous_response_id. Рост общего числа байт при растущей куче указывает на удержание диалогов, а не на аллокатор среды.", diff --git a/gui/src/i18n/zh.ts b/gui/src/i18n/zh.ts index b8695ca65f..fc6aa774fd 100644 --- a/gui/src/i18n/zh.ts +++ b/gui/src/i18n/zh.ts @@ -119,11 +119,15 @@ export const zh: Record = { "dash.tokens30d": "Token (30 天)", "dash.coverage": "覆盖率 {pct}", "dash.mem.title": "内存可观测性", - "dash.mem.hint": "只读运行时诊断。RSS 上升而 JS 堆保持平稳,说明是原生运行时内存,而非应用级泄漏。", + "dash.mem.hint": "只读运行时诊断。观测内存为 max(RSS, external, ArrayBuffers),避免 Windows working set trimming 隐藏已提交的保留内存。", "dash.mem.rss": "常驻内存 (RSS)", "dash.mem.jsHeap": "JS 堆(已用 / 总计)", "dash.mem.jscHeap": "JSC 堆", - "dash.mem.growth": "每小时 RSS 变化", + "dash.mem.external": "External", + "dash.mem.arrayBuffers": "ArrayBuffers", + "dash.mem.observed": "观测值", + "dash.mem.runtime": "运行时计数器", + "dash.mem.growth": "每小时观测变化", "dash.mem.perHour": "/小时", "dash.mem.store": "延续存储", "dash.mem.storeHint": "代理 previous_response_id 缓存。堆上升时总字节数增加,说明是对话保留而非运行时分配器。", diff --git a/gui/tests/memory-observability-card.test.tsx b/gui/tests/memory-observability-card.test.tsx index 279ac75995..b581ed1631 100644 --- a/gui/tests/memory-observability-card.test.tsx +++ b/gui/tests/memory-observability-card.test.tsx @@ -38,7 +38,21 @@ const MEMORY_PAYLOAD = { rss: 1536, heapUsed: 2_097_152, heapTotal: 4_194_304, + external: 8_388_608, + arrayBuffers: 1_048_576, + observedBytes: 8_388_608, + observedMetric: "external", responseState: { count: 3, totalBytes: 5_242_880, largestBytes: 1_048_576, oldestAgeMs: 60_000 }, + watchdog: { + warnThresholdBytes: 4 * 1024 ** 3, + lastWarnAt: null, + observedBytes: 8_388_608, + observedMetric: "external", + samples: [ + { at: 0, rss: 1536, heapUsed: 2_097_152, heapTotal: 4_194_304, external: 4_194_304, arrayBuffers: 1_048_576, observedBytes: 4_194_304, observedMetric: "external" }, + { at: 3_600_000, rss: 1536, heapUsed: 2_097_152, heapTotal: 4_194_304, external: 8_388_608, arrayBuffers: 1_048_576, observedBytes: 8_388_608, observedMetric: "external" }, + ], + }, }; async function mountCard(respond: () => Promise | Response): Promise<{ @@ -85,6 +99,8 @@ test("a healthy payload renders the metrics with binary units", async () => { const text = container.textContent ?? ""; expect(text).toContain("1.5 KiB"); // rss expect(text).toContain("2.0 MiB"); // heapUsed + expect(text).toContain("8.0 MiB (external)"); // observed memory + expect(text).toContain("4.0 MiB/h"); // observed drift/hour expect(text).toContain("5.0 MiB"); // response-store total expect(text).not.toContain("1.5 KB"); // the mislabelled decimal unit must be gone diff --git a/src/cli/doctor.ts b/src/cli/doctor.ts index abbb81682e..f4a28361ad 100644 --- a/src/cli/doctor.ts +++ b/src/cli/doctor.ts @@ -480,10 +480,14 @@ export type ServiceMemoryData = { platform: string; rss: number; heapUsed: number; + external: number; + arrayBuffers: number; + observedBytes?: number; + observedMetric?: MemoryMetric; jscHeap: { heapSize: number } | null; streamMode: string; eagerRelay: { useEagerRelay: boolean; reason: string } | null; - watchdog: { warnThresholdBytes: number; lastWarnAt: number | null } | null; + watchdog: { warnThresholdBytes: number; lastWarnAt: number | null; observedBytes?: number; observedMetric?: MemoryMetric } | null; }; export type ServiceMemoryReport = @@ -493,6 +497,19 @@ export type ServiceMemoryReport = const SERVICE_MEMORY_TIMEOUT_MS = 2000; const DEFAULT_MEMORY_THRESHOLD_BYTES = 4 * 1024 ** 3; +type MemoryMetric = "rss" | "external" | "arrayBuffers"; + +function observedMemory(data: { rss: number; external?: number; arrayBuffers?: number }): { + bytes: number; + metric: MemoryMetric; +} { + const values: Array<{ metric: MemoryMetric; bytes: number }> = [ + { metric: "rss", bytes: data.rss }, + { metric: "external", bytes: data.external ?? 0 }, + { metric: "arrayBuffers", bytes: data.arrayBuffers ?? 0 }, + ]; + return values.reduce((best, next) => next.bytes > best.bytes ? next : best, values[0]); +} export async function fetchServiceMemory( host: string, @@ -519,13 +536,26 @@ export async function fetchServiceMemory( platform: typeof body.platform === "string" ? body.platform : "unknown", rss: body.rss, heapUsed: typeof body.heapUsed === "number" ? body.heapUsed : 0, + external: typeof body.external === "number" ? body.external : 0, + arrayBuffers: typeof body.arrayBuffers === "number" ? body.arrayBuffers : 0, + observedBytes: typeof body.observedBytes === "number" ? body.observedBytes : undefined, + observedMetric: body.observedMetric === "rss" || body.observedMetric === "external" || body.observedMetric === "arrayBuffers" + ? body.observedMetric + : undefined, jscHeap: body.jscHeap && typeof body.jscHeap.heapSize === "number" ? { heapSize: body.jscHeap.heapSize } : null, streamMode: typeof body.streamMode === "string" ? body.streamMode : "auto", eagerRelay: body.eagerRelay && typeof body.eagerRelay.reason === "string" ? { useEagerRelay: body.eagerRelay.useEagerRelay === true, reason: body.eagerRelay.reason } : null, watchdog: body.watchdog && typeof body.watchdog.warnThresholdBytes === "number" - ? { warnThresholdBytes: body.watchdog.warnThresholdBytes, lastWarnAt: body.watchdog.lastWarnAt ?? null } + ? { + warnThresholdBytes: body.watchdog.warnThresholdBytes, + lastWarnAt: body.watchdog.lastWarnAt ?? null, + observedBytes: typeof body.watchdog.observedBytes === "number" ? body.watchdog.observedBytes : undefined, + observedMetric: body.watchdog.observedMetric === "rss" || body.watchdog.observedMetric === "external" || body.watchdog.observedMetric === "arrayBuffers" + ? body.watchdog.observedMetric + : undefined, + } : null, }, }; @@ -550,22 +580,29 @@ export function formatServiceMemoryLines(report: ServiceMemoryReport): string[] } const d = report.data; lines.push(` ok service pid ${d.pid}: Bun ${d.bunVersion} on ${d.platform}`); - lines.push(` rss=${mb(d.rss)}, heapUsed=${mb(d.heapUsed)}${d.jscHeap ? `, jscHeap=${mb(d.jscHeap.heapSize)}` : ""}`); + const observed = observedMemory(d); + const observedBytes = d.observedBytes ?? d.watchdog?.observedBytes ?? observed.bytes; + const observedMetric = d.observedMetric ?? d.watchdog?.observedMetric ?? observed.metric; + lines.push(` rss=${mb(d.rss)}, external=${mb(d.external)}, arrayBuffers=${mb(d.arrayBuffers)}, heapUsed=${mb(d.heapUsed)}${d.jscHeap ? `, jscHeap=${mb(d.jscHeap.heapSize)}` : ""}`); + lines.push(` observed=${mb(observedBytes)} (${observedMetric})`); lines.push(` streamMode=${d.streamMode}${d.eagerRelay ? ` (eager relay: ${d.eagerRelay.useEagerRelay ? "on" : "off"}, ${d.eagerRelay.reason})` : ""}`); if (d.watchdog) { lines.push(` watchdog threshold=${mb(d.watchdog.warnThresholdBytes)}${d.watchdog.lastWarnAt ? `, last warn ${new Date(d.watchdog.lastWarnAt).toISOString()}` : ", no warnings"}`); } - // Interpretation rule (devlog 040): reuse the watchdog's own threshold so - // doctor and watchdog never disagree about "high"; jsShare discriminates - // JS-heap growth from native runtime growth (the #314 shape). + // Interpretation rule: reuse the watchdog threshold and the same max-of + // observed memory counters, so doctor and watchdog never disagree about + // "high". RSS/working-set can under-report committed retention on Windows, and + // Bun 1.3.14 heap counters are not standalone leak proof. const threshold = d.watchdog?.warnThresholdBytes ?? DEFAULT_MEMORY_THRESHOLD_BYTES; const jsShare = d.rss > 0 ? Math.max(d.heapUsed, d.jscHeap?.heapSize ?? 0) / d.rss : 0; - if (d.rss < threshold) { + if (observedBytes < threshold) { lines.push(" memory usage looks normal"); + } else if (observedMetric !== "rss") { + lines.push(` !! high observed memory via ${observedMetric}; Windows RSS/working-set counters may be blind. See docs: troubleshooting/windows-memory`); } else if (jsShare < 0.25) { lines.push(" !! high RSS with a small JS heap — native-side growth (Bun runtime buffers/handles). See docs: troubleshooting/windows-memory"); } else if (jsShare >= 0.5) { - lines.push(" !! high RSS dominated by the JS heap — likely an opencodex bug; please report it"); + lines.push(" !! high RSS with large JS/JSC counters — possible JS-side retention; compare responseState/external samples before filing an app leak"); } else { lines.push(" !! high RSS, indeterminate split — capture two doctor runs over time to see the trend"); } diff --git a/src/server/management/system-routes.ts b/src/server/management/system-routes.ts index 277ec809d0..992cbc654d 100644 --- a/src/server/management/system-routes.ts +++ b/src/server/management/system-routes.ts @@ -7,15 +7,16 @@ * unauthenticated /healthz surface. * * The payload is scalar-only (numbers, enum strings): no paths, no tokens, no - * account identifiers. `jscHeap` (bun:jsc heapStats) is the js-vs-native - * discriminator: a flat JS heap under a growing RSS points at native runtime - * memory (the #314 shape), not an app-level JS leak. `responseState` attributes - * JS-heap growth further: it is the proxy's previous_response_id continuation - * store, so a growing responseState.totalBytes under a growing heap points the - * finger at conversation retention rather than the runtime allocator. + * account identifiers. `external` and `arrayBuffers` keep Windows diagnostics + * honest when RSS/working-set counters under-report committed retention. + * `jscHeap` (bun:jsc heapStats) is useful context, but on Bun 1.3.14 it is not a + * standalone leak discriminator. `responseState` attributes growth further: it + * is the proxy's previous_response_id continuation store, so a growing + * responseState.totalBytes under rising observed memory points at conversation + * retention rather than the runtime allocator. */ import { decideEagerRelay } from "../../lib/bun-stream-caps"; -import { getActiveMemoryWatchdog } from "../memory-watchdog"; +import { getActiveMemoryWatchdog, observedMemoryCounter } from "../memory-watchdog"; import { responseStateMetrics } from "../../responses/state"; import { jsonResponse } from "../auth-cors"; import type { ManagementContext } from "./context"; @@ -38,15 +39,22 @@ export async function handleSystemRoutes(ctx: ManagementContext): Promise { const snap = watchdogInstance.snapshot(); - return { - warnThresholdBytes: snap.warnThresholdBytes, - lastWarnAt: snap.lastWarnAt, - samples: snap.samples.slice(-ENDPOINT_SAMPLE_LIMIT), - }; + return { + warnThresholdBytes: snap.warnThresholdBytes, + lastWarnAt: snap.lastWarnAt, + observedBytes: snap.observedBytes, + observedMetric: snap.observedMetric, + samples: snap.samples.slice(-ENDPOINT_SAMPLE_LIMIT), + }; })() : null; const streamMode = config.streamMode ?? "auto"; @@ -58,8 +66,12 @@ export async function handleSystemRoutes(ctx: ManagementContext): Promise): { + observedBytes: number; + observedMetric: MemoryMetric; +} { + const values: Array<{ metric: MemoryMetric; bytes: number }> = [ + { metric: "rss", bytes: sample.rss }, + { metric: "external", bytes: sample.external }, + { metric: "arrayBuffers", bytes: sample.arrayBuffers }, + ]; + const best = values.reduce((current, next) => next.bytes > current.bytes ? next : current, values[0]); + return { observedBytes: best.bytes, observedMetric: best.metric }; +} + /** The running watchdog, if any — read by /api/system/memory. */ export function getActiveMemoryWatchdog(): MemoryWatchdog | null { return active; @@ -50,7 +78,19 @@ export function getActiveMemoryWatchdog(): MemoryWatchdog | null { function defaultSample(now: () => number): MemorySample { const usage = process.memoryUsage(); - return { at: now(), rss: usage.rss, heapUsed: usage.heapUsed, heapTotal: usage.heapTotal }; + const base = { + at: now(), + rss: usage.rss, + heapUsed: usage.heapUsed, + heapTotal: usage.heapTotal, + external: usage.external, + arrayBuffers: usage.arrayBuffers, + }; + return { ...base, ...observedMemoryCounter(base) }; +} + +function normalizeSample(sample: MemorySampleBase): MemorySample { + return { ...sample, ...observedMemoryCounter(sample) }; } /** @@ -64,7 +104,7 @@ export function startMemoryWatchdog(opts?: { warnThresholdBytes?: number; ringSize?: number; now?: () => number; - sample?: () => MemorySample; + sample?: () => MemorySampleBase; warn?: (msg: string) => void; }): MemoryWatchdog { active?.stop(); @@ -77,21 +117,25 @@ export function startMemoryWatchdog(opts?: { const samples: MemorySample[] = []; let lastWarnAt: number | null = null; + let observedBytes = 0; + let observedMetric: MemoryMetric = "rss"; const tick = () => { let s: MemorySample; try { - s = sample(); + s = normalizeSample(sample()); } catch { return; // sampling must never break the server } samples.push(s); if (samples.length > ringSize) samples.splice(0, samples.length - ringSize); - if (s.rss >= warnThresholdBytes && (lastWarnAt === null || now() - lastWarnAt >= WARN_INTERVAL_MS)) { + observedBytes = s.observedBytes; + observedMetric = s.observedMetric; + if (s.observedBytes >= warnThresholdBytes && (lastWarnAt === null || now() - lastWarnAt >= WARN_INTERVAL_MS)) { lastWarnAt = now(); - const rssMb = Math.round(s.rss / (1024 * 1024)); + const observedMb = Math.round(s.observedBytes / (1024 * 1024)); const thresholdMb = Math.round(warnThresholdBytes / (1024 * 1024)); - warn(`⚠️ opencodex RSS ${rssMb}MB exceeds the ${thresholdMb}MB watch threshold. On Windows this is usually the upstream Bun runtime memory issue — see ${DOCS_URL}`); + warn(`⚠️ opencodex observed memory ${observedMb}MB (${s.observedMetric}) exceeds the ${thresholdMb}MB watch threshold. On Windows this is usually the upstream Bun runtime memory issue — see ${DOCS_URL}`); } }; @@ -104,7 +148,7 @@ export function startMemoryWatchdog(opts?: { if (active === instance) active = null; }, snapshot() { - return { samples: [...samples], warnThresholdBytes, lastWarnAt }; + return { samples: [...samples], warnThresholdBytes, lastWarnAt, observedBytes, observedMetric }; }, }; active = instance; diff --git a/structure/05_gui-and-management-api.md b/structure/05_gui-and-management-api.md index cce18d60fd..eda458eee6 100644 --- a/structure/05_gui-and-management-api.md +++ b/structure/05_gui-and-management-api.md @@ -24,7 +24,7 @@ starts the proxy when needed and opens `http://localhost:`. | V2 / Multi-agent mode | `GET/PUT /api/v2` — reports/sets the codex `multi_agent_v2` feature flag, the 3-state `multiAgentMode` override (`v1`/`default`/`v2`), and the logical maximum thread count. Selecting `v2` enables the native flag and migrates `[agents] max_threads` to the v2 key; selecting `v1` disables it and migrates the same value back. `default` leaves the native flag unchanged. PUT accepts `enabled`, `multiAgentMode`, and/or the compatibility-named `maxConcurrentThreadsPerSession`; contradictory mode/flag pairs are rejected before writes. Every transition is rollback-safe and resyncs the catalog. | | Logs & Debug | One sidebar entry (`/#logs`) with two tabs. Logs tab: request/runtime logs for local diagnosis. Debug tab (`/#logs/debug`; legacy `/#debug` deep links redirect there): provider + usage toggles, refresh/follow log viewer. `GET/PUT /api/debug`; `GET /api/debug/logs` and `GET /api/debug/usage-logs` (monotonic `after` cursor, legacy `since` accepted). CLI: `ocx debug provider|usage …` (both streams via running proxy API). | | Usage | `GET /api/usage` aggregate read-only summary derived from `~/.opencodex/usage.jsonl`; measured / reported / unreported / unsupported / estimated counts, daily zero-filled grid, model and provider breakdowns. Never exposes prompts. | -| System | `GET /api/system/memory` — service-process runtime/memory identity (pid, Bun version/revision, platform, RSS/heap scalars, `bun:jsc` heap discriminator, streamMode + eager-relay gate decision, watchdog snapshot sliced to the last 60 samples). Scalar-only payload; rides the standard management auth gate and must never move to unauthenticated `/healthz`. Consumed by `ocx doctor`'s Memory/runtime section. | +| System | `GET /api/system/memory` — service-process runtime/memory identity (pid, Bun version/revision, platform, RSS/heap/external/ArrayBuffers scalars, observed memory = max(RSS, external, ArrayBuffers), `bun:jsc` heap context, streamMode + eager-relay gate decision, watchdog snapshot sliced to the last 60 samples). Scalar-only payload; rides the standard management auth gate and must never move to unauthenticated `/healthz`. Consumed by `ocx doctor`'s Memory/runtime section and the dashboard Memory observability card. | | Stop | `POST /api/stop` — restore native Codex, stop any installed service, and exit the proxy. | Provider writes must not round-trip masked API keys as real secrets. Dashboard actions that change diff --git a/tests/doctor.test.ts b/tests/doctor.test.ts index 5b490b3531..f5df6a7090 100644 --- a/tests/doctor.test.ts +++ b/tests/doctor.test.ts @@ -329,6 +329,8 @@ describe("service memory section (#314 WP4)", () => { platform: "win32", rss: 5 * 1024 ** 3, heapUsed: 200 * 1024 ** 2, + external: 300 * 1024 ** 2, + arrayBuffers: 200 * 1024 ** 2, jscHeap: { heapSize: 180 * 1024 ** 2 }, streamMode: "auto", eagerRelay: { useEagerRelay: false, reason: "auto-known-bad" }, @@ -366,15 +368,16 @@ describe("service memory section (#314 WP4)", () => { expect(lines.some(l => l.includes("native-side growth"))).toBe(true); }); - test("interpretation: high RSS dominated by JS heap → bug-report line", () => { + test("interpretation: high RSS with large JS counters asks for corroboration", () => { const lines = formatServiceMemoryLines({ status: "ok", data: { ...baseData, heapUsed: 4 * 1024 ** 3, jscHeap: { heapSize: 4 * 1024 ** 3 } }, }); - expect(lines.some(l => l.includes("likely an opencodex bug"))).toBe(true); + expect(lines.some(l => l.includes("possible JS-side retention"))).toBe(true); + expect(lines.some(l => l.includes("likely an opencodex bug"))).toBe(false); }); - test("interpretation: rss below threshold → normal line", () => { + test("interpretation: all observed counters below threshold → normal line", () => { const lines = formatServiceMemoryLines({ status: "ok", data: { ...baseData, rss: 300 * 1024 ** 2 }, @@ -383,6 +386,21 @@ describe("service memory section (#314 WP4)", () => { expect(lines.some(l => l.includes("native-side growth"))).toBe(false); }); + test("interpretation: high external memory is not hidden by low RSS (#509)", () => { + const lines = formatServiceMemoryLines({ + status: "ok", + data: { + ...baseData, + rss: 300 * 1024 ** 2, + external: 5 * 1024 ** 3, + arrayBuffers: 2 * 1024 ** 3, + }, + }); + expect(lines.some(l => l.includes("observed=5120MB (external)"))).toBe(true); + expect(lines.some(l => l.includes("high observed memory via external"))).toBe(true); + expect(lines.some(l => l.includes("looks normal"))).toBe(false); + }); + test("guidance gating: win32 + auto-known-bad prints version-claiming guidance", () => { const lines = formatServiceMemoryLines({ status: "ok", data: baseData }); expect(lines.some(l => l.includes("OPENCODEX_BUN_PATH"))).toBe(true); diff --git a/tests/memory-watchdog.test.ts b/tests/memory-watchdog.test.ts index 6f60b5c262..f5c3fd0eea 100644 --- a/tests/memory-watchdog.test.ts +++ b/tests/memory-watchdog.test.ts @@ -5,8 +5,9 @@ import { afterEach, describe, expect, test } from "bun:test"; import { getActiveMemoryWatchdog, + observedMemoryCounter, startMemoryWatchdog, - type MemorySample, + type MemorySampleBase, } from "../src/server/memory-watchdog"; import { handleManagementAPI } from "../src/server/management-api"; import type { OcxConfig } from "../src/types"; @@ -30,8 +31,15 @@ afterEach(() => { getActiveMemoryWatchdog()?.stop(); }); -function sampleAt(at: number, rssMb: number): MemorySample { - return { at, rss: rssMb * 1024 * 1024, heapUsed: 1000, heapTotal: 2000 }; +function sampleAt(at: number, rssMb: number, externalMb = 1, arrayBuffersMb = 1): MemorySampleBase { + return { + at, + rss: rssMb * 1024 * 1024, + heapUsed: 1000, + heapTotal: 2000, + external: externalMb * 1024 * 1024, + arrayBuffers: arrayBuffersMb * 1024 * 1024, + }; } describe("startMemoryWatchdog", () => { @@ -64,13 +72,61 @@ describe("startMemoryWatchdog", () => { }); await new Promise(resolve => setTimeout(resolve, 25)); expect(warns.length).toBe(1); - expect(warns[0]).toContain("600MB"); + expect(warns[0]).toContain("observed memory 600MB (rss)"); expect(warns[0]).toContain("500MB"); // No paths/hostnames in the warn line. expect(warns[0]).not.toContain("/Users/"); expect(warns[0]).not.toContain("C:\\"); }); + test("threshold warn uses external and ArrayBuffers when RSS is below threshold (#509)", async () => { + const warns: string[] = []; + let t = 0; + startMemoryWatchdog({ + intervalMs: 1, + warnThresholdBytes: 500 * 1024 * 1024, + now: () => t, + sample: () => sampleAt((t += 1), 100, 600, 300), + warn: msg => warns.push(msg), + }); + await new Promise(resolve => setTimeout(resolve, 25)); + expect(warns.length).toBe(1); + expect(warns[0]).toContain("observed memory 600MB (external)"); + + const snap = getActiveMemoryWatchdog()!.snapshot(); + expect(snap.observedMetric).toBe("external"); + expect(snap.observedBytes).toBe(600 * 1024 * 1024); + + getActiveMemoryWatchdog()?.stop(); + warns.length = 0; + t = 0; + startMemoryWatchdog({ + intervalMs: 1, + warnThresholdBytes: 500 * 1024 * 1024, + now: () => t, + sample: () => sampleAt((t += 1), 100, 300, 700), + warn: msg => warns.push(msg), + }); + await new Promise(resolve => setTimeout(resolve, 25)); + expect(warns.length).toBe(1); + expect(warns[0]).toContain("observed memory 700MB (arrayBuffers)"); + }); + + test("observedMemoryCounter uses max, not a sum", () => { + expect(observedMemoryCounter(sampleAt(1, 100, 90, 80))).toEqual({ + observedBytes: 100 * 1024 * 1024, + observedMetric: "rss", + }); + expect(observedMemoryCounter(sampleAt(1, 10, 100, 90))).toEqual({ + observedBytes: 100 * 1024 * 1024, + observedMetric: "external", + }); + expect(observedMemoryCounter(sampleAt(1, 10, 90, 100))).toEqual({ + observedBytes: 100 * 1024 * 1024, + observedMetric: "arrayBuffers", + }); + }); + test("below-threshold samples never warn", async () => { const warns: string[] = []; let t = 0; @@ -117,18 +173,23 @@ describe("GET /api/system/memory", () => { const res = await handleManagementAPI(req, new URL(req.url), config()); expect(res).not.toBeNull(); expect(res!.status).toBe(200); - const body = await res!.json() as { - pid: number; bunVersion: string; platform: string; rss: number; - heapUsed: number; jscHeap: { heapSize: number } | null; - responseState: { count: number; totalBytes: number; largestBytes: number; oldestAgeMs: number }; - streamMode: string; eagerRelay: unknown; - watchdog: { samples: unknown[]; warnThresholdBytes: number } | null; - }; + const body = await res!.json() as { + pid: number; bunVersion: string; platform: string; rss: number; + heapUsed: number; external: number; arrayBuffers: number; observedBytes: number; observedMetric: string; + jscHeap: { heapSize: number } | null; + responseState: { count: number; totalBytes: number; largestBytes: number; oldestAgeMs: number }; + streamMode: string; eagerRelay: unknown; + watchdog: { samples: unknown[]; warnThresholdBytes: number; observedBytes: number; observedMetric: string } | null; + }; expect(body.pid).toBe(process.pid); expect(body.bunVersion).toBe(Bun.version); - expect(body.rss).toBeGreaterThan(0); - expect(body.heapUsed).toBeGreaterThan(0); - expect(body.jscHeap?.heapSize).toBeGreaterThan(0); + expect(body.rss).toBeGreaterThan(0); + expect(body.heapUsed).toBeGreaterThan(0); + expect(body.external).toBeGreaterThanOrEqual(0); + expect(body.arrayBuffers).toBeGreaterThanOrEqual(0); + expect(body.observedBytes).toBeGreaterThan(0); + expect(["rss", "external", "arrayBuffers"]).toContain(body.observedMetric); + expect(body.jscHeap?.heapSize).toBeGreaterThan(0); // responseState is a scalar-only continuation-store attribution block: every field is a // finite number (no paths, tokens, or account identifiers), so it is safe on this surface. expect(typeof body.responseState.count).toBe("number"); @@ -140,9 +201,11 @@ describe("GET /api/system/memory", () => { // Non-win32 test runners report no gate decision; win32 reports one. if (process.platform === "win32") expect(body.eagerRelay).not.toBeNull(); else expect(body.eagerRelay).toBeNull(); - expect(body.watchdog).not.toBeNull(); - expect(body.watchdog!.samples.length).toBeLessThanOrEqual(60); - }); + expect(body.watchdog).not.toBeNull(); + expect(body.watchdog!.samples.length).toBeLessThanOrEqual(60); + expect(typeof body.watchdog!.observedBytes).toBe("number"); + expect(["rss", "external", "arrayBuffers"]).toContain(body.watchdog!.observedMetric); + }); test("watchdog null when no instance is running", async () => { getActiveMemoryWatchdog()?.stop();