fix(studio-save): 확인 없이 옮긴 문장을 고치고, 안 닿는 가지에 유효 범위를 적는다

「Decision 은 계약에 삭제 경로가 없다」고 적었는데 틀렸다. ManagementDocumentController
:123 에 DELETE /v1/studio/projects/{id}/decisions/{decisionId} 가 있다. 스킬의 문장을
확인 없이 옮겼다. 시험 초안을 CASE 로 고른 이유는 삭제 경로 때문이 아니라 그 기록이 이
배치가 만든 것이라 남의 것이 아니어서다.

멱등 키 길이 검사는 지금 정책에서 안 닿는다. 키가 studio-{op}-{32자}(+{32자})라 길이가
사실상 고정이고 저장 키가 77자다. 죽은 코드가 아니라 키 정책이 바뀌면 살아나는 방어선이라
지우지 않고, 언제 살아나는지를 주석에 적었다.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q4vKjQo9KKBBokzxqXLCfk
This commit is contained in:
DongHyeonka
2026-09-10 19:16:55 +09:00
co-authored by Claude Opus 5
parent c66eee6cb2
commit 4e9226a57d
2 changed files with 13 additions and 3 deletions
+8 -1
View File
@@ -178,6 +178,9 @@ def idempotency_key(op: str, record_rel: str, payload: dict | None = None) -> st
key = f"studio-{op}-{base}"
if payload is not None:
key += "-" + _sha256_text(json.dumps(payload, ensure_ascii=False, sort_keys=True))[:32]
# **지금 키 정책에서는 여기 안 온다.** 키가 `studio-{op}-{32자}`(+`-{32자}`)라 길이가
# 사실상 고정이고 저장 키가 77자다. 죽은 코드가 아니라 **키 정책이 바뀌면 그때 살아나는
# 방어선**이다 — 경로를 키에 그대로 넣는 식으로 바꾸면 200자를 넘길 수 있다. 지우지 않는다.
if len(key) > MAX_KEY_LENGTH: # 서버가 200자 초과를 거절한다
raise Refused(f"멱등 키가 {len(key)}자다. 서버 상한은 {MAX_KEY_LENGTH}")
return key
@@ -492,7 +495,11 @@ def plan_requests(record_path: str, doc: dict, document_id: str | None,
#
# `DELETE /api/v1/studio/cases/{id}` 는 실재한다
# (`ManagementDocumentController.java:72` · `studio-management-v1.yaml:126,246`).
# 종류를 CASE 로 고른 이유가 그것이다 — Decision 은 계약에 삭제 경로가 없다.
#
# **시험 초안을 CASE 로 고른 이유는 삭제 경로 때문이 아니다.** 처음에 「Decision 은
# 계약에 삭제 경로가 없다」고 적었는데 틀렸다 — `ManagementDocumentController.java:123`
# 에 `DELETE /v1/studio/projects/{id}/decisions/{decisionId}` 가 있다. 스킬의 문장을
# 확인 없이 옮겼다. **고른 이유는 그 기록이 이 배치가 만든 것이라 남의 것이 아니어서다.**
steps.append({
"op": "cleanup", "method": "DELETE",
"path": f"/api/v1/studio/cases/{document_id or '<생성된 id>'}",
+5 -2
View File
@@ -504,8 +504,11 @@ class HarnessTestPlanTest(unittest.TestCase):
def test_the_cleanup_path_is_the_case_delete_endpoint(self):
"""`DELETE /api/v1/studio/cases/{id}` 는 실재한다
(`ManagementDocumentController.java:72`). Decision 은 삭제 경로가 없어서
시험 초안의 종류를 CASE 로 고른다."""
(`ManagementDocumentController.java:72`).
시험 초안을 CASE 로 고른 이유는 삭제 경로 때문이 아니다 — Decision 에도
`DELETE /v1/studio/projects/{id}/decisions/{decisionId}` 가 있다(`:123`).
고른 이유는 그 기록이 이 배치가 만든 것이라 남의 것이 아니어서다."""
steps = ss.plan_requests("docs/p/t/case/x.md", dict(SENT), None, None,
harness_test=True)
cleanup = next(s for s in steps if s["op"] == "cleanup")