feat(studio-save): 게시된 기록을 코드가 저장 대상에서 뺀다
사용자가 studio:publish 권한 분리를 유예하고 write 권한을 가진 실계정으로 저장하기로 했다. 설계의 명시적 예외이고, 설계가 프롬프트 통제를 인정하지 않는 이유가 여기 그대로 적용된다 — 「이미 게시된 게시물은 건드리지 않는다」를 문장이 아니라 코드로 만든다. 한 번이라도 게시한 문서는 게시를 취소해도 삭제가 409 로 거절된다. public 이 찼거나 status 가 게시 중이면 저장 대상에서 뺀다. 저장소에서 두 표시가 같은 17건을 가리키지만 하나만 차 있어도 게시로 본다 — 한쪽이 뒤늦게 채워지는 경우를 놓치지 않는다. frontmatter 는 저장소가 아는 것이지 서버가 아는 것이 아니다. 그래서 계획의 첫 단계가 서버에 게시 상태를 묻고, PUBLISHED 면 저장 단계로 넘어가지 않는다. 새 문서를 만드는 계획에는 그 단계가 없다 — 아직 없는 문서에는 물어볼 게시 상태가 없다. 시험 초안에 [HARNESS-TEST] 접두사를 붙일 수 있게 했다. 나중에 사람이 눈으로 가린다. 무인 저장은 그대로 꺼져 있다. CR-001 이 유예됐다는 것은 무인 저장을 켠다는 뜻이 아니다 — 사람이 보는 앞에서 저장하는 것과 사람 없이 저장하는 것은 다른 이야기다. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Q4vKjQo9KKBBokzxqXLCfk
This commit is contained in:
co-authored by
Claude Opus 5
parent
87d70e7c80
commit
f16785b93e
@@ -427,3 +427,65 @@ class ReviewGateTest(unittest.TestCase):
|
||||
a = {"id": "유보 감소", "detail": "가능성 2회 → 1회"}
|
||||
b = {"id": "유보 감소", "detail": "가능성 3회 → 1회"}
|
||||
self.assertNotEqual(key(a), key(b))
|
||||
|
||||
|
||||
class PublishedRecordGuardTest(unittest.TestCase):
|
||||
"""게시된 기록은 저장 대상에서 **코드가** 뺀다.
|
||||
|
||||
한 번이라도 게시한 문서는 게시를 취소해도 삭제가 409 로 거절된다. 되돌릴 수 없는
|
||||
자리라 「건드리지 않겠다」는 문장이 아니라 코드가 막는다 — 설계가 프롬프트 통제를
|
||||
인정하지 않는 이유가 그대로 적용된다.
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
self.tmp = tempfile.mkdtemp()
|
||||
|
||||
def _record(self, extra=""):
|
||||
p = os.path.join(self.tmp, "r.md")
|
||||
open(p, "w", encoding="utf-8").write(
|
||||
f"---\nkind: CASE\nslug: x\ntitle: x\n{extra}---\n\n# x\n\n요약.\n")
|
||||
return p
|
||||
|
||||
def test_a_record_with_a_public_url_is_refused(self):
|
||||
marks = ss.published_marks(self._record('public: "https://example.test/x"\n'))
|
||||
self.assertTrue(marks)
|
||||
self.assertIn("public:", marks[0])
|
||||
|
||||
def test_a_record_marked_live_is_refused(self):
|
||||
marks = ss.published_marks(self._record("status: 게시 중\n"))
|
||||
self.assertTrue(marks)
|
||||
|
||||
def test_an_unpublished_record_is_a_save_target(self):
|
||||
"""대조군. 게시 전 기록은 그대로 저장 대상이다."""
|
||||
self.assertEqual([], ss.published_marks(self._record("status: 게시 전\n")))
|
||||
self.assertEqual([], ss.published_marks(self._record('public: ""\n')))
|
||||
|
||||
def test_every_published_record_in_this_repository_is_seen(self):
|
||||
"""저장소의 게시된 기록 전부가 이 가드에 걸려야 한다.
|
||||
|
||||
`public:` 이 찬 것과 `status: 게시 중` 인 것이 저장소에서 같은 집합이지만,
|
||||
한쪽만 차 있어도 게시로 본다 — 한쪽이 뒤늦게 채워지는 경우를 놓치지 않는다.
|
||||
"""
|
||||
import glob
|
||||
seen = 0
|
||||
for f in glob.glob(os.path.join(ROOT, "docs/*/tech-log-studio/*/*/*.md")):
|
||||
text = open(f, encoding="utf-8").read()
|
||||
if "status: 게시 중" in text or 'public: "http' in text:
|
||||
seen += 1
|
||||
self.assertTrue(ss.published_marks(f), f)
|
||||
self.assertGreater(seen, 0, "게시된 기록을 못 찾았다 — 이 대조가 무의미해진다")
|
||||
|
||||
def test_the_plan_asks_the_server_before_saving(self):
|
||||
"""저장소의 frontmatter 는 저장소가 아는 것이지 서버가 아는 것이 아니다."""
|
||||
steps = ss.plan_requests("docs/p/t/case/x.md", dict(SENT), "doc-1", 3)
|
||||
self.assertEqual("publication-precheck", steps[0]["op"])
|
||||
self.assertIn("PUBLISHED", steps[0]["expect"]["stopIf"])
|
||||
|
||||
def test_a_create_plan_has_no_precheck(self):
|
||||
"""아직 없는 문서에는 물어볼 게시 상태가 없다."""
|
||||
steps = ss.plan_requests("docs/p/t/case/x.md", dict(SENT), None, None)
|
||||
self.assertNotIn("publication-precheck", [s["op"] for s in steps])
|
||||
|
||||
def test_the_harness_prefix_is_defined(self):
|
||||
"""시험 초안을 사람이 눈으로 가릴 수 있어야 한다."""
|
||||
self.assertEqual("[HARNESS-TEST]", ss.HARNESS_PREFIX)
|
||||
|
||||
Reference in New Issue
Block a user