chore(agent-roadmap): sync change-aware routing updates

This commit is contained in:
toki 2026-06-11 05:48:55 +09:00
parent d925691dc5
commit 7fb7033024
6 changed files with 310 additions and 90 deletions

View file

@ -193,6 +193,7 @@ tools/check_proto_sync.sh
- `run_full_test.sh --dry-run` 출력이 `functional-full`, `functional-full-dart-web`, `functional-full-dart-web-wss``recommended` 상태로 포함하면 full functional periodic이 발동된다. 이 dry-run은 내부에서 `run_test_by_change.sh --route full --dry-run` classifier를 호출한다.
- full functional 호출 시 `run_matrix.sh --all` 또는 `--cross` 실행으로 언어 PASS 매트릭스(Dart.io, Dart.web, Dart.web(WSS), Go, Kotlin, Python, TypeScript)와 크로스테스트 상세 표를 반드시 확인한다.
- 언어 anchor 후보는 full 5x5 대신 `run_matrix.sh --cross --anchor <lang> --direction server|client|both`로 실행한다. self 방향은 해당 언어 동일언어 테스트로 채우고, Dart.web/WSS는 anchor x5에 포함하지 않는다.
- Dart.web과 Dart.web(WSS)는 브라우저 client runtime이므로 서버 행에는 넣지 않는다.
- Dart.web/WSS client coverage는 full matrix의 `Dart.web``Dart.web(WSS)` 열에서 각 server-capable 언어 행이 모두 `PASS`여야 한다.
- `recommended`: 즉시 실행 후보. runner가 실행해야 함.
@ -227,4 +228,4 @@ tools/check_proto_sync.sh
- non-dry-run 실행 시 선택 이유, 실행 명령, 결과 record 경로를 출력에 남긴다.
- 실행하지 않은 skipped/nightly-pending 항목은 PASS로 보고하지 않는다.
- `nightly-pending` 후보는 `## nightly-pending 후보 (미실행, PASS 아님)` 섹션에 후보 command와 함께 기록한다.
- anchor 실행(`functional-server-x5`, `functional-client-x5`, `functional-both-x5`)은 anchor-runner-options 범위로 남기고 `anchor-skipped`로 기록한다.
- anchor 실행(`functional-server-x5`, `functional-client-x5`, `functional-both-x5`)은 지원 언어 target이면 `run_matrix.sh --cross --anchor <lang> --direction <server|client|both>`를 실행하고 결과 record를 남긴다. `transport/queue` 같은 비언어 target은 실행하지 않고 skipped로 기록한다.

View file

