diff --git a/agent-ops/rules/project/rules.md b/agent-ops/rules/project/rules.md index 95668a1..8ce3e41 100644 --- a/agent-ops/rules/project/rules.md +++ b/agent-ops/rules/project/rules.md @@ -53,6 +53,10 @@ VERSIONING.md — 프로토콜/패키지 버전 정책 - 이 프로젝트의 주 사용 방식과 릴리즈 기준은 package registry나 CI/CD 배포 흐름이 아니라 Git ref/tag 기반 소비다. - 검증 루프는 내부 로컬 매트릭스 실행 결과를 기준으로 판단한다. - 기본 검증 명령은 `bash agent-ops/skills/project/run-proto-socket-test-matrix/scripts/run_matrix.sh --all`이다. +- 언어 PASS 매트릭스는 서버 가능한 구현만 행으로 둔다: `Dart.io`, `Go`, `Kotlin`, `Python`, `TypeScript`. +- 언어 PASS 매트릭스의 클라이언트 열은 `Dart.io`, `Dart.web`, `Dart.web(WSS)`, `Go`, `Kotlin`, `Python`, `TypeScript` 순서로 둔다. +- `Dart.web`과 `Dart.web(WSS)`는 browser client runtime이므로 서버 행에 넣지 않는다. +- Dart.web 상세는 `Server -> Dart.web`과 `Server -> Dart.web(WSS)`를 분리하고, 각각 `PASS scenarios` expected `2`로 기록한다. - 테스트 coverage 평가는 라인/브랜치 수치가 아니라 프로토콜 계약 시나리오가 매트릭스에서 빠짐없이 검증되는지로 판단한다. - Dart.web remote browser host 사용은 의도된 local 테스트 환경이다. 환경을 이전할 때는 그대로 유지할 규칙이 아니라 새 local 환경 규칙으로 재정의해야 한다. - Dart.web WSS는 테스트용 self-signed certificate의 SPKI를 허용하는 Chrome 실행 wrapper로 local/remote 브라우저 매트릭스에 포함한다. diff --git a/agent-ops/skills/project/run-proto-socket-test-matrix/SKILL.md b/agent-ops/skills/project/run-proto-socket-test-matrix/SKILL.md index 0115238..061911b 100644 --- a/agent-ops/skills/project/run-proto-socket-test-matrix/SKILL.md +++ b/agent-ops/skills/project/run-proto-socket-test-matrix/SKILL.md @@ -104,7 +104,7 @@ tools/check_proto_sync.sh - Proto 동기화 검사는 `tools/check_proto_sync.sh` 종료 코드가 0이면 `PASS`. - 크로스테스트는 각 방향의 `PASS scenario=...` 라인 수가 기대 개수와 일치하는지도 검증한다. 기본 cross matrix는 16개, Dart.web 표는 2개(send-push, request-response)를 기대한다. - 동일 언어 대각선 셀은 `unit` 또는 `all` 범위에서 해당 언어 동일 언어 테스트가 통과했을 때 `PASS`로 표시한다. `cross`만 실행했다면 대각선은 `-`로 둔다. - - Dart.web 클라이언트는 브라우저 WS만 지원하므로 기존 cross matrix(Dart 열)에는 포함하지 않고 별도 Dart.web 표로 보고한다. TCP/TLS/WSS는 `N/A` 또는 `Deferred`로 표기한다. + - Dart.web 클라이언트는 브라우저 WS/WSS만 지원하므로 서버 행에는 넣지 않고, `Dart.web`과 `Dart.web(WSS)` 클라이언트 열로 보고한다. 5. **결과 보고** - 요청 범위에 맞춰 proto 동기화 표, 동일 언어 테스트 표, 서버/클라이언트 크로스 통신 표를 한국어로 보고한다. @@ -133,22 +133,27 @@ tools/check_proto_sync.sh | 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 | **언어 PASS 매트릭스** -| 서버 \ 클라이언트 | Dart.io | Dart.web | Go | Kotlin | Python | TypeScript | -|---|---|---|---|---|---|---| -| Dart.io | PASS | PASS | PASS | PASS | PASS | PASS | -| Go | PASS | PASS | PASS | PASS | PASS | PASS | -| Kotlin | PASS | PASS | PASS | PASS | PASS | PASS | -| Python | PASS | PASS | PASS | PASS | PASS | PASS | -| TypeScript | PASS | PASS | PASS | PASS | 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 | **크로스테스트 상세** | 방향 | 결과 | PASS scenarios | Expected | FAIL lines | |---|---:|---:|---:|---:| -| Dart.io -> Dart.web | PASS | 4 | 4 | 0 | -| Go -> Dart.web | PASS | 4 | 4 | 0 | -| Kotlin -> Dart.web | PASS | 4 | 4 | 0 | -| Python -> Dart.web | PASS | 4 | 4 | 0 | -| TypeScript -> Dart.web | PASS | 4 | 4 | 0 | +| Dart.io -> Dart.web | PASS | 2 | 2 | 0 | +| Dart.io -> Dart.web(WSS) | PASS | 2 | 2 | 0 | +| Go -> Dart.web | PASS | 2 | 2 | 0 | +| Go -> Dart.web(WSS) | PASS | 2 | 2 | 0 | +| Kotlin -> Dart.web | PASS | 2 | 2 | 0 | +| Kotlin -> Dart.web(WSS) | PASS | 2 | 2 | 0 | +| Python -> Dart.web | PASS | 2 | 2 | 0 | +| Python -> Dart.web(WSS) | PASS | 2 | 2 | 0 | +| TypeScript -> Dart.web | PASS | 2 | 2 | 0 | +| TypeScript -> Dart.web(WSS) | PASS | 2 | 2 | 0 | ``` ## 금지 사항 diff --git a/agent-ops/skills/project/run-proto-socket-test-matrix/scripts/run_matrix.sh b/agent-ops/skills/project/run-proto-socket-test-matrix/scripts/run_matrix.sh index 67099d5..9760a55 100755 --- a/agent-ops/skills/project/run-proto-socket-test-matrix/scripts/run_matrix.sh +++ b/agent-ops/skills/project/run-proto-socket-test-matrix/scripts/run_matrix.sh @@ -106,13 +106,18 @@ web_cases=( "TypeScript|$repo_root/typescript|./node_modules/.bin/tsx crosstest/typescript_dart_web.ts" ) declare -A web_result +declare -A web_ws_result +declare -A web_wss_result declare -A web_status declare -A web_pass_lines +declare -A web_ws_pass_lines +declare -A web_wss_pass_lines declare -A web_fail_lines declare -A web_cmds declare -A web_block_reason expected_web_pass_lines=4 +expected_web_transport_pass_lines=2 count_lines() { local pattern="$1" @@ -222,6 +227,9 @@ if [ ! -d \"\$repo_dir/.git\" ]; then git clone \"\$repo_url\" \"\$repo_dir\" fi cd \"\$repo_dir\" +if ! git diff --quiet || ! git diff --cached --quiet || [ -n \"\$(git ls-files --others --exclude-standard)\" ]; then + git stash push -u -m \"proto-socket matrix remote test stash \$(date -u +%Y%m%d-%H%M%S)\" >/dev/null +fi git fetch origin --prune git checkout \"\$repo_ref\" git pull --ff-only origin \"\$repo_ref\" @@ -239,6 +247,34 @@ printf 'remote Dart.web git ref: %s\n' \"\$(git rev-parse --short HEAD)\" " } +sync_remote_web_worktree() { + local changed_list sync_count + changed_list="$log_dir/remote_web_changed_files.zlist" + { + git -C "$repo_root" diff --name-only -z + git -C "$repo_root" diff --cached --name-only -z + git -C "$repo_root" ls-files --others --exclude-standard -z + } | sort -zu >"$changed_list" + + if [ ! -s "$changed_list" ]; then + return 0 + fi + + sync_count="$(tr '\0' '\n' <"$changed_list" | sed '/^$/d' | wc -l | tr -d ' ')" + ( + cd "$repo_root" && + tar --null -T "$changed_list" -cf - + ) | remote_exec " +set -e +repo_parent=$remote_web_root_expr +repo_name=$(single_quote "$remote_web_repo_name") +repo_dir=\"\$repo_parent/\$repo_name\" +cd \"\$repo_dir\" +tar -xf - +" + remote_web_prepare_summary="${remote_web_prepare_summary} local worktree sync: ${sync_count} files " +} + run_remote_web_logged() { local server="$1" local dir="$2" @@ -316,8 +352,40 @@ run_cross_tests() { done } +set_web_result() { + local key="$1" + local status="$2" + local log_file="$3" + local pass_count fail_count ws_pass_count wss_pass_count + + pass_count="$(count_lines '^PASS scenario=' "$log_file")" + ws_pass_count="$(count_lines '^PASS scenario=ws-' "$log_file")" + wss_pass_count="$(count_lines '^PASS scenario=wss-' "$log_file")" + fail_count="$(count_lines '^FAIL ' "$log_file")" + web_status[$key]="$status" + web_pass_lines[$key]="$pass_count" + web_ws_pass_lines[$key]="$ws_pass_count" + web_wss_pass_lines[$key]="$wss_pass_count" + web_fail_lines[$key]="$fail_count" + if [ "$status" -eq 0 ] && [ "$fail_count" -eq 0 ] && [ "$pass_count" -eq "$expected_web_pass_lines" ]; then + web_result[$key]="PASS" + else + web_result[$key]="FAIL" + fi + if [ "$status" -eq 0 ] && [ "$fail_count" -eq 0 ] && [ "$ws_pass_count" -eq "$expected_web_transport_pass_lines" ]; then + web_ws_result[$key]="PASS" + else + web_ws_result[$key]="FAIL" + fi + if [ "$status" -eq 0 ] && [ "$fail_count" -eq 0 ] && [ "$wss_pass_count" -eq "$expected_web_transport_pass_lines" ]; then + web_wss_result[$key]="PASS" + else + web_wss_result[$key]="FAIL" + fi +} + run_web_tests() { - local item server dir cmd key log_file status pass_count fail_count remote_reason + local item server dir cmd key log_file status remote_reason if ! chrome_available; then if ! prepare_remote_web_repo >"$log_dir/remote_web_prepare.log" 2>&1; then remote_reason="$(tail -n 20 "$log_dir/remote_web_prepare.log" | tr '\n' ' ')" @@ -327,8 +395,12 @@ run_web_tests() { web_cmds[$key]="ssh $remote_web_host '(cd $remote_web_root_expr/$remote_web_repo_name/${dir#$repo_root/} && $cmd)'" web_status[$key]="127" web_pass_lines[$key]="0" + web_ws_pass_lines[$key]="0" + web_wss_pass_lines[$key]="0" web_fail_lines[$key]="0" web_result[$key]="BLOCKED" + web_ws_result[$key]="BLOCKED" + web_wss_result[$key]="BLOCKED" web_block_reason[$key]="Remote Dart.web environment unavailable: $remote_reason" printf 'BLOCKED remote web %s->Dart.web: %s\n' "$server" "$remote_reason" >&2 done @@ -336,6 +408,25 @@ run_web_tests() { fi remote_web_execution="remote" remote_web_prepare_summary="$(grep '^remote Dart.web ' "$log_dir/remote_web_prepare.log" 2>/dev/null | tr '\n' ' ')" + if ! sync_remote_web_worktree >"$log_dir/remote_web_sync.log" 2>&1; then + remote_reason="$(tail -n 20 "$log_dir/remote_web_sync.log" | tr '\n' ' ')" + for item in "${web_cases[@]}"; do + IFS='|' read -r server dir cmd <<<"$item" + key="$server" + web_cmds[$key]="ssh $remote_web_host '(cd $remote_web_root_expr/$remote_web_repo_name/${dir#$repo_root/} && $cmd)'" + web_status[$key]="127" + web_pass_lines[$key]="0" + web_ws_pass_lines[$key]="0" + web_wss_pass_lines[$key]="0" + web_fail_lines[$key]="0" + web_result[$key]="BLOCKED" + web_ws_result[$key]="BLOCKED" + web_wss_result[$key]="BLOCKED" + web_block_reason[$key]="Remote Dart.web worktree sync failed: $remote_reason" + printf 'BLOCKED remote web %s->Dart.web: %s\n' "$server" "$remote_reason" >&2 + done + return + fi for item in "${web_cases[@]}"; do IFS='|' read -r server dir cmd <<<"$item" @@ -344,16 +435,7 @@ run_web_tests() { web_cmds[$key]="ssh $remote_web_host '(cd $remote_web_root_expr/$remote_web_repo_name/${dir#$repo_root/} && $cmd)'" run_remote_web_logged "$server" "$dir" "$cmd" "$log_file" status=$? - pass_count="$(count_lines '^PASS scenario=' "$log_file")" - fail_count="$(count_lines '^FAIL ' "$log_file")" - web_status[$key]="$status" - web_pass_lines[$key]="$pass_count" - web_fail_lines[$key]="$fail_count" - if [ "$status" -eq 0 ] && [ "$fail_count" -eq 0 ] && [ "$pass_count" -eq "$expected_web_pass_lines" ]; then - web_result[$key]="PASS" - else - web_result[$key]="FAIL" - fi + set_web_result "$key" "$status" "$log_file" done return fi @@ -365,16 +447,7 @@ run_web_tests() { web_cmds[$key]="(cd ${dir#$repo_root/} && $cmd)" run_logged "web $server->Dart.web: $cmd" "$dir" "$cmd" "$log_file" status=$? - pass_count="$(count_lines '^PASS scenario=' "$log_file")" - fail_count="$(count_lines '^FAIL ' "$log_file")" - web_status[$key]="$status" - web_pass_lines[$key]="$pass_count" - web_fail_lines[$key]="$fail_count" - if [ "$status" -eq 0 ] && [ "$fail_count" -eq 0 ] && [ "$pass_count" -eq "$expected_web_pass_lines" ]; then - web_result[$key]="PASS" - else - web_result[$key]="FAIL" - fi + set_web_result "$key" "$status" "$log_file" done } @@ -419,7 +492,12 @@ language_matrix_cell() { local server client key if [ "$client_label" = "Dart.web" ]; then - echo "${web_result[$server_label]:--}" + echo "${web_ws_result[$server_label]:--}" + return + fi + + if [ "$client_label" = "Dart.web(WSS)" ]; then + echo "${web_wss_result[$server_label]:--}" return fi @@ -442,11 +520,19 @@ language_matrix_cell() { print_language_pass_matrix() { local server client local servers=("Dart.io" "Go" "Kotlin" "Python" "TypeScript") - local clients=("Dart.io" "Dart.web" "Go" "Kotlin" "Python" "TypeScript") + local clients=("Dart.io" "Dart.web" "Dart.web(WSS)" "Go" "Kotlin" "Python" "TypeScript") printf '\n**언어 PASS 매트릭스**\n' - printf '| 서버 \\ 클라이언트 | Dart.io | Dart.web | Go | Kotlin | Python | TypeScript |\n' - printf '|---|---|---|---|---|---|---|\n' + printf '| 서버 \\ 클라이언트' + for client in "${clients[@]}"; do + printf ' | %s' "$client" + done + printf ' |\n' + printf '|---' + for client in "${clients[@]}"; do + printf '|---' + done + printf '|\n' for server in "${servers[@]}"; do printf '| %s' "$server" for client in "${clients[@]}"; do @@ -472,7 +558,9 @@ print_cross_detail_table() { IFS='|' read -r server dir cmd <<<"$item" key="$server" printf '| %s -> Dart.web | %s | %s | %s | %s |\n' \ - "$server" "${web_result[$key]:--}" "${web_pass_lines[$key]:--}" "$expected_web_pass_lines" "${web_fail_lines[$key]:--}" + "$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 }