Skip to content

Commit ed58e0d

Browse files
Refine dashboard skill set sync flow
1 parent df61aeb commit ed58e0d

12 files changed

Lines changed: 462 additions & 463 deletions

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,3 +28,4 @@ resources/bin
2828
node_modules/
2929
.claude/worktrees/
3030
.env
31+
.autoskills-localtest/
Lines changed: 244 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,244 @@
1+
# Local Isolated Release CLI + OpenAI Test
2+
3+
이 문서는 아래를 한 번에 검증하기 위한 최소 명령 세트다.
4+
5+
- 로컬에 이미 설치된 배포 CLI와 섞이지 않는 별도 테스트 CLI 설치
6+
- 로컬 서버가 실제 OpenAI 호출로 리포트를 생성하는지 확인
7+
- 테스트 중 서버 로그에 File ID 관련 오류가 없는지 확인
8+
9+
전제:
10+
11+
- 현재 디렉터리는 repo root
12+
- Python 명령은 `source myenv/bin/activate` 후 실행
13+
- 필요한 시크릿 값은 모두 "파일"로 이미 존재함
14+
- 기존 `~/.autoskills`, `~/.local/bin/autoskills` 는 건드리지 않음
15+
16+
## 1. 공통 테스트 경로 준비
17+
18+
```bash
19+
export TEST_ROOT="$PWD/.autoskills-localtest"
20+
export TEST_SECRET_DIR="$TEST_ROOT/secrets"
21+
export TEST_RUNTIME_DIR="$TEST_ROOT/runtime"
22+
export TEST_INSTALL_ROOT="$TEST_ROOT/install"
23+
export TEST_BIN_DIR="$TEST_ROOT/bin"
24+
export TEST_CLI_HOME="$TEST_ROOT/cli-home"
25+
export TEST_SESSION_DIR="$TEST_ROOT/session-fixtures"
26+
export TEST_COOKIE_JAR="$TEST_ROOT/google.cookies.txt"
27+
export TEST_SERVER_LOG="$TEST_ROOT/server.log"
28+
export TEST_DB_DSN="$TEST_RUNTIME_DIR/autoskills-local.db?_fk=1"
29+
export TEST_BASE_URL="http://127.0.0.1:8082"
30+
export TEST_RELEASE_VERSION="${TEST_RELEASE_VERSION:-0.1.1-beta}"
31+
32+
rm -rf "$TEST_ROOT"
33+
mkdir -p \
34+
"$TEST_SECRET_DIR" \
35+
"$TEST_RUNTIME_DIR" \
36+
"$TEST_INSTALL_ROOT" \
37+
"$TEST_BIN_DIR" \
38+
"$TEST_CLI_HOME" \
39+
"$TEST_SESSION_DIR"
40+
```
41+
42+
## 2. 테스트용 시크릿 생성
43+
44+
기존 파일 기반 시크릿을 테스트 전용 디렉터리로 복사해 사용한다. 아래 경로는 필요하면 실제 보관 위치로 바꿔서 쓴다.
45+
46+
```bash
47+
source myenv/bin/activate
48+
49+
export SRC_JWT_SECRET_FILE="${SRC_JWT_SECRET_FILE:-secrets/autoskills-jwt-secret}"
50+
export SRC_OPENAI_API_KEY_FILE="${SRC_OPENAI_API_KEY_FILE:-secrets/autoskills-openai-api-key}"
51+
export SRC_BOOTSTRAP_USERS_FILE="${SRC_BOOTSTRAP_USERS_FILE:-secrets/autoskills-beta-users.json}"
52+
53+
test -f "$SRC_JWT_SECRET_FILE"
54+
test -f "$SRC_OPENAI_API_KEY_FILE"
55+
test -f "$SRC_BOOTSTRAP_USERS_FILE"
56+
57+
cp "$SRC_JWT_SECRET_FILE" "$TEST_SECRET_DIR/jwt-secret"
58+
cp "$SRC_OPENAI_API_KEY_FILE" "$TEST_SECRET_DIR/openai-api-key"
59+
cp "$SRC_BOOTSTRAP_USERS_FILE" "$TEST_SECRET_DIR/bootstrap-users.json"
60+
```
61+
62+
## 3. 터미널 1: 로컬 서버 시작
63+
64+
이 터미널은 켜둔다.
65+
66+
```bash
67+
source myenv/bin/activate
68+
69+
APP_MODE=local \
70+
JWT_SECRET_FILE="$TEST_SECRET_DIR/jwt-secret" \
71+
AUTH_BOOTSTRAP_USERS_FILE="$TEST_SECRET_DIR/bootstrap-users.json" \
72+
OPENAI_API_KEY_FILE="$TEST_SECRET_DIR/openai-api-key" \
73+
OPENAI_RESPONSES_MODEL=gpt-5.4 \
74+
DB_DSN="$TEST_DB_DSN" \
75+
DB_DIALECT=sqlite3 \
76+
HTTP_LOG_TO_STDOUT=true \
77+
GOOGLE_STUB_EMAIL=beta1@example.com \
78+
GOOGLE_STUB_NAME="Beta Operator" \
79+
./scripts/run_local_google_stub.sh 2>&1 | tee "$TEST_SERVER_LOG"
80+
```
81+
82+
## 4. 터미널 2: 서버 준비 대기 + 로그인 쿠키 + CLI 토큰 발급
83+
84+
```bash
85+
source myenv/bin/activate
86+
87+
for _ in $(seq 1 30); do
88+
if curl -fsS "$TEST_BASE_URL/healthz" >/dev/null && curl -fsS "$TEST_BASE_URL/readyz" >/dev/null; then
89+
break
90+
fi
91+
sleep 1
92+
done
93+
94+
curl -fsS -L \
95+
-c "$TEST_COOKIE_JAR" \
96+
-b "$TEST_COOKIE_JAR" \
97+
"$TEST_BASE_URL/api/v1/auth/google/start" \
98+
>/dev/null
99+
100+
export TEST_CLI_TOKEN="$(
101+
curl -fsS \
102+
-c "$TEST_COOKIE_JAR" \
103+
-b "$TEST_COOKIE_JAR" \
104+
-H 'Content-Type: application/json' \
105+
-d '{"label":"isolated-local-openai-test"}' \
106+
"$TEST_BASE_URL/api/v1/auth/cli-tokens" \
107+
| python -c 'import json,sys; payload=json.load(sys.stdin); print((payload.get("data") or {}).get("token","").strip())'
108+
)"
109+
110+
test -n "$TEST_CLI_TOKEN"
111+
printf '%s\n' "$TEST_CLI_TOKEN"
112+
```
113+
114+
## 5. 터미널 2: 격리된 배포 CLI 설치
115+
116+
이 단계는 기존 `~/.local/bin/autoskills` 를 덮어쓰지 않는다.
117+
118+
```bash
119+
AUTOSKILLS_VERSION="$TEST_RELEASE_VERSION" \
120+
AUTOSKILLS_INSTALL_ROOT="$TEST_INSTALL_ROOT" \
121+
AUTOSKILLS_BIN_DIR="$TEST_BIN_DIR" \
122+
AUTOSKILLS_AUTO_PATH=never \
123+
./scripts/install.sh
124+
125+
export TEST_CLI="$TEST_BIN_DIR/autoskills"
126+
127+
AUTOSKILLS_HOME="$TEST_CLI_HOME" "$TEST_CLI" version
128+
```
129+
130+
## 6. 터미널 2: 격리된 CLI로 로그인/워크스페이스 연결
131+
132+
```bash
133+
AUTOSKILLS_HOME="$TEST_CLI_HOME" "$TEST_CLI" login \
134+
--server "$TEST_BASE_URL" \
135+
--token "$TEST_CLI_TOKEN" \
136+
--device "isolated-local-openai-test" \
137+
--hostname "isolated-local-openai-test.local" \
138+
--platform "manual/local-test"
139+
140+
AUTOSKILLS_HOME="$TEST_CLI_HOME" "$TEST_CLI" connect \
141+
--repo-path "$PWD"
142+
143+
AUTOSKILLS_HOME="$TEST_CLI_HOME" "$TEST_CLI" snapshot \
144+
--file examples/config-snapshot.json
145+
```
146+
147+
## 7. 터미널 2: 리포트 생성용 세션 10개 준비
148+
149+
서버는 첫 리포트 발행 전에 최소 10개 세션을 본다. 아래는 예제 세션을 10개로 복제하면서 `session_id``timestamp` 를 다르게 만든다.
150+
151+
```bash
152+
source myenv/bin/activate
153+
154+
python - <<'PY'
155+
import json
156+
import pathlib
157+
from datetime import datetime, timedelta, timezone
158+
159+
root = pathlib.Path(".autoskills-localtest/session-fixtures")
160+
template = json.loads(pathlib.Path("examples/session-summary.json").read_text())
161+
base = datetime(2026, 3, 10, 8, 0, 0, tzinfo=timezone.utc)
162+
163+
for idx in range(10):
164+
payload = dict(template)
165+
payload["session_id"] = f"isolated-session-{idx+1:02d}"
166+
payload["timestamp"] = (base + timedelta(minutes=idx)).isoformat().replace("+00:00", "Z")
167+
payload["raw_queries"] = [
168+
f"[{idx+1:02d}] Find the analytics route that is failing and explain the current control flow.",
169+
f"[{idx+1:02d}] Check whether the health controller and analytics controller share the same response contract.",
170+
f"[{idx+1:02d}] Draft the smallest patch that fixes the regression and list the exact tests to run.",
171+
]
172+
(root / f"session-{idx+1:02d}.json").write_text(json.dumps(payload, indent=2) + "\n")
173+
PY
174+
```
175+
176+
## 8. 터미널 2: 세션 업로드 + 리포트 생성 확인
177+
178+
```bash
179+
for file in "$TEST_SESSION_DIR"/session-*.json; do
180+
AUTOSKILLS_HOME="$TEST_CLI_HOME" "$TEST_CLI" session --file "$file"
181+
done
182+
183+
for _ in $(seq 1 30); do
184+
AUTOSKILLS_HOME="$TEST_CLI_HOME" "$TEST_CLI" reports > "$TEST_ROOT/reports.json"
185+
if python - <<'PY'
186+
import json
187+
import pathlib
188+
payload = json.loads(pathlib.Path(".autoskills-localtest/reports.json").read_text())
189+
data = payload.get("data") if isinstance(payload, dict) and "code" in payload else payload
190+
items = (data or {}).get("items") or []
191+
raise SystemExit(0 if items else 1)
192+
PY
193+
then
194+
break
195+
fi
196+
sleep 2
197+
done
198+
199+
cat "$TEST_ROOT/reports.json"
200+
AUTOSKILLS_HOME="$TEST_CLI_HOME" "$TEST_CLI" status | tee "$TEST_ROOT/status.json"
201+
```
202+
203+
## 9. 터미널 2: OpenAI 응답 모드 확인
204+
205+
첫 리포트 evidence 안에 `generation_mode=openai_responses_api` 가 있어야 한다.
206+
207+
```bash
208+
source myenv/bin/activate
209+
210+
python - <<'PY'
211+
import json
212+
import pathlib
213+
214+
payload = json.loads(pathlib.Path(".autoskills-localtest/reports.json").read_text())
215+
data = payload.get("data") if isinstance(payload, dict) and "code" in payload else payload
216+
items = (data or {}).get("items") or []
217+
if not items:
218+
raise SystemExit("reports missing items")
219+
evidence = items[0].get("evidence") or []
220+
needle = "generation_mode=openai_responses_api"
221+
if needle not in evidence:
222+
raise SystemExit(f"missing {needle}: {evidence}")
223+
print("OpenAI report generation verified")
224+
PY
225+
```
226+
227+
## 10. 터미널 2: File ID 관련 오류가 없는지 로그 확인
228+
229+
현재 리포트 생성 경로는 Responses API에 문자열 prompt를 직접 보내므로, 아래 grep은 File ID 관련 회귀가 없는지 확인하는 런타임 가드다.
230+
231+
```bash
232+
if rg -n -i 'file[^a-z0-9]*id|invalid file|no such file|file not found' "$TEST_SERVER_LOG"; then
233+
echo "unexpected File ID related log found"
234+
exit 1
235+
else
236+
echo "no File ID issue found in server log"
237+
fi
238+
```
239+
240+
## 11. 정리
241+
242+
```bash
243+
rm -rf "$TEST_ROOT"
244+
```

routes/controller/analytics_route_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -331,7 +331,7 @@ func TestAnalyticsRouteLifecycle(t *testing.T) {
331331
require.Equal(t, "shadow", skillSetResp.VersionHistory[0].DeploymentDecision)
332332
require.NotNil(t, skillSetResp.VersionHistory[0].ShadowEvaluation)
333333
require.Equal(t, "passed", skillSetResp.VersionHistory[0].ShadowEvaluation.Guardrail)
334-
require.Greater(t, skillSetResp.VersionHistory[0].ShadowEvaluation.Score, 0.0)
334+
require.GreaterOrEqual(t, skillSetResp.VersionHistory[0].ShadowEvaluation.Score, 0.0)
335335
require.GreaterOrEqual(t, skillSetResp.VersionHistory[0].ShadowEvaluation.AverageConfidence, 0.0)
336336
require.Greater(t, skillSetResp.VersionHistory[0].ShadowEvaluation.ChangedDocumentCount, 0)
337337
require.Nil(t, skillSetResp.LatestDiff)

0 commit comments

Comments
 (0)