fix(studio-save): 양쪽이 빈 칸을 「같다」에 섞지 않는다 (C 의 B-006 ② 수정안)

C 가 V-009 에서 걸렸다 — P-QUESTION-01 의 「되읽기 차이 0」이 배열 다섯이 전부 비어
견줄 것이 없어서 나온 값이었다. same: true 가 「13칸이 다 맞았다」로 읽히는데 실제로는
찬 칸만 맞은 것이다. R14 와 같은 모양이 대조기 안에 한 번 더 있었다.

- _is_empty 로 양쪽이 빈 칸을 갈라 emptyBoth 로 낸다. 한쪽만 비면 차이다
- comparedFields 에서도 빠지고 comparedCount 가 실제로 견준 수를 낸다
- 사람이 보는 한 줄에 「N칸 중 M칸을 견줬다. K칸은 양쪽이 비어 견줄 것이 없었다」

C 의 수정안은 comparedFields 를 개수로 바꿨는데 목록을 유지하고 개수를 따로 뒀다 —
이미 목록으로 읽는 회귀가 있고, 어느 칸을 못 봤는지는 이름이 있어야 안다.

C-B006 §4 의 대조를 돌렸다. 손으로 센 찬 칸과 도구 출력이 맞는다.

  CONCEPT   6 / 6      REFERENCE 10 / 10      QUESTION 11 / 11

묶음은 B 가 읽을 수 없어(계약 §3.2) 같은 종류의 기록으로 셌다.

python3 -m unittest discover -s scripts/tests — Ran 282 · OK (skipped=13)

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-11 11:06:12 +09:00
co-authored by Claude Opus 5
parent f765a79d6f
commit 252f14c88e
2 changed files with 88 additions and 2 deletions
+51
View File
@@ -788,6 +788,57 @@ class HarnessTestPlanTest(unittest.TestCase):
verify = next(x for x in steps if x["op"] == "verify")
self.assertIn(pid, verify["expect"]["projectId"])
def test_fields_empty_on_both_sides_are_not_counted_as_compared(self):
"""C 가 V-009 에서 걸린 자리. 「되읽기 차이 0」이 배열 다섯이 전부 비어
**견줄 것이 없어서** 나온 값이었다 — R14 와 같은 모양이 대조기 안에 한 번 더 있다."""
sent = {"title": "t", "summary": "s", "facts": [], "assumptions": [],
"unknowns": [], "constraints": [], "options": []}
r = ss.compare_saved(sent, dict(sent))
self.assertTrue(r["same"])
self.assertEqual(2, r["comparedCount"])
self.assertEqual(["facts", "assumptions", "unknowns", "constraints", "options"],
r["emptyBoth"])
# 견준 목록에서도 빠진다 — 어느 칸을 못 봤는지 이름이 남아야 한다
self.assertNotIn("facts", r["comparedFields"])
def test_the_compare_summary_says_how_many_it_looked_at(self):
"""사람이 보는 한 줄이 그냥 「같음」이면 사정권 밖이 초록으로 돌아온다."""
blind = ss.compare_saved({"title": "t", "facts": []},
{"title": "t", "facts": []})["summary"]
full = ss.compare_saved({"title": "t", "facts": [{"id": "a", "text": "하나",
"order": 0}]},
{"title": "t", "facts": [{"id": "x", "text": "하나",
"order": 0}]})["summary"]
self.assertIn("견줄 것이 없었다", blind)
self.assertNotIn("견줄 것이 없었다", full)
self.assertNotEqual(blind, full)
def test_an_empty_field_on_one_side_only_is_still_a_difference(self):
"""**양쪽이 비었을 때만 뺀다.** 한쪽만 비면 값이 사라진 것이다."""
sent = {"facts": [{"id": "a", "text": "하나", "order": 0}]}
r = ss.compare_saved(sent, {"facts": []})
self.assertFalse(r["same"])
self.assertEqual([], r["emptyBoth"])
def test_the_tool_count_matches_what_c_counted_by_hand(self):
"""C-B006 §4 — 손으로 센 「찬 칸」과 도구 출력이 맞는가. 안 맞으면 그게 발견이다.
묶음은 B 가 읽을 수 없으므로(계약 §3.2) 같은 종류의 기록으로 센다."""
for path, want in (
("docs/keycloak/tech-log-studio/oauth-oidc-auth-boundary/concept/"
"concept-bearer-jwt-validation-chain.md", 6),
("docs/keycloak/tech-log-studio/oauth-oidc-auth-boundary/reference/"
"reference-token-vs-session.md", 10),
# `verify-pipeline.py:172` 의 `FORBIDDEN_LITERAL` 이 「옛 저장소 이름 의존」과
# 「그 이름의 docs 프로젝트를 가리킴」을 못 가른다(B-008 §7.6). 글자를 쪼개
# 피하지 않고 같은 종류의 다른 프로젝트 기록을 쓴다
("docs/keycloak/tech-log-studio/oauth-oidc-auth-boundary/question/"
"question-bff-state-store.md", 11),
):
with self.subTest(path=path):
import os
doc = ss.build_input(os.path.join(ROOT, path))
self.assertEqual(want, ss.compare_saved(doc, dict(doc))["comparedCount"])
def test_a_harness_plan_still_has_no_publish_path(self):
steps = ss.plan_requests("docs/p/t/case/x.md", dict(SENT), None, None,
harness_test=True)