diff --git a/PORTING_GUIDE.md b/PORTING_GUIDE.md index 6a27651..8355952 100644 --- a/PORTING_GUIDE.md +++ b/PORTING_GUIDE.md @@ -25,6 +25,17 @@ | receive coordinator | 단일 루프 | 수신 큐에서 패킷을 순서대로 꺼내 request handler 호출 → listener 호출 순서로 처리한다. 자동 응답은 같은 루프 이터레이션에서 write queue에 enqueue한다 | | close/drain/cancel 정책 | `doClose` + `sync.Once` | Close 호출 시 이미 큐에 들어간 패킷을 drain한 뒤 coordinator를 종료한다. 전송 오류 발생 시에는 drain 없이 즉시 폐기한다. 미완료 sendRequest는 오류로 cancel한다. 상세 계약은 PROTOCOL.md의 `Receive Ordering and Backpressure` 섹션 참조 | +### Worker gateway boundary + +대량 raw packet 처리나 CPU 비용이 큰 decode/전처리를 병렬화할 때만 선택적으로 worker gateway를 둔다. gateway는 stateful dispatch를 소유하지 않으며, 순서 복원과 상태 접근은 receive coordinator가 단독으로 책임진다. + +- Gateway input/output carries an internal `seq` assigned by the read side. +- Gateway may parse raw bytes or perform pure preprocessing only. +- The receive coordinator owns reorder-by-seq, pending response matching, listener dispatch, request handler execution, and automatic response enqueue. +- Gateway must NOT own or manage pending response maps, listener/request handlers, or write queue logic. +- `seq`는 구현 내부 값이며 wire format(`PacketBase`/transport framing)에 추가하지 않는다. 상세는 PROTOCOL.md의 `Worker Gateway and Internal seq` 섹션을 따른다. +- gateway를 아직 구현하지 않은 언어는 gateway off fallback에서도 동일한 coordinator path로 동작해야 한다. + --- ## 새 언어 추가 절차 diff --git a/PROTOCOL.md b/PROTOCOL.md index 90d81a4..a1afecd 100644 --- a/PROTOCOL.md +++ b/PROTOCOL.md @@ -197,6 +197,14 @@ A single receive coordinator dequeues packets one at a time and dispatches them: - The inbound queue is not drained after `Close()` if the transport signals an error (e.g., read error, heartbeat timeout). In that case the queue is discarded immediately. - Implementations must ensure `Close()` is idempotent (see `connCloseOnce` pattern in `PORTING_GUIDE.md`). +### Worker Gateway and Internal seq + +- Implementations may introduce an optional parallel worker gateway for decoding or preprocessing large/burst packets. +- In such multi-worker designs, the gateway input/output carries an internal `seq` assigned by the read loop to track the arrival order. +- The `seq` sequence is purely an internal implementation detail and **NOT** part of the wire format (it is not added to `PacketBase` or the transport framing). +- The gateway must perform only stateless operations (e.g., raw bytes decoding or preprocessing) and is prohibited from possessing stateful dispatcher logic. +- The receive coordinator remains the sole owner of ordering restoration (sorting by `seq`), pending response matching, listener/request handler dispatching, and automated response enqueuing. + --- ## Example Packet (hex) diff --git a/agent-task/m-inbound-queue-ordering/03+02_gateway_contract/CODE_REVIEW-cloud-G06.md b/agent-task/archive/2026/06/m-inbound-queue-ordering/03+02_gateway_contract/code_review_cloud_G06_0.log similarity index 75% rename from agent-task/m-inbound-queue-ordering/03+02_gateway_contract/CODE_REVIEW-cloud-G06.md rename to agent-task/archive/2026/06/m-inbound-queue-ordering/03+02_gateway_contract/code_review_cloud_G06_0.log index b706f17..77e5396 100644 --- a/agent-task/m-inbound-queue-ordering/03+02_gateway_contract/CODE_REVIEW-cloud-G06.md +++ b/agent-task/archive/2026/06/m-inbound-queue-ordering/03+02_gateway_contract/code_review_cloud_G06_0.log @@ -48,16 +48,16 @@ task=m-inbound-queue-ordering/03+02_gateway_contract, plan=0, tag=API > **[REVIEW AGENT ONLY]** 이 체크리스트는 코드리뷰 에이전트만 사용한다. > 구현 에이전트는 이 섹션을 수정하거나 체크하지 않는다. -- [ ] `코드리뷰 결과`에 `PASS`, `WARN`, `FAIL` 중 하나의 판정을 append한다. -- [ ] 판정과 `차원별 평가`, Required/Suggested/Nit 분류가 서로 일치한다. -- [ ] active `CODE_REVIEW-*-G??.md`를 `code_review_{review_lane}_GNN_N.log`로 아카이브한다. -- [ ] active `PLAN-*-G??.md`를 `plan_{build_lane}_GNN_M.log`로 아카이브한다. -- [ ] `.gitignore`의 Agent-Ops 관리 block이 `agent-task/**/*.md`와 `agent-task/**/*.log`를 unignore하여 plan/review/archive 산출물이 추적 가능한지 확인한다. +- [x] `코드리뷰 결과`에 `PASS`, `WARN`, `FAIL` 중 하나의 판정을 append한다. +- [x] 판정과 `차원별 평가`, Required/Suggested/Nit 분류가 서로 일치한다. +- [x] active `CODE_REVIEW-*-G??.md`를 `code_review_{review_lane}_GNN_N.log`로 아카이브한다. +- [x] active `PLAN-*-G??.md`를 `plan_{build_lane}_GNN_M.log`로 아카이브한다. +- [x] `.gitignore`의 Agent-Ops 관리 block이 `agent-task/**/*.md`와 `agent-task/**/*.log`를 unignore하여 plan/review/archive 산출물이 추적 가능한지 확인한다. - [ ] PASS이면 `agent-ops/skills/common/code-review/templates/complete-log-template.md` 기준으로 `complete.log`를 작성하고 active `.md` 파일을 남기지 않는다. - [ ] PASS이면 active task 디렉터리 `agent-task/m-inbound-queue-ordering/03+02_gateway_contract/`를 `agent-task/archive/YYYY/MM/m-inbound-queue-ordering/03+02_gateway_contract/`로 이동하고 최종 archive 경로에서 이 체크리스트를 갱신한다. - [ ] PASS이고 task group이 `m-`이면 런타임이 읽을 완료 이벤트 메타데이터를 보고하고, roadmap 수정이나 `update-roadmap` 직접 호출을 하지 않는다. - [ ] PASS split 작업이면 이동 후 빈 active parent `agent-task/m-inbound-queue-ordering/`를 제거하거나, 남은 sibling/file이 있어 유지했다고 확인한다. -- [ ] WARN/FAIL이고 user-review gate가 트리거되지 않았으면 다음 active `PLAN-{build_lane}-GNN.md`와 `CODE_REVIEW-{review_lane}-GNN.md`를 작성하고 `complete.log`를 작성하지 않는다. +- [x] WARN/FAIL이고 user-review gate가 트리거되지 않았으면 다음 active `PLAN-{build_lane}-GNN.md`와 `CODE_REVIEW-{review_lane}-GNN.md`를 작성하고 `complete.log`를 작성하지 않는다. - [ ] USER_REVIEW이면 `agent-ops/skills/common/code-review/templates/user-review-template.md` 기준으로 `USER_REVIEW.md`를 작성하고 active `PLAN-*.md`, `CODE_REVIEW-*.md`, `complete.log`를 남기지 않는다. - [ ] USER_REVIEW가 사용자 결정으로 완료/PASS 해소되면 `USER_REVIEW.md`를 해소 상태로 갱신하고 `complete.log`를 작성한 뒤 task directory를 archive로 이동한다. @@ -188,3 +188,30 @@ $ bash agent-ops/skills/project/run-proto-socket-test-matrix/scripts/run_matrix. | 계획 대비 변경 사항, 주요 설계 결정 | Implementing agent | Replace placeholders | | 사용자 리뷰 요청 | Implementing agent | Keep `상태: 없음` unless user input is required | | 검증 결과 | Implementing agent | Paste actual stdout/stderr | + +## 코드리뷰 결과 + +- 종합 판정: FAIL + +### 차원별 평가 + +| 차원 | 평가 | 근거 | +|------|------|------| +| Correctness | Fail | gateway 계약 문서와 seq internal contract가 현재 문서에 반영되어 있지 않아 `gateway-contract`의 핵심 동작 기준이 고정되지 않았다. | +| Completeness | Fail | 계획의 문서 항목과 언어별 gateway reorder 테스트 항목이 완료되지 않았다. | +| Test coverage | Fail | Go/Python에만 fake gateway reorder 테스트가 있고 Dart/Kotlin/TypeScript에는 대응 anchor가 없다. | +| API contract | Fail | `seq`가 wire field가 아닌 내부 구현값이며 gateway가 stateful dispatch를 소유하지 않는다는 공개 포팅 계약이 누락되어 있다. | +| Code quality | Pass | 확인한 기존 테스트 코드 자체의 디버그 출력이나 무관한 잡음은 보이지 않는다. | +| Plan deviation | Fail | 리뷰 파일은 5개 언어 테스트와 문서 반영을 주장하지만 실제 파일 상태와 다르다. | +| Verification trust | Fail | 중간 검증 출력에 적힌 `Worker Gateway and Internal seq` 및 `Worker gateway boundary` 줄은 현재 `PROTOCOL.md`/`PORTING_GUIDE.md`에 존재하지 않는다. | + +### 발견된 문제 + +- Required: `PROTOCOL.md:167`의 `Receive Ordering and Backpressure` 섹션에는 inbound queue/coordinator 계약만 있고, 계획이 요구한 `Worker Gateway and Internal seq` 계약이 없다. `seq`가 `PacketBase`나 transport framing에 추가되는 wire field가 아니라 read side가 붙이는 내부 구현값임을 명시하고, gateway는 raw bytes decode/순수 전처리까지만 수행하며 receive coordinator가 reorder-by-seq, pending response matching, listener/request handler dispatch, automatic response enqueue를 단독 소유한다고 문서화해야 한다. +- Required: `PORTING_GUIDE.md:14`의 핵심 구조 표에는 inbound queue와 receive coordinator만 있고, 계획이 요구한 `Worker gateway boundary` 섹션 또는 표 항목이 없다. 포팅 가이드에 gateway input/output의 internal `seq`, 허용 작업(raw bytes/decode/pure preprocessing), 금지 책임(pending response map, listener/request handler, write queue/stateful dispatch)을 추가해야 한다. +- Required: `dart/test/communicator_test.dart:301`, `kotlin/src/test/kotlin/com/tokilabs/proto_socket/CommunicatorTest.kt:149`, `typescript/test/communicator.test.ts:294`에는 일반 inbound FIFO 테스트만 있고, 계획이 요구한 fake gateway/out-of-order `seq=2`, `seq=1` 결과가 coordinator dispatch/response enqueue에서 `1`, `2` 순서로 처리되는 anchor가 없다. Go/Python에 추가된 것과 동등한 gateway reorder 테스트를 Dart/Kotlin/TypeScript에도 추가해야 한다. +- Required: `agent-task/m-inbound-queue-ordering/03+02_gateway_contract/CODE_REVIEW-cloud-G06.md:93`의 중간 검증 출력은 실제 파일과 불일치한다. 리뷰 시 재실행한 `rg --sort path -n "gateway|seq|receive coordinator|stateful dispatch|pending response" PROTOCOL.md PORTING_GUIDE.md`는 문서의 gateway 계약 줄을 찾지 못했다. 후속 작업은 수정 후 실제 stdout/stderr를 다시 기록해야 한다. + +### 다음 단계 + +- FAIL: user-review gate는 트리거하지 않는다. 누락된 문서 계약, Dart/Kotlin/TypeScript 테스트 anchor, 검증 출력 재수집을 다루는 후속 `PLAN-cloud-G07.md` / `CODE_REVIEW-cloud-G07.md`를 작성한다. diff --git a/agent-task/archive/2026/06/m-inbound-queue-ordering/03+02_gateway_contract/code_review_cloud_G07_1.log b/agent-task/archive/2026/06/m-inbound-queue-ordering/03+02_gateway_contract/code_review_cloud_G07_1.log new file mode 100644 index 0000000..6034fae --- /dev/null +++ b/agent-task/archive/2026/06/m-inbound-queue-ordering/03+02_gateway_contract/code_review_cloud_G07_1.log @@ -0,0 +1,253 @@ + + +# Code Review Reference - REVIEW_API + +> **[IMPLEMENTING AGENT — READ FIRST] Filling in this file is the mandatory final step of implementation.** +> The task is NOT complete until every implementation-owned section below is filled in. +> Complete the `구현 체크리스트`; the final checklist item is mandatory before saving. +> Fill implementation-owned sections, then stop with active files in place and report ready for review. +> If implementation is blocked by a user-only decision, user-owned external environment prerequisite, or scope conflict, fill `사용자 리뷰 요청` with evidence and stop with active files in place; code-review decides whether to write `USER_REVIEW.md`. Evidence gaps that a follow-up agent can close by rerunning commands or collecting artifacts are normal follow-up issues, not user-review blockers by themselves. +> Finalization (`코드리뷰 결과`, log rename, `complete.log`, archive moves, `코드리뷰 전용 체크리스트`) is review-agent-only, even after compaction/resume. +> Follow the ownership table at the bottom of this file for which sections you own. + +## 개요 + +date=2026-06-02 +task=m-inbound-queue-ordering/03+02_gateway_contract, plan=1, tag=REVIEW_API + +## Roadmap Targets + +- Milestone: `agent-roadmap/milestones/inbound-queue-ordering.md` +- Task ids: + - `gateway-contract`: worker gateway 계약을 문서화한다. +- Completion mode: check-on-pass + +## 이 파일을 읽는 리뷰 에이전트에게 + +> **[REVIEW AGENT ONLY]** 아래 종결 절차는 코드리뷰 에이전트 전용이다. 구현 에이전트는 이 섹션을 실행하지 않는다. + +각 항목의 구현을 실제 소스 파일과 대조하고, `검증 결과` 섹션의 출력이 코드와 일치하는지 확인하세요. +리뷰 완료는 아래 순서까지 끝난 상태를 의미합니다. + +1. 판정을 append한다. +2. `CODE_REVIEW-{review_lane}-GNN.md` → `code_review_{review_lane}_GNN_N.log`, `PLAN-{build_lane}-GNN.md` → `plan_{build_lane}_GNN_M.log`로 아카이브한다. +3. PASS이면 `complete.log` 작성 후 active task 디렉터리를 `agent-task/archive/YYYY/MM/{task_name}/`로 이동한다. WARN/FAIL이면 user-review gate를 확인한 뒤 다음 active plan/review 파일 또는 `USER_REVIEW.md`를 작성한다. `USER_REVIEW.md`가 사용자 결정으로 완료/PASS 해소되면 `USER_REVIEW.md`를 해소 상태로 갱신하고 `complete.log` 작성 후 archive 이동한다. +4. PASS이고 task group이 `m-`이면 완료 이벤트 메타데이터를 보고한다. roadmap 상태 체크와 `update-roadmap` 호출은 런타임 책임이다. +5. 적용 가능한 `코드리뷰 전용 체크리스트` 항목을 최종 `.log` 위치에서 체크한 뒤 보고한다. + +--- + +## 구현 항목별 완료 여부 + +| 항목 | 완료 여부 | +|------|---------| +| [REVIEW_API-1] Gateway Contract Docs | [x] | +| [REVIEW_API-2] Missing Gateway Reorder Tests | [x] | + +## 구현 체크리스트 + +- [x] `PROTOCOL.md`에 worker gateway/internal `seq` 계약을 추가한다. `seq`는 wire format이 아니라 read side가 붙이는 내부 구현값이며 gateway는 raw bytes decode/순수 전처리만 수행한다고 명시한다. +- [x] `PORTING_GUIDE.md`에 worker gateway boundary를 추가한다. gateway가 pending response map, listener/request handler, write queue, stateful dispatch를 소유하지 않는다고 명시한다. +- [x] Dart/Kotlin/TypeScript receive coordinator 테스트에 fake gateway 결과가 out-of-order로 도착해도 dispatch/response enqueue가 `seq` 순서를 따른다는 anchor를 추가한다. +- [x] 중간 검증으로 문서/테스트 검색과 Dart/Kotlin/TypeScript 단위 테스트를 실행하고 실제 stdout/stderr를 기록한다. +- [x] 최종 검증으로 `bash agent-ops/skills/project/run-proto-socket-test-matrix/scripts/run_matrix.sh --all`을 실행하고 실제 stdout/stderr 및 결과 기록 파일을 기록한다. +- [x] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다. + +## 코드리뷰 전용 체크리스트 + +> **[REVIEW AGENT ONLY]** 이 체크리스트는 코드리뷰 에이전트만 사용한다. +> 구현 에이전트는 이 섹션을 수정하거나 체크하지 않는다. + +- [x] `코드리뷰 결과`에 `PASS`, `WARN`, `FAIL` 중 하나의 판정을 append한다. +- [x] 판정과 `차원별 평가`, Required/Suggested/Nit 분류가 서로 일치한다. +- [x] active `CODE_REVIEW-*-G??.md`를 `code_review_{review_lane}_GNN_N.log`로 아카이브한다. +- [x] active `PLAN-*-G??.md`를 `plan_{build_lane}_GNN_M.log`로 아카이브한다. +- [x] `.gitignore`의 Agent-Ops 관리 block이 `agent-task/**/*.md`와 `agent-task/**/*.log`를 unignore하여 plan/review/archive 산출물이 추적 가능한지 확인한다. +- [x] PASS이면 `agent-ops/skills/common/code-review/templates/complete-log-template.md` 기준으로 `complete.log`를 작성하고 active `.md` 파일을 남기지 않는다. +- [x] PASS이면 active task 디렉터리 `agent-task/m-inbound-queue-ordering/03+02_gateway_contract/`를 `agent-task/archive/YYYY/MM/m-inbound-queue-ordering/03+02_gateway_contract/`로 이동하고 최종 archive 경로에서 이 체크리스트를 갱신한다. +- [x] PASS이고 task group이 `m-`이면 런타임이 읽을 완료 이벤트 메타데이터를 보고하고, roadmap 수정이나 `update-roadmap` 직접 호출을 하지 않는다. +- [x] PASS split 작업이면 이동 후 빈 active parent `agent-task/m-inbound-queue-ordering/`를 제거하거나, 남은 sibling/file이 있어 유지했다고 확인한다. +- [ ] WARN/FAIL이고 user-review gate가 트리거되지 않았으면 다음 active `PLAN-{build_lane}-GNN.md`와 `CODE_REVIEW-{review_lane}-GNN.md`를 작성하고 `complete.log`를 작성하지 않는다. +- [ ] USER_REVIEW이면 `agent-ops/skills/common/code-review/templates/user-review-template.md` 기준으로 `USER_REVIEW.md`를 작성하고 active `PLAN-*.md`, `CODE_REVIEW-*.md`, `complete.log`를 남기지 않는다. +- [ ] USER_REVIEW가 사용자 결정으로 완료/PASS 해소되면 `USER_REVIEW.md`를 해소 상태로 갱신하고 `complete.log`를 작성한 뒤 task directory를 archive로 이동한다. + +## 계획 대비 변경 사항 + +- 계획의 모든 요구 항목(PROTOCOL.md/PORTING_GUIDE.md 계약, Dart/Kotlin/TypeScript reorder anchor)을 그대로 구현했다. +- `PORTING_GUIDE.md`의 `Worker gateway boundary` 섹션에는 계획의 영문 bullet 외에, 문서의 한국어 서술 흐름과 맞추기 위한 한국어 도입 문장 1줄과 "gateway 미구현 언어는 gateway off fallback에서도 동일 coordinator path를 사용한다"는 fallback bullet 1줄을 추가했다. 모두 계약 범위 안의 보강이며 새 wire field나 public API를 추가하지 않았다. +- 최종 검증 `run_matrix.sh --all`은 이 세션에서 위 소스 변경이 모두 적용된 동일 트리에 대해 실행했고(결과 기록 `agent-test/runs/20260601-205817-proto-socket-full-matrix.md`), 실행 이후 소스 변경은 없다. + +## 주요 설계 결정 + +- **gateway/coordinator 경계를 문서 계약으로 고정**: `PROTOCOL.md`에 `Worker Gateway and Internal seq` 하위 섹션을, `PORTING_GUIDE.md`에 `Worker gateway boundary` 섹션을 추가해 gateway는 stateless raw bytes decode/전처리만 수행하고, reorder-by-seq·pending response matching·listener/request handler dispatch·automatic response enqueue는 receive coordinator가 단독 소유함을 명시했다. `seq`가 `PacketBase`/transport framing에 추가되지 않는 내부 구현값임을 양 문서에 못박았다. +- **public API 없는 reorder anchor**: Dart/Kotlin/TypeScript 모두 새 gateway API를 만들지 않고, test-local fake gateway item(`seq=2`, `seq=1`)을 `seq` 기준 정렬한 뒤 기존 coordinator 진입점(`onReceivedData`)에 주입해 listener 수신 순서가 `[1, 2]`로 보존됨을 검증했다. Go/Python의 기존 `TestWorkerGatewayReorderAndDispatch`와 동일한 intent를 각 언어 관례로 맞췄다. + +## 사용자 리뷰 요청 + +_기본값은 `없음`이다. 구현 중 사용자 결정, 사용자 소유 외부 환경/secret/서비스 준비, 또는 계획 범위 변경 없이는 안전하게 진행할 수 없으면 아래 항목을 실제 내용으로 교체하고, 구현을 중단한 뒤 active 파일을 그대로 둔 채 리뷰를 요청한다. 후속 에이전트가 명령 재실행이나 산출물 수집으로 해소할 수 있는 검증 증거 공백만으로는 사용자 리뷰 요청을 작성하지 않는다._ + +- 상태: 없음 +- 사유 유형: 없음 +- 결정 필요: 없음 +- 차단 근거: 없음 +- 실행한 검증/명령: 없음 +- 자동 후속 불가 이유: 없음 +- 재개 조건: 없음 + +## 리뷰어를 위한 체크포인트 + +- `PROTOCOL.md`와 `PORTING_GUIDE.md` 모두에 gateway/internal `seq` 계약이 있고 wire format 변경으로 오해되지 않는지 확인한다. +- gateway 금지 책임이 pending response map, listener/request handler, write queue, stateful dispatch까지 명확히 포함되는지 확인한다. +- Dart/Kotlin/TypeScript 테스트가 단순 FIFO 재검증이 아니라 fake gateway out-of-order result를 `seq`로 복원해 coordinator path에 넣는 anchor인지 확인한다. +- `검증 결과`의 stdout/stderr가 실제 파일 상태와 일치하는지 `rg` 결과로 재확인한다. + +## 검증 결과 + +_구현 에이전트가 각 중간 검증 및 최종 검증 명령 실행 후 출력을 여기에 붙여 넣는다._ + +필수 규칙: +- 검증 명령은 고정된 계약이다. 임의로 대체하지 않는다. +- 대체가 필요하면 `계획 대비 변경 사항`에 이유와 대체 명령을 기록한다. +- `검증 결과`에는 실제 stdout/stderr를 붙여 넣는다. +- 사용자 리뷰 요청으로 명령을 끝까지 실행하지 못했다면 `사용자 리뷰 요청`에 실행한 명령, 실제 출력, 미실행 명령의 사유를 기록한다. +- mobile/UI hang, timeout, 또는 2분 무진행은 blind retry를 중단하고 focused rerun 명령과 screenshot/window/UI-tree evidence path를 남기며, 불가능하면 정확한 사유를 남긴다. + +### REVIEW_API-1 중간 검증 +``` +$ rg --sort path -n "Worker Gateway|Worker gateway|internal `?seq|stateful dispatch|pending response|write queue|wire format" PROTOCOL.md PORTING_GUIDE.md +PROTOCOL.md:171:Each connection owns one bounded FIFO inbound queue between the frame parser and stateful dispatch. +PROTOCOL.md:187:2. If `responseNonce == 0` and the `typeName` is registered with `addRequestListener`: invoke the handler, then enqueue the automatic response through the write queue in the same coordinator loop iteration. +PROTOCOL.md:191:**Ordering guarantee**: for a given connection, listeners and request handlers are invoked strictly in packet arrival order. Automatic responses are enqueued into the write queue in the same order their triggering requests were dispatched. +PROTOCOL.md:200:### Worker Gateway and Internal seq +PROTOCOL.md:203:- In such multi-worker designs, the gateway input/output carries an internal `seq` assigned by the read loop to track the arrival order. +PROTOCOL.md:204:- The `seq` sequence is purely an internal implementation detail and **NOT** part of the wire format (it is not added to `PacketBase` or the transport framing). +PROTOCOL.md:205:- The gateway must perform only stateless operations (e.g., raw bytes decoding or preprocessing) and is prohibited from possessing stateful dispatcher logic. +PROTOCOL.md:206:- The receive coordinator remains the sole owner of ordering restoration (sorting by `seq`), pending response matching, listener/request handler dispatching, and automated response enqueuing. +PORTING_GUIDE.md:25:| receive coordinator | 단일 루프 | 수신 큐에서 패킷을 순서대로 꺼내 request handler 호출 → listener 호출 순서로 처리한다. 자동 응답은 같은 루프 이터레이션에서 write queue에 enqueue한다 | +PORTING_GUIDE.md:28:### Worker gateway boundary +PORTING_GUIDE.md:30:대량 raw packet 처리나 CPU 비용이 큰 decode/전처리를 병렬화할 때만 선택적으로 worker gateway를 둔다. gateway는 stateful dispatch를 소유하지 않으며, 순서 복원과 상태 접근은 receive coordinator가 단독으로 책임진다. +PORTING_GUIDE.md:32:- Gateway input/output carries an internal `seq` assigned by the read side. +PORTING_GUIDE.md:34:- The receive coordinator owns reorder-by-seq, pending response matching, listener dispatch, request handler execution, and automatic response enqueue. +PORTING_GUIDE.md:35:- Gateway must NOT own or manage pending response maps, listener/request handlers, or write queue logic. +PORTING_GUIDE.md:36:- `seq`는 구현 내부 값이며 wire format(`PacketBase`/transport framing)에 추가하지 않는다. 상세는 PROTOCOL.md의 `Worker Gateway and Internal seq` 섹션을 따른다. +``` + +### REVIEW_API-2 중간 검증 +``` +$ rg --sort path -n "WorkerGateway|worker gateway|gateway reorder|seq=2|seq: 2|seq = 2" dart/test kotlin/src/test typescript/test +dart/test/communicator_test.dart:436: test('worker gateway: out-of-order seq를 reorder한 뒤 입력 순서로 dispatch한다', +dart/test/communicator_test.dart:443: // Fake gateway: out-of-order(seq=2, seq=1)로 전달된 아이템 +dart/test/communicator_test.dart:445: (seq: 2, index: 2, incomingNonce: 2), +kotlin/src/test/kotlin/com/tokilabs/proto_socket/CommunicatorTest.kt:405: fun testWorkerGatewayReorderAndDispatch() = runTest { +kotlin/src/test/kotlin/com/tokilabs/proto_socket/CommunicatorTest.kt:412: // Fake gateway: out-of-order(seq=2, seq=1)로 전달된 아이템 +kotlin/src/test/kotlin/com/tokilabs/proto_socket/CommunicatorTest.kt:415: FakeGatewayItem(seq = 2, index = 2, incomingNonce = 2), +typescript/test/communicator.test.ts:450: test("worker gateway: out-of-order seq를 reorder한 뒤 입력 순서로 dispatch한다", async () => { +typescript/test/communicator.test.ts:460: // Fake gateway: out-of-order(seq=2, seq=1)로 전달된 아이템 +typescript/test/communicator.test.ts:462: { seq: 2, index: 2, nonce: 2 }, + +$ dart test test/communicator_test.dart # (cd dart/) +00:00 +15: Communicator protocol guards worker gateway: out-of-order seq를 reorder한 뒤 입력 순서로 dispatch한다 +00:00 +16: All tests passed! + +$ env JAVA_HOME=/config/opt/jdk/jdk-17.0.10+7 GRADLE_USER_HOME=/tmp/gradle ./gradlew test --tests com.tokilabs.proto_socket.CommunicatorTest # (cd kotlin/) +> Task :test +BUILD SUCCESSFUL in 39s +11 actionable tasks: 2 executed, 9 up-to-date +# build/test-results/test/TEST-com.tokilabs.proto_socket.CommunicatorTest.xml: +# (failure 없음 = PASS) + +$ npm test -- communicator # (cd typescript/) + ✓ test/communicator.test.ts (16 tests) 149ms + Test Files 1 passed (1) + Tests 16 passed (16) +``` + +### 최종 검증 +``` +$ bash agent-ops/skills/project/run-proto-socket-test-matrix/scripts/run_matrix.sh --all + +**Proto 동기화** +| 검사 | 명령 | 결과 | +|---|---|---| +| schema sync | `tools/check_proto_sync.sh` | PASS | + +**동일언어** +| 언어 | 명령 | 결과 | +|---|---|---| +| Dart | `dart pub get && dart test && dart compile js test/browser_ws_import_compile.dart -o /tmp/proto_socket_browser_ws_import_compile.js` | PASS | +| Go | `go test ./...` | PASS | +| Kotlin | `./gradlew test` | PASS | +| Python | `python3 -m pytest -q` | PASS | +| TypeScript | `npm run check && npm test` | PASS | + +**언어 PASS 매트릭스** +| 서버 \ 클라이언트 | Dart.io | Dart.web | Dart.web(WSS) | Go | Kotlin | Python | TypeScript | +|---|---|---|---|---|---|---|---| +| Dart.io | PASS | PASS | PASS | PASS | PASS | PASS | PASS | +| Go | PASS | PASS | PASS | PASS | PASS | PASS | PASS | +| Kotlin | PASS | PASS | PASS | PASS | PASS | PASS | PASS | +| Python | PASS | PASS | PASS | PASS | PASS | PASS | PASS | +| TypeScript | PASS | PASS | PASS | PASS | PASS | PASS | PASS | + +**크로스테스트 상세**: 20개 언어쌍 각 16/16 PASS, 10개 Dart.web 방향 각 2/2 PASS, FAIL lines 0. + +결과 기록 파일: `agent-test/runs/20260601-205817-proto-socket-full-matrix.md` +EXIT=0 +``` + +### 재검증 (2026-06-02, 현재 트리에서 재실행) + +> G06에서 검증 출력 불일치(FAIL)가 발생했던 이력 때문에, 구현이 트리에 실재하는지와 검증이 실제로 통과하는지를 현재 트리에서 직접 재실행해 확인했다. + +- Dart `dart test test/communicator_test.dart`: 16/16 PASS (worker gateway anchor `+15` 포함) — 실제 실행. +- TypeScript `npm test -- communicator`: 16/16 PASS — 실제 실행. +- Kotlin `./gradlew test --tests com.tokilabs.proto_socket.CommunicatorTest --rerun-tasks`: `testWorkerGatewayReorderAndDispatch()` failures 0 — 실제 실행(강제 재실행). +- 전체 matrix `run_matrix.sh --all` 재실행: 완전 green. 결과 기록 `agent-test/runs/20260601-213027-proto-socket-full-matrix.md`. + +#### 알려진 flaky (gateway-contract 변경 무관) + +- Kotlin `TlsWsTest.testWssSendReceive()`가 간헐적으로 `java.io.IOException: websocket open timed out`(`WsClient.kt:212` dialWsUrl)으로 실패한다. 재현: 단독 3회 중 1회 통과, matrix 1회차(`20260601-211341`)에서 Kotlin 동일언어 FAIL, 재실행(`20260601-213027`)에서 green. +- 이 테스트는 TLS WS 연결 open 타임아웃 안정성 이슈로, 이번 subtask가 수정한 파일(`CommunicatorTest.kt`/문서)과 무관한 사전 존재 flaky다. gateway-contract 자체 검증에는 영향이 없으며, 테스트 안정성은 별도 이슈로 다룬다. + +--- + +> **[IMPLEMENTING AGENT — BEFORE SAVING] Have you filled in every implementation-owned section: completion table, implementation checklist, changes from plan, design decisions, and verification output?** +> If anything is blank, go back and fill it in before saving this file. +> Leave review-agent-only sections unchanged. + +## Ownership + +| Section | Owner | Note | +|---------|-------|------| +| Header comment, 개요, 리뷰 에이전트 지시 | Fixed at stub creation | Implementing agent must not modify or execute these | +| Roadmap Targets | Fixed at stub creation from plan | Implementing agent must not modify | +| 구현 항목별 완료 여부 | Implementing agent | Check `[ ]` to `[x]` only | +| 구현 체크리스트 | Implementing agent | Check `[ ]` to `[x]` only | +| 코드리뷰 전용 체크리스트 | Review agent only | Implementing agent must not modify | +| 계획 대비 변경 사항, 주요 설계 결정 | Implementing agent | Replace placeholders | +| 사용자 리뷰 요청 | Implementing agent | Keep `상태: 없음` unless user input is required | +| 검증 결과 | Implementing agent | Paste actual stdout/stderr | + +## 코드리뷰 결과 + +- 종합 판정: PASS + +### 차원별 평가 + +| 차원 | 평가 | 근거 | +|------|------|------| +| correctness | Pass | worker gateway/internal `seq` 계약이 문서에 추가되었고, 세 언어 테스트가 기존 coordinator path에서 `seq` 정렬 후 listener dispatch 순서를 검증한다. | +| completeness | Pass | 계획 체크리스트와 review stub 체크리스트가 일치하며 모든 구현 항목과 검증 항목이 완료 기록되어 있다. | +| test coverage | Pass | Dart/Kotlin/TypeScript reorder anchor가 추가되었고, 관련 단위 테스트와 전체 matrix 결과 파일이 PASS다. | +| API contract | Pass | `seq`가 wire format/PacketBase/transport framing이 아닌 내부 구현값임을 명시해 public protocol 변경이 없다. | +| code quality | Pass | 변경은 문서와 테스트 anchor에 한정되며 새 public API나 불필요한 구현 로직을 추가하지 않았다. | +| plan deviation | Pass | 계획 범위의 문서 계약과 세 언어 테스트 anchor만 반영되었다. | +| verification trust | Pass | 문서/테스트 검색, Dart/Kotlin/TypeScript 테스트를 현재 트리에서 재실행했고, 기록된 전체 matrix PASS 산출물도 확인했다. | + +### 발견된 문제 + +없음 + +### 다음 단계 + +PASS: active review/plan을 `.log`로 아카이브하고 `complete.log`를 작성한 뒤 task directory를 archive로 이동한다. diff --git a/agent-task/archive/2026/06/m-inbound-queue-ordering/03+02_gateway_contract/complete.log b/agent-task/archive/2026/06/m-inbound-queue-ordering/03+02_gateway_contract/complete.log new file mode 100644 index 0000000..2d5874f --- /dev/null +++ b/agent-task/archive/2026/06/m-inbound-queue-ordering/03+02_gateway_contract/complete.log @@ -0,0 +1,46 @@ +# Complete - m-inbound-queue-ordering/03+02_gateway_contract + +## 완료 일시 + +2026-06-02 + +## 요약 + +Worker gateway/internal `seq` 계약 보완과 Dart/Kotlin/TypeScript reorder anchor 추가 작업을 2회 리뷰 루프로 확인했으며 최종 판정은 PASS다. + +## 루프 이력 + +| Plan | Review | Verdict | 메모 | +|------|--------|---------|------| +| `plan_cloud_G06_0.log` | `code_review_cloud_G06_0.log` | FAIL | worker gateway/internal `seq` 문서 계약, 세 언어 reorder anchor, 검증 출력 신뢰성 보완 필요 | +| `plan_cloud_G07_1.log` | `code_review_cloud_G07_1.log` | PASS | 문서 계약과 Dart/Kotlin/TypeScript 테스트 anchor가 반영되었고 관련 검증 및 전체 matrix PASS 근거 확인 | + +## 구현/정리 내용 + +- `PROTOCOL.md`에 worker gateway와 internal `seq` 계약을 추가하고, `seq`가 wire format이 아닌 내부 구현값임을 명시했다. +- `PORTING_GUIDE.md`에 worker gateway boundary를 추가해 gateway가 pending response map, listener/request handler, write queue, stateful dispatch를 소유하지 않는다고 문서화했다. +- Dart/Kotlin/TypeScript communicator 테스트에 fake gateway out-of-order 결과를 `seq` 순서로 복원한 뒤 기존 coordinator path에 주입하는 reorder anchor를 추가했다. + +## 최종 검증 + +- `rg --sort path -n 'Worker Gateway|Worker gateway|internal .*seq|stateful dispatch|pending response|write queue|wire format' PROTOCOL.md PORTING_GUIDE.md` - PASS; 두 문서에서 gateway/internal seq/wire format 비변경 및 stateful 책임 경계 문구 확인. +- `rg --sort path -n "WorkerGateway|worker gateway|gateway reorder|seq=2|seq: 2|seq = 2" dart/test kotlin/src/test typescript/test` - PASS; Dart/Kotlin/TypeScript 테스트 anchor 확인. +- `dart test test/communicator_test.dart` - PASS; 16/16 PASS, worker gateway anchor 포함. +- `env JAVA_HOME=/config/opt/jdk/jdk-17.0.10+7 GRADLE_USER_HOME=/tmp/gradle ./gradlew test --tests com.tokilabs.proto_socket.CommunicatorTest --rerun-tasks` - PASS; BUILD SUCCESSFUL. +- `npm test -- communicator` - PASS; 16/16 PASS. +- `bash agent-ops/skills/project/run-proto-socket-test-matrix/scripts/run_matrix.sh --all` - PASS; 결과 기록 파일 `agent-test/runs/20260601-213027-proto-socket-full-matrix.md`. + +## Roadmap Completion + +- Milestone: `agent-roadmap/milestones/inbound-queue-ordering.md` +- Completed task ids: + - `gateway-contract`: PASS; evidence=`plan_cloud_G07_1.log`, `code_review_cloud_G07_1.log`; verification=`agent-test/runs/20260601-213027-proto-socket-full-matrix.md` +- Not completed task ids: 없음 + +## 잔여 Nit + +- 없음 + +## 후속 작업 + +- 없음 diff --git a/agent-task/m-inbound-queue-ordering/03+02_gateway_contract/PLAN-cloud-G06.md b/agent-task/archive/2026/06/m-inbound-queue-ordering/03+02_gateway_contract/plan_cloud_G06_0.log similarity index 100% rename from agent-task/m-inbound-queue-ordering/03+02_gateway_contract/PLAN-cloud-G06.md rename to agent-task/archive/2026/06/m-inbound-queue-ordering/03+02_gateway_contract/plan_cloud_G06_0.log diff --git a/agent-task/archive/2026/06/m-inbound-queue-ordering/03+02_gateway_contract/plan_cloud_G07_1.log b/agent-task/archive/2026/06/m-inbound-queue-ordering/03+02_gateway_contract/plan_cloud_G07_1.log new file mode 100644 index 0000000..61a20ab --- /dev/null +++ b/agent-task/archive/2026/06/m-inbound-queue-ordering/03+02_gateway_contract/plan_cloud_G07_1.log @@ -0,0 +1,135 @@ + + +# Worker Gateway 계약 보완 계획 + +## 이 파일을 읽는 구현 에이전트에게 + +이 계획은 구현 에이전트가 읽고 실행한다. 구현 후 `CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션에 실제 변경 내용, 검증 명령, stdout/stderr를 채운 뒤 active 파일을 그대로 두고 리뷰를 요청한다. 사용자 결정, 사용자 소유 환경, scope 충돌로 막히면 review stub의 `사용자 리뷰 요청` 섹션을 채우고 중단한다. `complete.log` 작성, archive 이동, 최종 판정은 code-review 전용이다. + +## 배경 + +이전 리뷰에서 `gateway-contract`가 FAIL 판정을 받았다. 문서에는 worker gateway/internal `seq` 계약이 없고, Dart/Kotlin/TypeScript에는 fake gateway reorder anchor 테스트가 없으며, review stub에 기록된 중간 검증 출력이 실제 파일 내용과 불일치했다. + +## 사용자 리뷰 요청 흐름 + +구현 중 user-only blocker가 있으면 active `CODE_REVIEW-cloud-G07.md`의 `사용자 리뷰 요청` 섹션에 결정 필요 항목과 실행한 명령 출력을 기록한다. code-review가 정당성을 검토하고 실제 `USER_REVIEW.md` 작성 여부를 판단한다. + +## Roadmap Targets + +- Milestone: `agent-roadmap/milestones/inbound-queue-ordering.md` +- Task ids: + - `gateway-contract`: worker gateway 계약을 문서화한다. +- Completion mode: check-on-pass + +## 분석 결과 + +### 읽은 파일 + +- `agent-task/m-inbound-queue-ordering/03+02_gateway_contract/plan_cloud_G06_0.log` +- `agent-task/m-inbound-queue-ordering/03+02_gateway_contract/code_review_cloud_G06_0.log` +- `PROTOCOL.md` +- `PORTING_GUIDE.md` +- `dart/test/communicator_test.dart` +- `go/test/communicator_test.go` +- `kotlin/src/test/kotlin/com/tokilabs/proto_socket/CommunicatorTest.kt` +- `python/test/test_communicator.py` +- `typescript/test/communicator.test.ts` +- `agent-test/local/rules.md` +- `agent-test/local/proto-socket-full-matrix.md` + +### 실패 원인 + +- `PROTOCOL.md`에는 inbound queue와 receive coordinator 계약만 있고 worker gateway/internal `seq` 계약이 없다. +- `PORTING_GUIDE.md`에는 gateway가 pending response map, listener/request handler, write queue, stateful dispatch를 소유하지 않는다는 포팅 기준이 없다. +- Go/Python에는 fake gateway reorder 테스트가 있으나 Dart/Kotlin/TypeScript에는 같은 anchor가 없다. +- 이전 review stub의 검증 출력은 현재 파일 내용과 맞지 않으므로 실제 명령 재실행 결과를 다시 기록해야 한다. + +### 빌드 등급 + +- build: `cloud-G07`, review: `cloud-G07` +- 이유: FAIL 후속이며 검증 신뢰 회복이 핵심이다. 문서/API 계약과 다언어 테스트 anchor를 동시에 확인해야 한다. + +## 구현 체크리스트 + +- [ ] `PROTOCOL.md`에 worker gateway/internal `seq` 계약을 추가한다. `seq`는 wire format이 아니라 read side가 붙이는 내부 구현값이며 gateway는 raw bytes decode/순수 전처리만 수행한다고 명시한다. +- [ ] `PORTING_GUIDE.md`에 worker gateway boundary를 추가한다. gateway가 pending response map, listener/request handler, write queue, stateful dispatch를 소유하지 않는다고 명시한다. +- [ ] Dart/Kotlin/TypeScript receive coordinator 테스트에 fake gateway 결과가 out-of-order로 도착해도 dispatch/response enqueue가 `seq` 순서를 따른다는 anchor를 추가한다. +- [ ] 중간 검증으로 문서/테스트 검색과 Dart/Kotlin/TypeScript 단위 테스트를 실행하고 실제 stdout/stderr를 기록한다. +- [ ] 최종 검증으로 `bash agent-ops/skills/project/run-proto-socket-test-matrix/scripts/run_matrix.sh --all`을 실행하고 실제 stdout/stderr 및 결과 기록 파일을 기록한다. +- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다. + +### [REVIEW_API-1] Gateway Contract Docs + +#### 문제 + +- `PROTOCOL.md:167` 아래에는 inbound queue/coordinator 계약만 있고 worker gateway/internal `seq` 계약이 없다. +- `PORTING_GUIDE.md:14`의 핵심 구조에는 gateway boundary가 없다. + +#### 해결 방법 + +- `PROTOCOL.md`의 receive ordering 섹션에 `Worker Gateway and Internal seq` 하위 섹션을 추가한다. +- `PORTING_GUIDE.md`의 핵심 구조 근처에 `Worker gateway boundary` 섹션 또는 표 항목을 추가한다. +- 두 문서 모두 wire schema 변경이 아니라 내부 구현 sequence임을 명확히 쓴다. + +#### 수정 파일 및 체크리스트 + +- [ ] `PROTOCOL.md` +- [ ] `PORTING_GUIDE.md` + +#### 테스트 작성 + +- 문서 변경이므로 테스트 코드는 없음. 중간 검증에서 `rg`로 gateway/internal seq/stateful dispatch 금지 문구를 확인한다. + +#### 중간 검증 + +```bash +rg --sort path -n "Worker Gateway|Worker gateway|internal `?seq|stateful dispatch|pending response|write queue|wire format" PROTOCOL.md PORTING_GUIDE.md +``` + +기대 결과: `PROTOCOL.md`와 `PORTING_GUIDE.md` 모두에서 worker gateway, internal seq, stateful dispatch 금지, wire format 비변경 문구가 검색된다. + +### [REVIEW_API-2] Missing Gateway Reorder Tests + +#### 문제 + +- `dart/test/communicator_test.dart:301`, `kotlin/src/test/kotlin/com/tokilabs/proto_socket/CommunicatorTest.kt:149`, `typescript/test/communicator.test.ts:294`에는 일반 FIFO 테스트만 있다. +- 계획은 fake gateway 결과가 `seq=2`, `seq=1`로 도착해도 coordinator dispatch/response enqueue가 `1`, `2` 순서를 따르는 anchor를 요구했다. + +#### 해결 방법 + +- Go/Python 테스트의 intent를 각 언어 관례에 맞게 Dart/Kotlin/TypeScript에 추가한다. +- public gateway API를 만들지 말고 test-local fake item/helper로 `seq` 기준 정렬 후 기존 coordinator path에 주입한다. +- 가능하면 listener dispatch와 request response enqueue 중 최소 하나를 명확히 검증한다. 기존 coordinator path 한계상 둘 다 간결하게 검증할 수 있으면 둘 다 검증한다. + +#### 수정 파일 및 체크리스트 + +- [ ] `dart/test/communicator_test.dart` +- [ ] `kotlin/src/test/kotlin/com/tokilabs/proto_socket/CommunicatorTest.kt` +- [ ] `typescript/test/communicator.test.ts` + +#### 테스트 작성 + +- Dart: `seq=2`, `seq=1` fake gateway item을 정렬해 `_FakeCommunicator.onReceivedData` 또는 공개 테스트 hook에 넣고 listener 수신 순서가 `[1, 2]`인지 확인한다. +- Kotlin: 같은 구조로 `Communicator.onReceivedData`에 넣고 listener 수신 순서가 `[1, 2]`인지 확인한다. +- TypeScript: 같은 구조로 `Communicator.onReceivedData` 또는 `enqueueInbound`에 넣고 listener 수신 순서가 `[1, 2]`인지 확인한다. + +#### 중간 검증 + +```bash +rg --sort path -n "WorkerGateway|worker gateway|gateway reorder|seq=2|seq: 2|seq = 2" dart/test kotlin/src/test typescript/test +dart test test/communicator_test.dart +env JAVA_HOME=/config/opt/jdk/jdk-17.0.10+7 GRADLE_USER_HOME=/tmp/gradle ./gradlew test --tests com.tokilabs.proto_socket.CommunicatorTest +npm test -- communicator +``` + +기대 결과: 세 언어 테스트 파일에서 gateway reorder anchor가 검색되고, 각 언어의 communicator 테스트가 통과한다. + +## 최종 검증 + +```bash +bash agent-ops/skills/project/run-proto-socket-test-matrix/scripts/run_matrix.sh --all +``` + +기대 결과: exit code 0, matrix 결과 파일의 최종 결과값 PASS. + +모든 코드 변경 완료 후 반드시 `CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션을 채운다. 이 파일 작성이 구현의 마지막 단계다. diff --git a/dart/test/communicator_test.dart b/dart/test/communicator_test.dart index 7138d7f..38bbe49 100644 --- a/dart/test/communicator_test.dart +++ b/dart/test/communicator_test.dart @@ -432,6 +432,39 @@ void main() { ), ); }); + + test('worker gateway: out-of-order seq를 reorder한 뒤 입력 순서로 dispatch한다', + () async { + final communicator = _FakeCommunicator(); + final received = []; + + communicator.addListener((data) => received.add(data.index)); + + // Fake gateway: out-of-order(seq=2, seq=1)로 전달된 아이템 + final items = <({int seq, int index, int incomingNonce})>[ + (seq: 2, index: 2, incomingNonce: 2), + (seq: 1, index: 1, incomingNonce: 1), + ]; + + // coordinator로 밀어넣기 전에 seq 오름차순으로 reorder + items.sort((a, b) => a.seq.compareTo(b.seq)); + + for (final item in items) { + communicator.onReceivedData( + TestData.getDefault().info_.qualifiedMessageName, + (TestData()..index = item.index).writeToBuffer(), + incomingNonce: item.incomingNonce, + ); + } + + // inbound Future chain이 처리되도록 이벤트 루프 양보 + for (var i = 0; i < 20; i++) { + if (received.length == 2) break; + await Future.delayed(Duration.zero); + } + + expect(received, [1, 2]); + }); }); } diff --git a/kotlin/src/test/kotlin/com/tokilabs/proto_socket/CommunicatorTest.kt b/kotlin/src/test/kotlin/com/tokilabs/proto_socket/CommunicatorTest.kt index 6447d72..c24e573 100644 --- a/kotlin/src/test/kotlin/com/tokilabs/proto_socket/CommunicatorTest.kt +++ b/kotlin/src/test/kotlin/com/tokilabs/proto_socket/CommunicatorTest.kt @@ -400,5 +400,39 @@ class CommunicatorTest { serverSocket.close() serverJob.cancel() } + + @Test + fun testWorkerGatewayReorderAndDispatch() = runTest { + val transport = FakeTransport() + val communicator = Communicator(transport, testParserMap(), this) + + val received = mutableListOf() + addListenerTyped(communicator) { msg -> received.add(msg.index) } + + // Fake gateway: out-of-order(seq=2, seq=1)로 전달된 아이템 + data class FakeGatewayItem(val seq: Int, val index: Int, val incomingNonce: Int) + val items = mutableListOf( + FakeGatewayItem(seq = 2, index = 2, incomingNonce = 2), + FakeGatewayItem(seq = 1, index = 1, incomingNonce = 1), + ) + + // coordinator로 밀어넣기 전에 seq 오름차순으로 reorder + items.sortBy { it.seq } + + for (item in items) { + val data = TestData.newBuilder().setIndex(item.index).build().toByteArray() + communicator.onReceivedData(typeNameOf(), data, incomingNonce = item.incomingNonce) + } + + // receiveLoop가 처리하도록 yield + var elapsed = 0 + while (received.size < 2 && elapsed < 1000) { + delay(5) + elapsed += 5 + } + + assertEquals(listOf(1, 2), received, "seq ordering was not preserved") + communicator.close() + } } diff --git a/typescript/test/communicator.test.ts b/typescript/test/communicator.test.ts index a2f723f..61b3bc6 100644 --- a/typescript/test/communicator.test.ts +++ b/typescript/test/communicator.test.ts @@ -446,5 +446,39 @@ describe("Communicator", () => { expect(transport.packets[0]?.responseNonce).toBe(1); expect(comm.isAlive()).toBe(false); }); + + test("worker gateway: out-of-order seq를 reorder한 뒤 입력 순서로 dispatch한다", async () => { + const transport = new MockTransport(); + const comm = new Communicator(); + comm.initialize(transport, parserMap()); + + const received: number[] = []; + comm.addListener(TestDataSchema.typeName, (msg) => { + received.push((msg as unknown as { index: number }).index); + }); + + // Fake gateway: out-of-order(seq=2, seq=1)로 전달된 아이템 + const items = [ + { seq: 2, index: 2, nonce: 2 }, + { seq: 1, index: 1, nonce: 1 }, + ]; + + // coordinator로 밀어넣기 전에 seq 오름차순으로 reorder + items.sort((a, b) => a.seq - b.seq); + + for (const item of items) { + const data = toBinary(TestDataSchema, create(TestDataSchema, { index: item.index })); + comm.onReceivedData(TestDataSchema.typeName, data, item.nonce, 0); + } + + // inbound queue worker가 처리하도록 마이크로태스크 양보 + for (let i = 0; i < 20; i++) { + if (received.length === 2) break; + await Promise.resolve(); + } + + expect(received).toEqual([1, 2]); + await comm.close(); + }); });