docs(TechLog): 자료에 남아 있던 사람의 흔적을 제자리에 놓는다
writing-as-the-person-who-did-it 을 서브에이전트 셋으로 나눠 56편에 적용했다. 56편 중 20편만 고쳤다 — 나머지 36편은 SSOT 를 절 단위로 대조했을 때 옮길 흔적이 이미 옮겨져 있었거나 없었다. 없는 목소리를 채우지 않는다. 옮긴 것은 전부 SSOT 의 어느 절에서 왔는지 댈 수 있다. §3.4 「눈으로 찾을 일이 아니었다」— 세 계약을 파싱해 뽑은 이유 §4.3 구현하지 않기로 한 것과 빠뜨린 것은 다르다 §8.5 표의 마지막 줄을 더할 때 이 목록을 또 빠뜨렸다 §9.4 「왜 주제 링크가 탐색으로 가지?」— 우회를 남겨 두면 계속 나온 질문 §11.3 「세 버튼」을 실제 이름으로 되돌리고 두 언어가 섞인 것을 그 자리에 §12.2 막지 않은 대신 메모리에 남긴 것 §13.1 여섯 벌 인용이 어느 커밋이 짚은 말인지 §13.3 고쳐 쓴 첫 안이 거절당한 것과 사용자가 고른 말 두 쌍 §14.3 「문서가 그대로 나온다」는 구조 차이가 아니라 내용 양의 차이라는 정정 §16.1 삭제가 막힌 실제 기록 이름과 그것을 막은 프로젝트 링크 §16.9 주제 논지와 축 결론이 AI 가 써서 DB 에 직접 넣은 미검토 초안이라는 것 §17.5 바운딩 박스로 잘못 지목한 대상이 「판단 기준」이었다는 것 검증 환경의 커밋 해시 하나가 틀려 있었다(ca1cfa2 → ca1fc92). SSOT §13.1 과 부록 A 가 적은 값이고 저장소에 그 커밋이 있다. 검사 넷 전부 통과한다 — check_prose 56편 error 0 · check_body PASS · check_evidence --repo 문제 없음 · verify-tech-log-tree 프로젝트 5 error 0 warn 0. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
0650d91def
commit
6feee5ba57
+1
-1
@@ -94,7 +94,7 @@ tech-log-frontend : fe6b56a
|
||||
|
||||
`PublicPathsTest` 는 백엔드에서 종류마다 만들어 낸 경로가 실제 공개 라우트 패턴에 맞는지 본다.
|
||||
|
||||
`resolvesToPublicRoute` 는 프론트에서 라우트 계약이 준 표에 서버가 준 주소를 맞춰 보고, 맞는 라우트가 없으면 링크로 그리지 않는다.
|
||||
`resolvesToPublicRoute` 는 프론트에서 라우트 계약이 준 표에 서버가 준 주소를 맞춰 보고, 맞는 라우트가 없으면 링크로 그리지 않는다. 이 부류가 또 생겨도 방문자가 404 를 만나지는 않는다. 틀린 주소가 만들어지는 것 자체는 백엔드 쪽 검사가 잡는다.
|
||||
|
||||
## 배포 뒤 전수 감사
|
||||
|
||||
|
||||
+2
@@ -56,4 +56,6 @@ source:
|
||||
|
||||
돌린 이유는 그 페이지만 줄 수 있는 것 — 설명, 범위, 선별한 대표 기록 — 이 전부 비어 있었고 Studio 에 주제 설명을 쓸 칸조차 없었기 때문이다.
|
||||
|
||||
우회를 남겨 두면 「왜 주제 링크가 탐색으로 가지?」라는 질문이 계속 따라온다.
|
||||
|
||||
그 조건을 커밋 메시지에 적었고, 주제 화면을 계약에 잇고 하드코딩을 없앤 뒤 링크를 곧장 주제 화면으로 되돌렸다.
|
||||
|
||||
+1
-1
@@ -52,4 +52,4 @@ source:
|
||||
|
||||
기록이 어느 축에 걸리는지를 담는 표에 외래키를 걸 수 없다. 기록이 종류마다 다른 테이블에 살기 때문이다.
|
||||
|
||||
축에 걸린 기록이 하나뿐이면 축 제목과 그 기록 제목이 비슷해져 「문서가 그대로 나온다」로 보인다.
|
||||
축에 걸린 기록이 하나뿐이면 축 제목과 그 기록 제목이 비슷해져 「문서가 그대로 나온다」로 보인다. 구조 차이가 아니라 내용 양의 차이다.
|
||||
|
||||
+1
-1
@@ -38,7 +38,7 @@ source:
|
||||
|
||||
그래서 기록 1개짜리 축과 20개짜리 축이 홈에서 똑같아 보인다.
|
||||
|
||||
주제의 논지와 축의 결론은 2026-09-01 에 DB 에 직접 넣은 초안이고 아직 검토되지 않았다.
|
||||
주제의 논지와 축의 결론은 2026-09-01 에 AI 가 써서 DB 에 직접 넣은 초안이다. 사용자 검토 대상이고 아직 검토되지 않았다.
|
||||
|
||||
## 가정
|
||||
|
||||
|
||||
+2
-2
@@ -88,7 +88,7 @@ tech-log-frontend : 344dadb · 805d400 · 8c5dbe1
|
||||
|
||||
## 전역 규칙이 닿지 않는 화면
|
||||
|
||||
버튼에서 상자를 걷어내는 변경이 앱 전역 규칙만 고쳤다. 게시 기록·게시 흐름·워크플로 게이트는 CSS module 을 쓰므로 그 규칙이 닿지 않아, 다른 화면에서 상자를 걷어낸 뒤에도 세 버튼만 테두리와 파란 채움으로 남았다.
|
||||
버튼에서 상자를 걷어내는 변경이 앱 전역 규칙만 고쳤다. 게시 기록·게시 흐름·워크플로 게이트는 CSS module 을 쓰므로 그 규칙이 닿지 않아, 다른 화면에서 상자를 걷어낸 뒤에도 「Snapshot 보기」·「게시 취소」·「적용」만 테두리와 파란 채움으로 남았다. 한 화면 안에서 두 언어가 섞여 더 눈에 띄었다.
|
||||
|
||||
## 검사
|
||||
|
||||
@@ -96,6 +96,6 @@ tech-log-frontend : 344dadb · 805d400 · 8c5dbe1
|
||||
|
||||
## 확인하지 못한 것
|
||||
|
||||
CSS module 을 쓰는 화면은 전역 규칙이 닿지 않아 따로 고쳤다. 「지금 집중하는 것」 탭과 주제 탭의 표시 방식이 아직 다르다 — 한쪽은 파란 밑줄이고 한쪽은 알약이다.
|
||||
CSS module 을 쓰는 화면은 전역 규칙이 닿지 않아 따로 고쳤다. 「지금 집중하는 것」 탭과 주제 탭의 표시 방식이 아직 다르다 — 한쪽은 파란 밑줄이고 한쪽은 알약이다. 주제 탭 밑줄을 그쪽에서 베껴 왔다가 뺐기 때문이다.
|
||||
|
||||
<!-- body:end -->
|
||||
|
||||
+1
-1
@@ -60,7 +60,7 @@ CSS 선언이 정본과 같은지를 보는 검사는 렌더 결과를 재지
|
||||
|
||||
격자와 열만 재고 「정상」이라 답했다. 사용자가 다시 지적한 뒤 전체 페이지 스크린샷을 찍어서야 26px 과 400 을 봤다.
|
||||
|
||||
목록 간격을 바운딩 박스로만 재서 엉뚱한 구역을 결함으로 지목한 적이 있다.
|
||||
목록 간격을 바운딩 박스로만 재서 「판단 기준」을 결함으로 잘못 지목한 적이 있다.
|
||||
|
||||
브라우저 세션이 리셋되면 창이 약 877px 로 돌아가는데 이 사이트의 분기는 1179·1050·900·767 이라, 877px 에서 본 배치는 방문자 대부분이 보는 배치가 아니다.
|
||||
|
||||
|
||||
+1
-1
@@ -72,7 +72,7 @@ tech-log-frontend : 15e6ea8 이후
|
||||
|
||||
## 가드 둘
|
||||
|
||||
축 CRUD 를 더한 커밋에서 가드를 둘 넣었다. 공개 계약은 전수 대조한다 — 계약이 선언한 연산이 기여 목록에 전부 있는지 본다. 관리 계약은 86 operation 이라 전수 대조가 무겁고, 대신 「한 종류만 빠진 항목」을 본다.
|
||||
축 CRUD 를 더한 커밋에서 가드를 둘 넣었다. 공개 계약은 전수 대조한다 — 계약이 선언한 연산이 기여 목록에 전부 있는지 본다. 관리 계약은 86 operation 이라 전수 대조가 무겁고, 대신 「한 종류만 빠진 항목」을 본다. 깨진 것이 늘 그 모양이었기 때문이다.
|
||||
|
||||
## 확인하지 못한 것
|
||||
|
||||
|
||||
+3
-1
@@ -72,6 +72,8 @@ tech-log-frontend : 계약에서 생성한 타입을 그대로 사용
|
||||
|
||||
홈 focus 가 가장 오래 숨었다. 세 슬롯이 다 비면 화면이 그 영역을 통째로 그리지 않으므로, 그런 영역이 있다는 사실조차 화면에서 알 수 없다.
|
||||
|
||||
> 화면은 오류를 내지 않고 빈칸을 그렸고, 저는 그것을 "아직 안 쓴 글"로 읽었습니다.
|
||||
|
||||
## 편집기가 부르던 두 목록
|
||||
|
||||
`GET /v1/studio/questions` 와 `GET /v1/studio/projects/{id}/decisions` 도 같은 모양이었다. 계약에 있고 모델도 생성됐는데 컨트롤러가 없었다. 화면은 그것을 「이 프로젝트에 열린 질문이 없습니다」로 그렸고, 실제로는 넷이 있었으며 공개 사이트에도 나오고 있었다.
|
||||
@@ -84,7 +86,7 @@ tech-log-frontend : 계약에서 생성한 타입을 그대로 사용
|
||||
|
||||
`ContractRouteCoverageTest` 가 `@RestController` 들을 리플렉션으로 훑어 매핑을 모으고 계약이 선언한 경로와 대조한다.
|
||||
|
||||
- 작업본 API 로 대체된 옛 연산 51개는 `SUPERSEDED_BY_WORKING_COPY_API` 로 명시한다
|
||||
- 작업본 API 로 대체된 옛 연산 51개는 `SUPERSEDED_BY_WORKING_COPY_API` 로 명시한다 — 「구현하지 않기로 한 것」과 「빠뜨린 것」은 다르다
|
||||
- 봉투 없이 바이트를 주는 `/media` 하나만 `ELSEWHERE` 로 면제한다
|
||||
- 매핑을 떼어 보고 그 연산 하나를 정확히 짚는 것을 확인했다
|
||||
|
||||
|
||||
+2
-2
@@ -46,7 +46,7 @@ source:
|
||||
증상이 오류였던 건 : 3 (404 · 400 · PUBLIC_REQUEST_INVALID)
|
||||
증상이 조용한 누락이었던 건 : 10
|
||||
|
||||
표와 sealed switch 식으로 바꾼 뒤에는 종류를 더할 때 컴파일러가 빠진 값을 짚는다. 계약과 코드 사이는 컴파일러가 못 보므로 계약을 파싱해 대조하는 가드를 따로 뒀다.
|
||||
같은 실수를 열세 번 하고 나서야 규칙으로 굳혔다. 표와 sealed switch 식으로 바꾼 뒤에는 종류를 더할 때 컴파일러가 빠진 값을 짚는다. 계약과 코드 사이는 컴파일러가 못 보므로 계약을 파싱해 대조하는 가드를 따로 뒀다.
|
||||
|
||||
## 검증 환경
|
||||
|
||||
@@ -126,7 +126,7 @@ const PATH_PREFIX_KINDS: Record<PublicRecord["kind"], string> = {
|
||||
- `contract-operation-coverage.test.ts` — 계약이 선언한 연산이 기여 목록에 등록됐는지 본다
|
||||
- `StudioContractUnionJacksonTest` — 모든 `RecordKind` 가 `CatalogEntry.KindEnum` 으로 변환되는지 순회한다. 계약에서 CONCEPT 을 빼면 실제로 빨개지는 것을 확인했다
|
||||
|
||||
설계 패키지 쪽은 세 계약을 파싱해 「CASE 와 REFERENCE 를 함께 열거하면서 CONCEPT 이 없는 enum」을 전부 뽑았다.
|
||||
설계 패키지 쪽은 세 계약을 파싱해 「CASE 와 REFERENCE 를 함께 열거하면서 CONCEPT 이 없는 enum」을 전부 뽑았다. 눈으로 찾을 일이 아니었다.
|
||||
|
||||
## 지금 확인한 범위
|
||||
|
||||
|
||||
+2
@@ -37,6 +37,8 @@ CONCEPT 이 실제로 이 분기에서 빠져 있었고, 경로가 null 로 나
|
||||
|
||||
`PublicPathsTest` 가 지금 종류마다 만들어 낸 경로를 공개 라우트 패턴에 맞춰 본다.
|
||||
|
||||
이 둘은 작업하면서 드러난 것이 아니다. 나중에 근거를 모으며 종류를 나열하는 곳을 다시 훑다가 새로 확인했다.
|
||||
|
||||
## 가정
|
||||
|
||||
`stringFields` 는 배타적 사슬이 아니라 가산형이라, 종류를 빠뜨리면 잘못된 분기로 떨어지는 것이 아니라 그 종류의 추가 칸을 검사하지 않는 결과가 된다. 실제로 종류를 빠뜨려 확인하지는 않았다.
|
||||
|
||||
+1
-1
@@ -72,7 +72,7 @@ SPA 안에서 이동할 때는 라우터가 화면을 그리므로 웹 서버가
|
||||
|
||||
공개 절반도 유도가 아니었다. 서빙 계약이 번들된 픽스처에 우연히 들어 있던 공개 경로를 전부 열거하고, 생성된 nginx 가 정확히 그것들을 `location =` 블록으로 게시했다.
|
||||
|
||||
빌드 이후에 게시된 기록은 SPA 에 묻기도 전에 엣지에서 404 였다. 경로 27개가 얼어 있었고 28번째는 무엇이든 닿을 수 없었다.
|
||||
빌드 이후에 게시된 기록 — 백엔드를 두는 이유 그 자체 — 은 SPA 에 묻기도 전에 엣지에서 404 였다. 경로 27개가 얼어 있었고 28번째는 무엇이든 닿을 수 없었다.
|
||||
|
||||
## 라우트 계약에서 유도한다
|
||||
|
||||
|
||||
+3
-1
@@ -88,7 +88,9 @@ FE-GATE-009 는 설치된 라우트마다 수동 접근성 증거를 하나씩
|
||||
| `048c1b2` | `/concepts/:slug` | +1 | +1 | fb138e7c… 재계산 |
|
||||
| `fe6b56a` | `/topics`, `/topics/:s/:v`, `/studio/topics/:id` | 135 → 138 | 114 → 117 | 87a22f68… 재계산 |
|
||||
|
||||
digest 를 다시 계산할 때는 매번 이전 gates.json 에서 옛 상수를 먼저 재현해 계산 방법이 맞는지 확인한 뒤 새 파일을 해싱했다.
|
||||
표의 마지막 줄인 주제 화면 셋을 더할 때는 이 목록을 또 빠뜨렸다. 게이트가 빨간 채로 여러 커밋을 지나갔고, 결정 404 를 고치던 `fe6b56a` 에서야 함께 맞췄다.
|
||||
|
||||
digest 를 다시 계산할 때는 매번 이전 gates.json 에서 옛 상수를 먼저 재현해 계산 방법이 맞는지 확인한 뒤 새 파일을 해싱했다. 그렇게 하지 않으면 계산이 달라져서 새 값이 나온 것과 파일이 바뀌어서 새 값이 나온 것을 구분할 수 없다.
|
||||
|
||||
## 확인하지 못한 것
|
||||
|
||||
|
||||
+5
-1
@@ -47,7 +47,7 @@ source:
|
||||
|
||||
## 검증 환경
|
||||
|
||||
tech-log-frontend : dc2fda7 · ca1cfa2 계열 · 82e992d
|
||||
tech-log-frontend : dc2fda7 · ca1fc92 · 82e992d
|
||||
확인 방식 : 한 화면에 동시에 뜨는 종류 이름을 세고, 표가 몇 벌인지 확인
|
||||
|
||||
## 재현 조건
|
||||
@@ -73,6 +73,8 @@ tech-log-frontend : dc2fda7 · ca1cfa2 계열 · 82e992d
|
||||
|
||||
## 표가 여섯 벌이었다
|
||||
|
||||
종류 이름을 한 곳에 모은 커밋이 원인을 이렇게 적어 두었다.
|
||||
|
||||
> 표가 화면마다 복사되어 **여섯 벌**이었고 그래서 갈라졌다: 같은 QUESTION 이 공개 화면에서 "Open Question", 작업본 목록과 게시 기록에서 "Question", 편집기 상태 줄에서 "QUESTION" 이었다. **쓰는 사람은 같은 문서를 화면마다 다른 이름으로 만난다.**
|
||||
|
||||
## 표 하나로 모았다
|
||||
@@ -90,6 +92,8 @@ tech-log-frontend : dc2fda7 · ca1cfa2 계열 · 82e992d
|
||||
선택지 → 검토한 선택지
|
||||
```
|
||||
|
||||
쓰는 사람이 지금 채우는 칸이 공개 화면 어디로 가는지 외우지 않아도 되게 했다.
|
||||
|
||||
## 확인하지 못한 것
|
||||
|
||||
이름을 바꾸기 전보다 나빠진 화면이 홈 하나였다는 것은 화면으로 확인했다. 다른 화면에서 두 이름이 같이 뜨는 곳을 전수로 세지는 않았다.
|
||||
|
||||
+3
-1
@@ -41,10 +41,12 @@ UNION ALL SELECT 1 FROM topic_featured_document WHERE document_id = :id
|
||||
UNION ALL SELECT 1 FROM project_decision WHERE source_case_id = :id
|
||||
```
|
||||
|
||||
실제 사례에서 관계를 다 지워도 삭제가 안 됐다. 남아 있던 것은 프로젝트 링크 한 행이었다.
|
||||
실제 사례는 「DTO 변환 과정에서 발생한 Highlight 컬렉션 N+1」이다. 관계를 다 지워도 삭제가 안 됐고, 남아 있던 것은 프로젝트 「Liner N + 1문제」로 가는 링크 한 행이었다.
|
||||
|
||||
프로젝트 연결은 「관계」 편집기가 아니라 문서의 Project 필드다. 관계를 아무리 지워도 그 행은 남는다.
|
||||
|
||||
문구가 `another record` 라고 하니 관계를 찾아 지우게 되는데, 정작 막는 것은 record 가 아니라 프로젝트다. 문구가 잘못된 것을 가리키고 있다.
|
||||
|
||||
사용자는 Project 필드를 「미지정」으로 바꾸고 저장한 뒤 삭제했다.
|
||||
|
||||
## 가정
|
||||
|
||||
+3
-1
@@ -32,7 +32,7 @@ source:
|
||||
## 규칙
|
||||
|
||||
**톤을 지적받으면 고쳐 쓰기 전에 어떤 말을 쓸지 묻는다**
|
||||
「더 나은 문장」을 제안할 때와 그 사람의 말투로 쓸 때 필요한 것이 다르다.
|
||||
「더 나은 문장」을 제안하는 것과 그 사람의 말투로 쓰는 것은 다른 일이고, 후자는 제안하는 쪽이 잘하지 못한다.
|
||||
|
||||
**무엇이 AI 스러운지 구체적으로 받아 적는다**
|
||||
무엇을 하는지 말하지 않는 동사로 끝나는 것과 번역투가 실제로 지적된 두 가지였다.
|
||||
@@ -58,4 +58,6 @@ Case 소제목에 쓰는 말이 「~한 것」 명사형이면 새 제목도 그
|
||||
|
||||
「무엇을 견줬나」는 의문형 꼬리에 이 기록에서 쓰지 않는 낱말이었다. 작성자가 Case 소제목에 쓰는 말은 「이 구조에서 감수한 것」처럼 「~한 것」 명사형이라 그쪽에 맞췄다.
|
||||
|
||||
「이 프로젝트가 밝힌 것」은 「프로젝트를 통해 확인한 결과」로, 「운영 가능한 설계로 연결합니다」는 「실제 운영에 적용할 수 있는 형태로 정리합니다」로 바꿨다.
|
||||
|
||||
고쳐 쓴 첫 번째 안이 거절당한 뒤 사용자가 직접 쓴 텍스트를 실었다.
|
||||
|
||||
+1
-1
@@ -78,6 +78,6 @@ SOURCE_DATE_EPOCH
|
||||
|
||||
## 확인하지 못한 것
|
||||
|
||||
빌드가 이 인자를 요구하도록 막지 않았다. 빠뜨리면 여전히 빌드는 성공하고 배포본만 틀린다. 인자 목록을 적어 둔 것으로 그쳤다.
|
||||
빌드가 이 인자를 요구하도록 막지 않았다. 빠뜨리면 여전히 빌드는 성공하고 배포본만 틀린다. 인자 목록을 적어 두고 메모리에 한 건 남긴 것으로 그쳤다 — `techlog-deploy-runtime-api-base.md`.
|
||||
|
||||
<!-- body:end -->
|
||||
|
||||
+2
@@ -27,6 +27,8 @@ source:
|
||||
|
||||
## 문제
|
||||
|
||||
이 건은 같은 갈래의 다른 것들과 결이 다르다. 테스트가 아니라 생성기가 값을 버렸다.
|
||||
|
||||
파생 스펙을 파서에 넣으면 스키마 15개를 「is not of type `object`」로 거절했다. 그 스키마들은 전부 `type: object` 를 명시하고 있어서 계약 결함처럼 보이지 않았다.
|
||||
|
||||
`validateSpec` 을 끄면 생성이 성공한다. 그렇게 만든 모델을 컴파일하면 통과한다.
|
||||
|
||||
+1
-1
@@ -86,7 +86,7 @@ tech-log-backend : 92679f5 이후
|
||||
| 작성자가 쓴 이유 | 「다음에 무엇을 읽을지」의 답 | 공개 조회 경로: **아예 버려짐** |
|
||||
| 대상의 요약 | 대상이 무엇인지 | — |
|
||||
|
||||
셋을 `label` · `note` · `summary` 로 갈랐다. 설명 칸에는 작성자가 쓴 문장이 있으면 그것을, 없으면 대상의 요약을 보인다.
|
||||
셋을 `label` · `note` · `summary` 로 갈랐다. 설명 칸에는 작성자가 쓴 문장이 있으면 그것을, 없으면 대상의 요약을 보인다. 요약은 대상을 설명하고 문장은 왜 지금 이것을 읽어야 하는지를 설명한다.
|
||||
|
||||
## 확인하지 못한 것
|
||||
|
||||
|
||||
+2
@@ -54,6 +54,8 @@ PostgreSQL 테이블
|
||||
|
||||
저장 쪽에 둘, 백엔드 조립에 넷, 전선에 하나, 프론트엔드 조립에 셋, 화면에 하나다.
|
||||
|
||||
이 기간에 적은 결함의 절반 이상이 「이 중 한 경계가 값을 버렸다」는 같은 모양이었다.
|
||||
|
||||
## 어디서 검사가 끊기는가
|
||||
|
||||
경계마다 무엇이 값을 지키는지가 다르다.
|
||||
|
||||
+1
-1
@@ -76,6 +76,6 @@ tsconfig : 루트가 project references 만 나열
|
||||
|
||||
## 남은 것
|
||||
|
||||
루트 tsconfig 는 그대로 뒀다. 누군가 다시 `npx tsc --noEmit` 을 쓰는 것을 막는 검사는 없고, 배포 전에 돌릴 다섯 명령의 목록에 적어 뒀다.
|
||||
루트 tsconfig 는 그대로 뒀다. 누군가 다시 `npx tsc --noEmit` 을 쓰는 것을 막는 검사는 없고, 배포 전에 돌릴 다섯 명령의 목록에 적어 뒀다. 이 건은 메모리에도 남겨 뒀다 — `tech-log-frontend-typecheck-command.md`.
|
||||
|
||||
<!-- body:end -->
|
||||
|
||||
Reference in New Issue
Block a user