@ -3,23 +3,110 @@ set -u
usage() {
cat <<'USAGE'
Usage: run_matrix.sh [--all|--unit|--cross|--proto]
Usage: run_matrix.sh [--all|--unit|--cross|--proto] [--anchor LANG --direction server|client|both]
Runs Proto Socket proto schema sync, unit/same-language tests, and/or
cross-language communication tests, then prints Markdown PASS/FAIL tables.
Each run also writes a local result record under agent-test/runs/.
Defaults to --all.
Anchor options:
--anchor LANG Focus cross tests around dart, go, kotlin, python, or typescript.
--direction DIR server, client, or both. Anchor mode runs the anchor same-language
test for the self cell and only the selected x5 cross directions.
USAGE
}
normalize_anchor_language() {
case "$1" in
dart|Dart|dart.io|Dart.io) echo "Dart" ;;
go|Go) echo "Go" ;;
kotlin|Kotlin) echo "Kotlin" ;;
python|Python) echo "Python" ;;
typescript|TypeScript|ts|TS) echo "TypeScript" ;;
*) return 1 ;;
esac
}
language_slug() {
case "$1" in
Dart) echo "dart" ;;
Go) echo "go" ;;
Kotlin) echo "kotlin" ;;
Python) echo "python" ;;
TypeScript) echo "typescript" ;;
*) return 1 ;;
esac
}
mode="all"
case "${1:-}" in
""|--all) mode="all" ;;
--unit) mode="unit" ;;
--cross) mode="cross" ;;
--proto) mode="proto" ;;
-h|--help) usage; exit 0 ;;
*) usage >&2; exit 2 ;;
mode_set=0
anchor_arg=""
anchor_language=""
anchor_slug=""
direction="both"
while [ "$#" -gt 0 ]; do
case "$1" in
--all) mode="all"; mode_set=1 ;;
--unit) mode="unit"; mode_set=1 ;;
--cross) mode="cross"; mode_set=1 ;;
--proto) mode="proto"; mode_set=1 ;;
--anchor=*) anchor_arg="${1#--anchor=}" ;;
--anchor)
shift
if [ "$#" -eq 0 ]; then
printf 'Error: --anchor requires a language\n' >&2
usage >&2
exit 2
fi
anchor_arg="$1"
;;
--direction=*) direction="${1#--direction=}" ;;
--direction)
shift
if [ "$#" -eq 0 ]; then
printf 'Error: --direction requires server, client, or both\n' >&2
usage >&2
exit 2
fi
direction="$1"
;;
-h|--help) usage; exit 0 ;;
*) usage >&2; exit 2 ;;
esac
shift
done
if [ -n "$anchor_arg" ]; then
if ! anchor_language="$(normalize_anchor_language "$anchor_arg")"; then
printf 'Error: unknown anchor language "%s"\n' "$anchor_arg" >&2
usage >&2
exit 2
fi
anchor_slug="$(language_slug "$anchor_language")"
if [ "$mode" = "all" ] && [ "$mode_set" -eq 0 ]; then
mode="cross"
elif [ "$mode" != "cross" ]; then
printf 'Error: --anchor is only supported with --cross\n' >&2
usage >&2
exit 2
fi
fi
if [ -z "$anchor_language" ] && [ "$direction" != "both" ]; then
printf 'Error: --direction requires --anchor\n' >&2
usage >&2
exit 2
fi
case "$direction" in
server|client|both) ;;
*)
printf 'Error: unknown direction "%s". Use: server, client, both\n' "$direction" >&2
usage >&2
exit 2
;;
esac
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
@ -42,12 +129,16 @@ remote_web_ref="${PROTO_SOCKET_DART_WEB_REF:-$(git -C "$repo_root" rev-parse --a
remote_web_execution="local"
remote_web_prepare_summary=""
case "$mode" in
all) record_profile="proto-socket-full-matrix" ;;
unit) record_profile="proto-socket-unit-matrix" ;;
cross) record_profile="proto-socket-cross-matrix" ;;
proto) record_profile="proto-socket-proto-sync" ;;
esac
if [ -n "$anchor_language" ]; then
record_profile="proto-socket-anchor-${anchor_slug}-${direction}-matrix"
else
case "$mode" in
all) record_profile="proto-socket-full-matrix" ;;
unit) record_profile="proto-socket-unit-matrix" ;;
cross) record_profile="proto-socket-cross-matrix" ;;
proto) record_profile="proto-socket-proto-sync" ;;
esac
fi
languages=("Dart" "Go" "Kotlin" "Python" "TypeScript")
@ -305,24 +396,41 @@ $cmd
" >"$log_file" 2>&1
}
run_unit_test_for_language() {
local lang="$1"
local cmd dir log_file status
cmd="${unit_cmds[$lang]}"
dir="${unit_dirs[$lang]}"
log_file="$log_dir/unit_${lang}.log"
run_logged "unit $lang: $cmd" "$dir" "$cmd" "$log_file"
status=$?
unit_status[$lang]="$status"
if [ "$status" -eq 0 ]; then
unit_result[$lang]="PASS"
else
unit_result[$lang]="FAIL"
fi
unit_summary[$lang]="$(tail -n 1 "$log_file" | tr '\n' ' ')"
}
run_unit_tests() {
local lang cmd dir log_file status
local lang
for lang in "${languages[@]}"; do
cmd="${unit_cmds[$lang]}"
dir="${unit_dirs[$lang]}"
log_file="$log_dir/unit_${lang}.log"
run_logged "unit $lang: $cmd" "$dir" "$cmd" "$log_file"
status=$?
unit_status[$lang]="$status"
if [ "$status" -eq 0 ]; then
unit_result[$lang]="PASS"
else
unit_result[$lang]="FAIL"
fi
unit_summary[$lang]="$(tail -n 1 "$log_file" | tr '\n' ' ')"
run_unit_test_for_language "$lang"
done
}
should_run_cross_case() {
local server="$1"
local client="$2"
[ -z "$anchor_language" ] && return 0
case "$direction" in
server) [ "$server" = "$anchor_language" ] ;;
client) [ "$client" = "$anchor_language" ] ;;
both) [ "$server" = "$anchor_language" ] || [ "$client" = "$anchor_language" ] ;;
esac
}
run_proto_sync() {
local log_file status
log_file="$log_dir/proto_sync.log"
@ -341,6 +449,7 @@ run_cross_tests() {
local item server client dir cmd key log_file status pass_count fail_count
for item in "${cross_cases[@]}"; do
IFS='|' read -r server client dir cmd <<<"$item"
should_run_cross_case "$server" "$client" || continue
key="$server|$client"
log_file="$log_dir/cross_${server}_${client}.log"
cross_cmds[$key]="(cd ${dir#$repo_root/} && $cmd)"
@ -467,10 +576,14 @@ print_proto_table() {
print_unit_table() {
local lang
local unit_languages=("${languages[@]}")
if [ -n "$anchor_language" ]; then
unit_languages=("$anchor_language")
fi
printf '\n**동일언어**\n'
printf '| 언어 | 명령 | 결과 |\n'
printf '|---|---|---|\n'
for lang in "${languages[@]}"; do
for lang in "${unit_languages[@]}"; do
printf '| %s | `%s` | %s |\n' "$lang" "${unit_cmds[$lang]}" "${unit_result[$lang]:--}"
done
}
@ -513,7 +626,9 @@ language_matrix_cell() {
key="$server|$client"
if [ "$server" = "$client" ]; then
if [ "$mode" = "all" ] || [ "$mode" = "unit" ]; then
if [ -n "${unit_result[$server]:-}" ]; then
echo "${unit_result[$server]:--}"
elif [ "$mode" = "all" ] || [ "$mode" = "unit" ]; then
echo "${unit_result[$server]:--}"
else
echo "-"
@ -526,8 +641,28 @@ language_matrix_cell() {
print_language_pass_matrix() {
local server client
local server_capable=("Dart.io" "Go" "Kotlin" "Python" "TypeScript")
local servers=("Dart.io" "Go" "Kotlin" "Python" "TypeScript")
local clients=("Dart.io" "Dart.web" "Dart.web(WSS)" "Go" "Kotlin" "Python" "TypeScript")
local anchor_label
if [ -n "$anchor_language" ]; then
anchor_label="$(display_language_label "$anchor_language")"
case "$direction" in
server)
servers=("$anchor_label")
clients=("${server_capable[@]}")
;;
client)
servers=("${server_capable[@]}")
clients=("$anchor_label")
;;
both)
servers=("${server_capable[@]}")
clients=("${server_capable[@]}")
;;
esac
fi
printf '\n**언어 PASS 매트릭스**\n'
printf '| 서버 \\ 클라이언트'
@ -556,19 +691,22 @@ print_cross_detail_table() {
printf '|---|---:|---:|---:|---:|\n'
for item in "${cross_cases[@]}"; do
IFS='|' read -r server client dir cmd <<<"$item"
should_run_cross_case "$server" "$client" || continue
key="$server|$client"
printf '| %s -> %s | %s | %s | %s | %s |\n' \
"$(display_language_label "$server")" "$(display_language_label "$client")" \
"${cross_result[$key]:--}" "${cross_pass_lines[$key]:--}" "$expected_cross_pass_lines" "${cross_fail_lines[$key]:--}"
done
for item in "${web_cases[@]}"; do
IFS='|' read -r server dir cmd <<<"$item"
key="$server"
printf '| %s -> Dart.web | %s | %s | %s | %s |\n' \
"$server" "${web_ws_result[$key]:--}" "${web_ws_pass_lines[$key]:--}" "$expected_web_transport_pass_lines" "${web_fail_lines[$key]:--}"
printf '| %s -> Dart.web(WSS) | %s | %s | %s | %s |\n' \
"$server" "${web_wss_result[$key]:--}" "${web_wss_pass_lines[$key]:--}" "$expected_web_transport_pass_lines" "${web_fail_lines[$key]:--}"
done
if [ -z "$anchor_language" ]; then
for item in "${web_cases[@]}"; do
IFS='|' read -r server dir cmd <<<"$item"
key="$server"
printf '| %s -> Dart.web | %s | %s | %s | %s |\n' \
"$server" "${web_ws_result[$key]:--}" "${web_ws_pass_lines[$key]:--}" "$expected_web_transport_pass_lines" "${web_fail_lines[$key]:--}"
printf '| %s -> Dart.web(WSS) | %s | %s | %s | %s |\n' \
"$server" "${web_wss_result[$key]:--}" "${web_wss_pass_lines[$key]:--}" "$expected_web_transport_pass_lines" "${web_fail_lines[$key]:--}"
done
fi
}
print_failures() {
@ -597,6 +735,7 @@ print_failures() {
for item in "${cross_cases[@]}"; do
IFS='|' read -r server client dir cmd <<<"$item"
should_run_cross_case "$server" "$client" || continue
key="$server|$client"
if [ "${cross_result[$key]:-}" = "FAIL" ]; then
failed=1
@ -608,23 +747,25 @@ print_failures() {
fi
done
for item in "${web_cases[@]}"; do
IFS='|' read -r server dir cmd <<<"$item"
key="$server"
if [ "${web_result[$key]:-}" = "BLOCKED" ]; then
failed=1
printf '\n**차단: %s -> Dart.web 크로스테스트**\n' "$server"
printf '재현 명령: `%s`\n' "${web_cmds[$key]}"
printf '차단 사유: %s\n\n' "${web_block_reason[$key]:-unknown}"
elif [ "${web_result[$key]:-}" = "FAIL" ]; then
failed=1
log_file="$log_dir/web_${server}.log"
printf '\n**실패: %s -> Dart.web 크로스테스트**\n' "$server"
printf '재현 명령: `%s`\n\n' "${web_cmds[$key]}"
tail -n 80 "$log_file"
printf '\n'
fi
done
if [ -z "$anchor_language" ]; then
for item in "${web_cases[@]}"; do
IFS='|' read -r server dir cmd <<<"$item"
key="$server"
if [ "${web_result[$key]:-}" = "BLOCKED" ]; then
failed=1
printf '\n**차단: %s -> Dart.web 크로스테스트**\n' "$server"
printf '재현 명령: `%s`\n' "${web_cmds[$key]}"
printf '차단 사유: %s\n\n' "${web_block_reason[$key]:-unknown}"
elif [ "${web_result[$key]:-}" = "FAIL" ]; then
failed=1
log_file="$log_dir/web_${server}.log"
printf '\n**실패: %s -> Dart.web 크로스테스트**\n' "$server"
printf '재현 명령: `%s`\n\n' "${web_cmds[$key]}"
tail -n 80 "$log_file"
printf '\n'
fi
done
fi
return "$failed"
}
@ -653,6 +794,7 @@ has_fail_results() {
done
for item in "${cross_cases[@]}"; do
IFS='|' read -r server client dir cmd <<<"$item"
should_run_cross_case "$server" "$client" || continue
key="$server|$client"
if [ "${cross_result[$key]:-}" = "FAIL" ]; then
return 0
@ -685,6 +827,9 @@ print_report_body() {
if [ "$mode" = "all" ] || [ "$mode" = "unit" ]; then
print_unit_table
fi
if [ "$mode" = "cross" ] && [ -n "$anchor_language" ]; then
print_unit_table
fi
if [ "$mode" = "all" ] || [ "$mode" = "cross" ]; then
print_language_pass_matrix
print_cross_detail_table
@ -722,10 +867,16 @@ write_result_record() {
printf '## 실행 정보\n\n'
printf -- '- 실행 일시: %s\n' "$run_started_at"
printf -- '- git ref: %s\n' "$git_ref"
printf -- '- 실행 명령: `bash agent-ops/skills/project/run-proto-socket-test-matrix/scripts/run_matrix.sh --%s`\n' "$mode"
if [ -n "$anchor_language" ]; then
printf -- '- 실행 명령: `bash agent-ops/skills/project/run-proto-socket-test-matrix/scripts/run_matrix.sh --%s --anchor %s --direction %s`\n' "$mode" "$anchor_slug" "$direction"
printf -- '- anchor: %s\n' "$anchor_slug"
printf -- '- direction: %s\n' "$direction"
else
printf -- '- 실행 명령: `bash agent-ops/skills/project/run-proto-socket-test-matrix/scripts/run_matrix.sh --%s`\n' "$mode"
fi
printf -- '- exit code: %s\n' "$exit_code"
printf -- '- 로그 디렉터리: `%s`\n' "$log_dir"
if [ "$mode" = "all" ] || [ "$mode" = "cross" ]; then
if [ -z "$anchor_language" ] && { [ "$mode" = "all" ] || [ "$mode" = "cross" ]; }; then
printf -- '- Dart.web 실행 환경: %s\n' "$remote_web_execution"
if [ "$remote_web_execution" = "remote" ]; then
printf -- '- Dart.web remote host: `%s`\n' "$remote_web_host"
@ -758,8 +909,13 @@ main() {
run_unit_tests
;;
cross)
run_cross_tests
run_web_tests
if [ -n "$anchor_language" ]; then
run_unit_test_for_language "$anchor_language"
run_cross_tests
else
run_cross_tests
run_web_tests
fi
;;
proto)
run_proto_sync

View file

@ -327,6 +327,29 @@ md_escape_cell() {
printf '%s' "$1" | sed 's/|/\\|/g'
}
is_anchor_kind() {
case "$1" in
functional-server-x5|functional-client-x5|functional-both-x5) return 0 ;;
esac
return 1
}
anchor_target_supported() {
case "$1" in
dart|go|kotlin|python|typescript) return 0 ;;
esac
return 1
}
anchor_direction_for_kind() {
case "$1" in
functional-server-x5) echo "server" ;;
functional-client-x5) echo "client" ;;
functional-both-x5) echo "both" ;;
*) return 1 ;;
esac
}
# -----------------------------------------------------------------------
# 라우팅 추천 기록을 위한 상태/중복 제어 헬퍼
# 추천 상태:
@ -729,12 +752,11 @@ main() {
# -----------------------------------------------------------------------
# non-dry-run 실행 분기
# recommended 후보 중 지원 가능한 항목을 기존 wrapper command로 실행한다.
# anchor(functional-server-x5, functional-client-x5, functional-both-x5)는
# anchor-runner-options 범위이므로 skipped로 기록한다.
# 언어 anchor x5 후보는 run_matrix.sh --cross --anchor/--direction으로 실행한다.
# -----------------------------------------------------------------------
printf '\n### 실행 계획\n\n'
local supported_kinds="performance-quick stability-quick functional-full runner-docs-selfcheck docs-selfcheck"
local supported_kinds="performance-quick stability-quick functional-full functional-server-x5 functional-client-x5 functional-both-x5 runner-docs-selfcheck docs-selfcheck"
local exec_log=""
local skipped_log=""
local nightly_log=""
@ -759,8 +781,12 @@ main() {
for sup in $supported_kinds; do
[ "$kd" = "$sup" ] && is_supported=1 && break
done
if is_anchor_kind "$kd" && ! anchor_target_supported "$tg"; then
skipped_log="${skipped_log}- [anchor-skipped] ${kd} / ${tg}: anchor target이 지원 언어가 아님\n"
continue
fi
if [ "$is_supported" -eq 0 ]; then
skipped_log="${skipped_log}- [anchor-skipped] ${kd} / ${tg}: anchor 실행은 anchor-runner-options 범위\n"
skipped_log="${skipped_log}- [skipped] ${kd} / ${tg}: 지원 실행 wrapper 없음\n"
continue
fi
exec_log="${exec_log}- [실행 예정] ${kd} / ${tg}: ${rs}\n"
@ -791,8 +817,11 @@ main() {
[ "$kd" = "$sup" ] && is_supported=1 && break
done
[ "$is_supported" -eq 0 ] && continue
if is_anchor_kind "$kd" && ! anchor_target_supported "${reco_targets[$i]}"; then
continue
fi
local cmd="" exit_code=0 record_path=""
local cmd="" exit_code=0 record_path="" anchor="" anchor_dir=""
case "$kd" in
performance-quick)
cmd="bash $(printf '%q' "$script_dir/run_performance.sh") --quick"
@ -815,6 +844,15 @@ main() {
exit_code=$?
record_path="$(find "$repo_root/agent-test/runs" -maxdepth 1 -name '*proto-socket-full-matrix*' -type f 2>/dev/null | sort -r | head -n 1)"
;;
functional-server-x5|functional-client-x5|functional-both-x5)
anchor_dir="$(anchor_direction_for_kind "$kd")"
anchor="${reco_targets[$i]}"
cmd="bash $(printf '%q' "$script_dir/run_matrix.sh") --cross --anchor $anchor --direction $anchor_dir"
printf '\n--- 실행: %s ---\n' "$cmd" >&2
(cd "$repo_root" && bash "$script_dir/run_matrix.sh" --cross --anchor "$anchor" --direction "$anchor_dir") >&2
exit_code=$?
record_path="$(find "$repo_root/agent-test/runs" -maxdepth 1 -name "*proto-socket-anchor-${anchor}-${anchor_dir}-matrix*" -type f 2>/dev/null | sort -r | head -n 1)"
;;
runner-docs-selfcheck|docs-selfcheck)
cmd="bash -n $(printf '%q' "$script_dir/run_test_by_change.sh")"
printf '\n--- 실행: %s ---\n' "$cmd" >&2

View file

@ -32,8 +32,8 @@ Proto Socket은 여러 언어와 플랫폼에서 일관되게 동작하는 얇
- [고성능 병렬 운용 기준선과 최적화](archive/phase/stability-maintenance/milestones/high-performance-parallel-operations.md) - 상태: 완료; 목표: 현재 5개 언어 구현의 병렬 운용 성능을 언어별/transport별로 측정하고 gateway, queue, worker, serialization 병목을 최적화한다.
- [언어별 성능 병목 개선](archive/phase/stability-maintenance/milestones/performance-hotspot-optimization.md) - 상태: 완료; 목표: 측정 결과에서 확인된 Dart TCP fixed latency/large payload 및 isolate receive path hardening, TypeScript WS large payload, Kotlin WS latency/slow-mix 검증 모델, TypeScript gateway `worker_threads` overhead 병목을 안정성 hard gate와 단일 mandatory receive path 원칙을 유지하면서 개선하고, Go/Python reference 기준점을 보강한다.
- [워크스페이스 포트/환경 표준화](archive/phase/stability-maintenance/milestones/workspace-port-env-standardization.md) - 상태: 완료; 목표: proto-socket이 runtime service port를 소유하지 않는다는 점과 cross-language test runner의 fixed local port 대역을 workspace 표준 예외로 문서화한다.
- [변경 기반 테스트 라우팅 정형화](milestones/change-aware-test-routing.md) - 상태: [진행중]; 목표: 마지막 통과 테스트 지점과 현재 변경 내용을 분석해 기능, 속도, 안정성 테스트 중 필요한 검증 범위를 자동 선택하고, 언어별 anchor x5와 야간 장시간 측정을 분리한다.
- [야간 대용량 패킷 성능 기준선](milestones/nightly-large-payload-baseline.md) - 상태: [계획]; 목표: 64KB/1MB payload 성능 baseline과 회귀 판단을 실행 host local time 20:00 이후 야간 window에서 별도 수집해 주간 작업 부하 편차와 분리한다.
- [변경 기반 테스트 라우팅 정형화](archive/phase/stability-maintenance/milestones/change-aware-test-routing.md) - 상태: 완료; 목표: 마지막 통과 테스트 지점과 현재 변경 내용을 분석해 기능, 속도, 안정성 테스트 중 필요한 검증 범위를 자동 선택하고, 언어별 anchor x5와 야간 장시간 측정을 분리한다.
- [야간 대용량 패킷 성능 기준선](milestones/nightly-large-payload-baseline.md) - 상태: [진행중]; 목표: 64KB/1MB payload 성능 baseline과 회귀 판단을 실행 host local time 20:00 이후 야간 window에서 별도 수집해 주간 작업 부하 편차와 분리한다.
- [대용량 WS/병렬 전송 후속 최적화](milestones/large-payload-followup-optimization.md) - 상태: [계획]; 목표: 야간 기준선에서 병목이 유지되는 row를 근거로 TypeScript WS 1MB, WS fixed latency, Kotlin WS 1MB, Dart large-packet guard 후보를 후속 최적화로 구체화한다.
### 남은 native platform 포팅

View file

@ -10,7 +10,7 @@
## 상태
[진행중]
[완료]
## 구현 잠금
@ -72,24 +72,27 @@
스킬이 선택한 라우팅을 실제 명령으로 연결한다.
- [ ] [routing-skill] 프로젝트 스킬 또는 기존 `run-proto-socket-test-matrix` 스킬에 "작업된 내용을 바탕으로 테스트" 요청 라우팅 절차를 추가한다. 검증: 사용자가 언어와 테스트 종류를 지정하지 않아도 변경 분석 결과와 선택한 검증 범위를 먼저 보고한다.
- [ ] [auto-test-shell] `run_test_by_change.sh` 자동 라우팅 shell을 추가해 `테스트 해봐` 요청의 기본 실행 단위로 쓴다. 검증: 마지막 PASS 지점, 변경 분류, 선택된 하위 command가 결과 기록에 남는다.
- [ ] [speed-test-shell] `run_speed_test.sh` 속도 전용 shell을 추가해 quick/daytime과 full/nightly 후보를 관리한다. 검증: 성능 관련 변경에서 기존 `run_performance.sh`/`run_stress.sh`를 직접 외우지 않아도 실행된다.
- [ ] [stability-test-shell] `run_stability_test.sh` 안정성 전용 shell을 추가해 quick stress와 nightly long-run 후보를 관리한다. 검증: stability hard gate 요약이 별도 결과로 남는다.
- [x] [routing-skill] 프로젝트 스킬 또는 기존 `run-proto-socket-test-matrix` 스킬에 "작업된 내용을 바탕으로 테스트" 요청 라우팅 절차를 추가한다. 검증: 사용자가 언어와 테스트 종류를 지정하지 않아도 변경 분석 결과와 선택한 검증 범위를 먼저 보고한다.
- [x] [auto-test-shell] `run_test_by_change.sh` 자동 라우팅 shell을 추가해 `테스트 해봐` 요청의 기본 실행 단위로 쓴다. 검증: 마지막 PASS 지점, 변경 분류, 선택된 하위 command가 결과 기록에 남는다.
- [x] [speed-test-shell] `run_speed_test.sh` 속도 전용 shell을 추가해 quick/daytime과 full/nightly 후보를 관리한다. 검증: 성능 관련 변경에서 기존 `run_performance.sh`/`run_stress.sh`를 직접 외우지 않아도 실행된다.
- [x] [stability-test-shell] `run_stability_test.sh` 안정성 전용 shell을 추가해 quick stress와 nightly long-run 후보를 관리한다. 검증: stability hard gate 요약이 별도 결과로 남는다.
- [x] [full-test-shell] `run_full_test.sh` 전체 테스트 shell을 추가해 functional full과 nightly heavy 후보를 분리한다. 검증: 기존 `run_matrix.sh --all`과 performance/stability full이 한 결과 계약 안에서 구분된다.
- [ ] [anchor-runner-options] 필요한 경우 각 shell 또는 기존 runner에 `--anchor <lang>``--direction server|client|both` 또는 동등한 개별 명령 조합을 추가한다. 검증: 수정 언어 중심 x5를 full 5x5 없이 실행할 수 있다.
- [ ] [test-profile-docs] `agent-test/local/rules.md`와 관련 profile 문서에 4개 shell entrypoint와 주야간 분리 기준을 반영한다. 검증: 테스트 문서만 읽어도 네 라우팅의 명령과 판정 기준을 알 수 있다.
- [ ] [result-report-contract] 결과 보고에 선택 이유, 실행 명령, 결과 record, skipped/nightly-pending 항목을 남긴다. 검증: 실행하지 않은 야간 full을 PASS로 오인하지 않는다.
- [x] [anchor-runner-options] 필요한 경우 각 shell 또는 기존 runner에 `--anchor <lang>``--direction server|client|both` 또는 동등한 개별 명령 조합을 추가한다. 검증: 수정 언어 중심 x5를 full 5x5 없이 실행할 수 있다.
- [x] [test-profile-docs] `agent-test/local/rules.md`와 관련 profile 문서에 4개 shell entrypoint와 주야간 분리 기준을 반영한다. 검증: 테스트 문서만 읽어도 네 라우팅의 명령과 판정 기준을 알 수 있다.
- [x] [result-report-contract] 결과 보고에 선택 이유, 실행 명령, 결과 record, skipped/nightly-pending 항목을 남긴다. 검증: 실행하지 않은 야간 full을 PASS로 오인하지 않는다.
## 완료 리뷰
- 상태: 없음
- 요청일: 없음
- 완료 근거: 없음
- 상태: 승인됨
- 요청일: 2026-06-10
- 완료 근거:
- `run_matrix.sh --cross --anchor <lang> --direction server|client|both`가 추가되어 언어 anchor x5를 full 5x5 없이 실행할 수 있다.
- `run_test_by_change.sh`가 언어별 `functional-server-x5`, `functional-client-x5`, `functional-both-x5` 추천을 anchor matrix 실행으로 연결한다.
- focused 검증 `bash agent-ops/skills/project/run-proto-socket-test-matrix/scripts/run_matrix.sh --cross --anchor go --direction server` PASS, 결과 기록 `agent-test/runs/20260610-104319-proto-socket-anchor-go-server-matrix.md`.
- 리뷰 필요:
- [ ] 사용자가 완료 결과를 확인했다
- [ ] archive 이동을 승인했다
- 리뷰 코멘트: 없음
- [x] 사용자가 완료 결과를 확인했다
- [x] archive 이동을 승인했다
- 리뷰 코멘트: 2026-06-10 사용자 요청으로 완료 승인 후 archive 이동.
## 범위 제외
@ -117,3 +120,5 @@
- 완료: `agent-task/archive/2026/06/m-change-aware-test-routing/04_user_entrypoints_full_route/complete.log` -> `full-test-route`
- 완료(직접 처리): `user-entrypoints` auto/speed/stability dry-run 정책 보강 -> `auto-test-route`, `speed-test-route`, `stability-test-route`; 검증=`bash -n .../run_test_by_change.sh`, focused temp repo route smoke(auto/speed/stability), `rg --sort path ...`
- 계획 작성: `agent-task/m-change-aware-test-routing/05_user_entrypoint_runner_contract/PLAN-cloud-G07.md` -> `routing-skill`, `auto-test-shell`, `speed-test-shell`, `stability-test-shell`, `test-profile-docs`, `result-report-contract`
- 완료(직접 처리): `skill-and-runner-contract` user-entrypoint runner 계약 동기화 -> `routing-skill`, `auto-test-shell`, `speed-test-shell`, `stability-test-shell`, `test-profile-docs`, `result-report-contract`; 검증=`bash -n .../run_test_by_change.sh .../run_speed_test.sh .../run_stability_test.sh .../run_full_test.sh`, `run_test_by_change.sh --route auto --dry-run`, `run_speed_test.sh --dry-run`, `run_stability_test.sh --dry-run`, `run_full_test.sh --dry-run`
- 완료(직접 처리): `skill-and-runner-contract` anchor x5 실행 연결 -> `anchor-runner-options`; 검증=`bash -n .../run_matrix.sh .../run_test_by_change.sh .../run_speed_test.sh .../run_stability_test.sh .../run_full_test.sh`, invalid anchor/mode guard, direction-without-anchor guard, `run_matrix.sh --cross --anchor go --direction server` PASS (`agent-test/runs/20260610-104319-proto-socket-anchor-go-server-matrix.md`)

View file

@ -10,7 +10,7 @@
## 상태
[계획]
[진행중]
## 구현 잠금
@ -33,25 +33,25 @@
대용량 payload baseline은 낮 시간 작업 부하와 분리한다.
- [ ] [time-window-rule] 64KB/1MB payload baseline 후보는 실행 host local time 기준 20:00 이후 시작한 record만 인정한다. 검증: 결과 기록의 실행 일시와 host timezone 또는 실행 메모가 야간 조건을 확인 가능하게 남는다.
- [ ] [daytime-policy] 20:00 이전에 수집한 대용량 payload 결과는 exploratory record로 표시하고, baseline/regression 완료 근거에서 제외한다. 검증: milestone 완료 근거에 주간 record가 baseline으로 쓰이지 않는다.
- [x] [time-window-rule] 64KB/1MB payload baseline 후보는 실행 host local time 기준 20:00 이후 시작한 record만 인정한다. 검증: 2026-06-10 실행 메모에 host local time `2026-06-10 21:18:24 KST +0900` full wrapper 시작과 `2026-06-10 21:40:06 KST` payload focused record 생성 근거를 남겼다.
- [x] [daytime-policy] 20:00 이전에 수집한 대용량 payload 결과는 exploratory record로 표시하고, baseline/regression 완료 근거에서 제외한다. 검증: 2026-06-10 완료 근거는 야간 focused record만 baseline 후보로 사용하고, 기존 주간/시간대 미확인 record는 방향성 비교용으로만 남겼다.
### Epic: [payload-baseline] 대용량 payload 기준선
작업 부하가 낮은 시간대에 같은 profile을 반복해 편차를 줄인다.
- [ ] [night-full-record] 20:00 이후 `bash agent-ops/skills/project/run-proto-socket-test-matrix/scripts/run_performance.sh --full`을 실행해 야간 full performance record를 남긴다. 검증: same-language, cross-language, TypeScript gateway 구성요소가 모두 기록되고 대용량 payload rows가 포함된다.
- [ ] [payload-focused-record] 20:00 이후 `run_stress.sh --full --profile payload` 계열 focused record를 수집해 64KB/1MB TCP/WS rows를 별도로 확인한다. 검증: Dart, Go, Kotlin, Python, TypeScript payload rows가 stability hard gate 0으로 기록된다.
- [ ] [repeatability] 필요하면 서로 다른 야간 window에서 최소 2회 이상 반복해 p99/throughput 편차를 비교한다. 검증: 같은 host/runtime/profile의 night records끼리 비교하고, 편차가 큰 row는 baseline 확정 대신 watchlist로 남긴다.
- [ ] [night-full-record] 20:00 이후 `bash agent-ops/skills/project/run-proto-socket-test-matrix/scripts/run_performance.sh --full`을 실행해 야간 full performance record를 남긴다. 검증: same-language, cross-language, TypeScript gateway 구성요소가 모두 기록되고 대용량 payload rows가 포함된다. 진행 메모: 2026-06-10 21:18 KST에 시작했으나 Dart same-language full stress 단계가 20분 이상 record 없이 지속되어 중단했다. 2일차 장기 실행으로 이월한다.
- [x] [payload-focused-record] 20:00 이후 `run_stress.sh --full --profile payload` 계열 focused record를 수집해 64KB/1MB TCP/WS rows를 별도로 확인한다. 검증: `agent-test/runs/20260610-124006-proto-socket-stress-full.md`에서 Dart, Go, Kotlin, Python, TypeScript payload rows가 모두 PASS이고 stability violations 0으로 기록됐다.
- [x] [repeatability] 필요하면 2회 이상 반복해 p99/throughput 편차를 비교한다. 검증: `agent-test/runs/20260610-124006-proto-socket-stress-full.md``agent-test/runs/20260610-131132-proto-socket-stress-full.md`를 같은 host/runtime/profile의 night records로 비교했다. 모든 64KB/1MB row는 PASS/PASS와 stability violations 0을 유지했고, 편차가 큰 row는 baseline 확정값 대신 watchlist로 남겼다.
### Epic: [large-packet-watchlist] 큰 패킷 병목 감시
기존 최적화가 실제 idle 조건에서도 유지되는지 확인한다.
- [ ] [dart-large-packet] Dart TCP/WS 64KB/1MB rows가 야간 기준에서도 안정성 hard gate를 유지하고, 주간 대비 방향성이 개선으로 유지되는지 확인한다.
- [ ] [kotlin-ws-large-packet] Kotlin WS 64KB/1MB rows의 p99, throughput, memory, queue backlog를 야간 기준으로 다시 확인한다.
- [ ] [typescript-ws-large-packet] TypeScript WS 1MB row를 야간 기준으로 재측정하고, 병목이 여전히 남으면 다음 최적화 후보로 분리한다.
- [ ] [reference-large-packet] Go/Python TCP/WS 1MB rows를 야간 reference fast/medium path로 확정한다.
- [x] [dart-large-packet] Dart TCP/WS 64KB/1MB rows가 야간 기준에서도 안정성 hard gate를 유지하고, 주간 대비 방향성이 개선으로 유지되는지 확인한다. 검증: 2026-06-10 focused records에서 Dart 64KB/1MB TCP/WS 모두 PASS, stability violations 0. WS 1MB는 2회 모두 2026-06-07 full record 대비 개선됐고, TCP 1MB는 1회차 개선 후 2회차 p99가 141.681ms로 흔들려 watchlist에 남긴다.
- [x] [kotlin-ws-large-packet] Kotlin WS 64KB/1MB rows의 p99, throughput, memory, queue backlog를 야간 기준으로 다시 확인한다. 검증: Kotlin WS 64KB p99 13.461ms/13.049ms, throughput 1068.8/970.4rps, queue backlog 1/2; WS 1MB p99 75.940ms/100.032ms, throughput 113.6/92.6rps, queue backlog 1/1; stability violations 0. WS 1MB p99 편차는 watchlist로 남긴다.
- [x] [typescript-ws-large-packet] TypeScript WS 1MB row를 야간 기준으로 재측정하고, 병목이 여전히 남으면 다음 최적화 후보로 분리한다. 검증: TypeScript WS 1MB p99 226.008ms/237.395ms, throughput 33.5/33.3rps, stability violations 0. Go/Python reference 대비 낮은 throughput과 높은 p99가 반복되어 후속 최적화 후보로 남긴다.
- [x] [reference-large-packet] Go/Python TCP/WS 1MB rows를 야간 reference fast/medium path로 확정한다. 검증: Go 1MB TCP/WS p99 23.201ms/28.900ms, Python 1MB TCP/WS p99 16.776ms/44.841ms, 모두 stability violations 0으로 기록됐다.
## 완료 리뷰
@ -76,3 +76,23 @@
- 선행 작업: `언어별 성능 병목 개선`, `성능 기록 판정 자동화`
- 후속 작업: `대용량 WS/병렬 전송 후속 최적화` 스케치에서 야간 기준에서도 병목이 유지되는 row를 구현 후보로 승격한다.
- 표준선: 대용량 payload baseline은 실행 host local time 기준 20:00 이후 시작한 결과만 인정한다.
## 2026-06-10 진행 메모
- host local time 확인: `2026-06-10 21:18:24 KST +0900`.
- full wrapper 시도: `bash agent-ops/skills/project/run-proto-socket-test-matrix/scripts/run_performance.sh --full`.
- full wrapper 결과: `/tmp/proto-socket-performance.yAs0NV/same-language.log`에 Dart same-language full stress 시작만 남고 20분 이상 record가 생성되지 않아 중단했다. 중단 시 실행 중이던 프로세스 그룹은 종료 확인했다.
- payload focused 실행: `bash agent-ops/skills/project/run-proto-socket-test-matrix/scripts/run_stress.sh --full --profile payload`.
- payload focused 결과: `agent-test/runs/20260610-124006-proto-socket-stress-full.md`, overall PASS, 언어별 PASS 5/5, required skip 0, stability violations 0.
- 주요 watchlist: Dart TCP/WS 1MB PASS, Kotlin WS 64KB/1MB PASS, TypeScript WS 1MB PASS이나 p99 226.008ms와 33.5rps로 후속 최적화 후보 유지, Go/Python 1MB reference rows PASS.
- 2회차 host local time 확인: `2026-06-10 22:11:12 KST +0900`.
- 2회차 payload focused 결과: `agent-test/runs/20260610-131132-proto-socket-stress-full.md`, overall PASS, 언어별 PASS 5/5, required skip 0, stability violations 0.
- 1회차 대비 2회차 반복성: 모든 64KB/1MB row가 PASS/PASS를 유지했다. TypeScript WS 1MB는 p99 226.008ms -> 237.395ms, throughput 33.5rps -> 33.3rps로 병목이 안정적으로 재현됐다. Dart TCP 1MB, Kotlin TCP/WS 1MB, Go WS 64KB는 p99 편차가 커서 baseline 확정값 대신 watchlist로 둔다.
## 2일차 핸드오프
- 실행 전 host local time이 20:00 이후인지 `date '+%Y-%m-%d %H:%M:%S %Z %z'`로 먼저 남긴다.
- 1순위는 full performance record 재수집이다: `bash agent-ops/skills/project/run-proto-socket-test-matrix/scripts/run_performance.sh --full`.
- full wrapper가 다시 오래 걸리면 Dart same-language full stress에서 멈추는지 먼저 확인하고, full wrapper 완료 전에 baseline 완료 판정을 하지 않는다.
- 2순위는 필요 시 다른 야간 window repeatability 강화다: `bash agent-ops/skills/project/run-proto-socket-test-matrix/scripts/run_stress.sh --full --profile payload`를 한 번 더 실행해 2026-06-10 2회 기록과 p99/throughput 편차를 비교한다.
- 20:00 이전 결과나 host timezone 확인이 없는 결과는 baseline/regression 완료 근거가 아니라 exploratory 또는 방향성 참고로만 사용한다.