Skip to content

메모 스펙 정비: type 도입, 압축 원칙, 검사 자동화 - #18

Merged
cbcruk merged 22 commits into
mainfrom
claude/okf-memo-integration-review-cnd7su
Jul 28, 2026
Merged

메모 스펙 정비: type 도입, 압축 원칙, 검사 자동화#18
cbcruk merged 22 commits into
mainfrom
claude/okf-memo-integration-review-cnd7su

Conversation

@cbcruk

@cbcruk cbcruk commented Jul 28, 2026

Copy link
Copy Markdown
Owner

OKF(Open Knowledge Format)를 대조해보려던 게 메모 스펙 전반의 정비로 이어졌다. 결론부터: OKF에서 가져온 건 필드 이름 하나이고, 규칙은 이 저장소의 데이터가 정했다.

배경과 판단 근거는 577.md에 정리했다.

무엇이 바뀌었나

1. type — 형태를 데이터로 (542개 백필)

bookmarks / snippet / note. 형태만 담고 주제는 tags가 담당한다.

이전에는 유형이 CLAUDE.md 안에만 있었고, 유일하게 새어나온 bookmarks가 태그로 들어가서 유사도 계산에 예외(STRUCTURAL_TAGS)를 만들고 있었다. type으로 빼면서 그 예외를 지웠다.

분류 규칙은 예전에 손으로 붙여둔 bookmarks 태그 33개를 정답지로 검증했다. 처음 규칙은 그중 10개를 놓쳤는데 전부 링크 1개짜리 메모였다 — 링크 하나만 저장해둔 것도 "링크 모음"이라는 기준이 그때 드러났다. 최종 33/33 일치.

bookmarks 282
snippet 189
note 71

2. archivedraft의 이중 의미 분리

bookmarks 274개 중 231개(84%)가 draft였다. 그런데 링크 모음에는 "작성 중"이 없다 — 붙여넣은 순간이 최종형이다. draft가 "미완성"과 "공개 안 함" 두 뜻으로 쓰이면서 작성 중 큐 268개가 완성될 일 없는 링크로 막혀 있었다.

작성 중 큐 268 → 37. 공개(release) 274개는 변화 없음.

3. 압축 — 남의 문장 걷어내기 (21개 메모)

번역문·AI 요약은 존댓말("~합니다")로 들어오고 저자 문체는 평서체("~다")다. 그 차이로 압축 대상을 찾았다.

가장 선명했던 건 268 — "이것이 바로 제가 Tachyon을 만든 이유이며". 메모 안의 1인칭이 저자가 아니었다(벤더 홍보문 번역). 274는 AI가 지어낸 우화였고, 272는 Graphite 문서 목차를 절 단위로 번역한 것이었다(같은 내용이 두 단락에 반복).

272 17줄 2줄
329 50줄 3줄
271 14줄 링크 3개

줄이니 형태가 드러났다 — 268은 군더더기를 걷어내자 남는 게 링크뿐이어서 notebookmarks가 됐다. 이렇게 type이 바뀐 메모가 10개.

검출 29 → 0 (공개 23 → 0). 남은 309는 저자 본인이 존댓말로 쓴 작업 기록이라 voice: author로 표시해 제외했다.

4. 검사 자동화

원칙을 사람 기억이 아니라 코드에 넣었다.

검사 대상 빌드
lint:memo type ↔ 본문, 각주 참조 ↔ 정의 게이트
lint:voice 남의 문장 (문체) 게이트 (release만)
lint:links 죽은 링크 미연결 (네트워크 의존)
find-related parent·통합 후보 도구

게이트는 논리적 모순에만 걸려 있다. 판단이 필요한 것(휴리스틱 불일치, 문체)은 경고이고, draft는 작성 중이라 항상 경고까지만이다.

5. URL 겹침을 관계 신호로

같은 글을 가리키는 메모쌍 29개 중 14쌍은 공유 태그가 0개라 태그 IDF로는 영원히 만나지 못한다. 사이트 "관련 메모"와 find-related 둘 다 공유 URL을 신호로 쓴다.

공개 274개에 대한 영향: 관련 메모가 아예 없던 2개(#197·#485)가 연결되고, 1위가 바뀐 게 11개, 새로 목록에 든 항목 7개. 새 연결 4쌍 전부 확인했다 — 예를 들어 #51↔#485는 태그가 하나도 겹치지 않는데 둘 다 concurrently를 참조한다.

그 후보로 첫 계보 3쌍을 연결했다(parent 0 → 3). 도구가 준 6쌍 중 3쌍만 — 나머지는 주제만 겹쳐서 자동 관련 메모로 충분하다.

6. 고친 버그

  • /memos/all이 status 무관하게 542개 전부를 렌더하고 있었다. 비공개 268개가 본문까지 공개되고, 상세 페이지는 release만 생성되니 268개가 404로 연결됐다 → 274개, 깨진 링크 0
  • 각주 짝이 어긋난 7개 파일. 정의가 남아 있던 3개는 번호 순서가 위치를 알려줘서 마커를 복원했다(삭제가 아니라 복원)
  • create-memo.mjstype 없이 프론트매터를 만들어 새 메모마다 스키마 검증에 걸릴 상태였다
  • 발췌에 각주 마커([^235-1])가 그대로 남던 것

7. 잃어버린 경로 복구

bookmarks를 태그에서 type으로 옮기면서 /tagged/bookmarks가 사라졌다. /type/[type] 목록을 만들고 /tagged 인덱스에 입구를 뒀다.

안 가져온 것 (근거 있음)

  • stale_after — 버전을 고정한 메모가 10개뿐이라 만료를 선언할 표면이 없다. 게다가 시간축이 오염돼 있다(ctime 182개·mtime 288개가 일괄값). 그래서 신선도는 날짜로 선언하지 않고 링크로 판정한다
  • resource — URL이 2개 이상인 메모가 359개라 대상을 하나로 특정할 수 없다
  • verified / 신뢰 등급 — 단일 저자라 전부 같은 값

확인

pnpm lint:memo    →  메모 542개 검사 — 오류 0, 경고 0
pnpm lint:voice   →  542개 중 0개에 남의 문장이 남아 있다
pnpm build        →  735 page(s) built
  • 두 린터 모두 픽스처로 검증했다(오류·경고 분기, 예외 플래그, 코드 블록·인용 제외)
  • lint:links는 로컬 서버로 상태 코드별 동작을 확인했다 — 404·410은 죽음, 403은 "확인 불가", HEAD 막히면 GET 재시도. 거짓 양성을 만들지 않는 게 설계 목표라 죽었다고 단정하는 건 404·410·DNS 실패뿐이다
  • 공개 범위는 변하지 않았다 — 상세 페이지·태그 페이지·RSS·사이트맵 모두 release 274개 기준으로 재확인

남은 것

  • 죽은 링크 9개lint:links가 찾았다. #78은 본인 저장소가 404이고 #58blog.sindresorhus.com은 도메인이 사라졌다. 전체 검사는 네트워크가 되는 로컬에서 실행해야 한다
  • draft 37개 — 이제 진짜 작성 큐다
  • vitest가 startup error로 실행되지 않는다(astro 7의 vite 플러그인 vs vitest의 vite 5). 이 브랜치와 무관하며 main에서도 동일하게 재현된다

🤖 Generated with Claude Code

https://claude.ai/code/session_01Qv2pQrCq9z1GaxxDu2vohK


Generated by Claude Code

claude added 22 commits July 27, 2026 07:51
type을 프론트매터 필수 필드로 두는 OKF 방식과 대조해서
현재 메모 스펙의 두 지점을 정리:
- bookmarks가 태그로 들어가 유사도 계산에 예외를 만든 것
- mtime이 일괄 수정으로 오염되어 신선도 신호가 아닌 것

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qv2pQrCq9z1GaxxDu2vohK
type은 형태만 담는다 — bookmarks / snippet / note.
debug·comparison 같은 목적/주제는 tags에 남긴다.

- content.config.ts: type을 필수 필드로 추가
- 542개 메모 백필 (bookmarks 274 / snippet 188 / note 80)
- bookmarks 태그를 type으로 이동, 유사도 계산의
  STRUCTURAL_TAGS 예외 제거
- CLAUDE.md: Type 기준 + 템플릿 6종 frontmatter 갱신

분류 규칙은 예전에 손으로 붙여둔 bookmarks 태그 33개를
정답지로 검증했다 (33/33 일치).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qv2pQrCq9z1GaxxDu2vohK
draft가 "작성 중"과 "공개 안 함" 두 뜻으로 쓰이고 있었다.
bookmarks 274개 중 231개(84%)가 draft였는데, 링크 모음에는
"작성 중"이라는 상태가 없다 — 붙여넣은 순간이 최종형이다.
그 결과 작성 중 큐 268개가 완성될 일 없는 링크로 막혀 있었다.

- status에 archive 추가 (draft: 작성 중 / archive: 보관)
- bookmarks의 draft 231개 → archive
  작성 중 큐 268 → 37, 공개(release) 274개는 변화 없음
- /memos/archive 추가 (draft.astro와 동일한 dev 전용 뷰)
- CLAUDE.md: Status 기준 + Type A 템플릿 + 워크플로우 갱신

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qv2pQrCq9z1GaxxDu2vohK
getCollection('memo')을 필터 없이 써서 draft·archive 268개가
본문까지 공개되고 있었다. 게다가 링크 대상인 /memo/[id] 는
release만 생성되므로 268개가 404로 연결됐다.

getReleaseMemoCollection()으로 교체 — 다른 공개 경로(상세·태그·
RSS)와 동일한 필터를 쓴다.

- /memos/all: 542 → 274개
- 깨진 링크 268 → 0

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qv2pQrCq9z1GaxxDu2vohK
분류기를 검증기로 뒤집어 새 메모의 drift를 빌드에서 잡는다.

- scripts/lint-memo.mjs: 오류(논리적 모순) / 경고(휴리스틱 불일치)
  2단계. draft 는 아직 형태가 안 잡혔을 수 있어 경고까지만.
  본문이 빈 메모는 판정 불가라 건너뜀.
- build 에 연결 + lint:memo 스크립트. 현재 542개 오류 0, 경고 0
- create-memo.mjs 가 type 없이 프론트매터를 만들어 새 메모마다
  스키마 검증에 걸리던 문제 수정. 첫 인자로 type 을 받는다
  (bookmarks 는 status 를 archive 로)

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qv2pQrCq9z1GaxxDu2vohK
문체(존댓말)로 검출한 provenance 문제. 둘 다 공개 중이었다.

274: 본문 전체가 '에릭'이라는 개발자가 등장하는 창작 우화였다.
     출처는 구글 검색 URL 하나. 저자의 내용이 없으므로 용어
     정의 + 실제 출처만 남기고 draft 로 내렸다.

268: 벤더 홍보문 번역이었다 — "이것이 바로 제가 Tachyon을 만든
     이유이며" 처럼 1인칭이 저자가 아니었다. 사실만 남겼다
     (prefetch=내려받기만 / prerender=렌더링까지, Tachyon 동작 방식).
     링크 모음 형태가 됐으므로 type 도 bookmarks 로.

rel=prerender 의 폐기 여부는 출처를 확인할 수 없어 쓰지 않았다.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qv2pQrCq9z1GaxxDu2vohK
Graphite 문서의 목차를 절 단위로 번역한 요약이었다
("Why stack", "What is stacking", "Splitting" ...).
게다가 두 단락이 거의 같은 내용을 반복했다.

스택을 기능 브랜치와 구분하는 사실과 병합 순서만 남겼다.
링크 5개는 저자가 고른 것이라 그대로 둔다.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qv2pQrCq9z1GaxxDu2vohK
577 은 "OKF 도입 검토"라는 틀이었는데, 그러면 나중에 읽어도
"그래서 OKF가 뭐였지"가 된다. 실제 주인공은 압축이었다.

- 577: 첫 줄을 압축으로. OKF 는 규칙집이 아니라 압축 대상을
  찾는 방법(문체)을 열어준 것으로 정리. tags 도 okf →
  memo-spec/compression/provenance. status: release
  (CLAUDE.md 가 세 곳에서 577 을 근거로 인용하고 있어서
  비공개면 인용이 끊긴다)
- CLAUDE.md: "압축 (필수)" 절 추가 — 존댓말 = 남의 문장 신호,
  인용은 표시, 1인칭 확인, 걷어내면 형태가 드러남

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qv2pQrCq9z1GaxxDu2vohK
46·269·173·21·83. 링크는 저자가 고른 것이라 그대로 두고
기계가 붙인 설명만 지웠다. 새로 쓴 문장은 없다.

- 46: "유연성/재사용 가능성/테스트 가능성..." 일반적 장점 7줄
- 269: 라이브러리 소개 + "주요 기능 및 특징" (저자가 쓴
       선택 이유 한 줄은 남김)
- 173·21: 번역체 도입 단락
- 83: 산문이 아니라 각주가 부풀려져 있었다 (각주 하나가 5문장).
       참조와 정의를 함께 지워 짝이 깨지지 않게 했고,
       같이 달려 있던 grep.app 링크는 살렸다

걷어내니 형태가 드러나 46·269 는 note → bookmarks 로 바뀌었다
(린터가 잡아줬다). 문체 검출 26 → 21개.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qv2pQrCq9z1GaxxDu2vohK
빌드에는 연결하지 않는다. 문체는 논리적 모순이 아니라 판단이라
경고밖에 못 되고, 지금 게이트로 걸면 20개가 상주해서 린터를
무시하게 된다. 필요할 때 돌리는 도구로 둔다.

scripts/lint-voice.mjs (pnpm lint:voice, --release, --strict)
- 코드 블록과 인용(>)은 제외 — 출처를 밝힌 인용은 정직하다
- 산문과 각주를 나눠서 보고 (83.md 는 산문이 아니라 각주가
  부풀려져 있었다)
- "니까?"는 저자 본인의 질문 문장에도 나와서 제외 (218.mdx 오검출)
- "니다"로 일반화하면 평서체인 "아니다"가 걸려서 어미를 명시.
  덕분에 "보여줍니다" 같은 형태를 놓치던 23.md 가 잡힌다
- 픽스처 5종으로 제외 규칙 검증

create-memo 스킬: "압축 확인 (필수)" 단계 추가 (문체·1인칭·반복·각주).
status 안내가 archive 도입 전 내용이라 함께 갱신.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qv2pQrCq9z1GaxxDu2vohK
정의 없는 참조 3개 / 참조 없는 정의 4개.

- 106·245·57: 정의가 아예 없어 살릴 게 없다 → 마커 삭제
- 15·121·127: 정의는 남아 있고 번호 순서가 위치를 알려줬다
  (121 은 마커가 1,2,3,4,_,6 으로 비어 있었다) → 마커 복원.
  삭제가 아니라 저자 의도 복원이다
- 233: [^233-2] 는 붙을 링크가 없고 내용도 번역문이라 삭제

lint-memo.mjs: 각주 참조/정의 짝 검사 (draft 는 경고).
인라인 코드를 지워 정규식 문자클래스를 각주로 오인하지 않게 했다
(197·3 이 그 오검출이었다). 픽스처 4종으로 검증.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qv2pQrCq9z1GaxxDu2vohK
코드는 저자 것이라 그대로 두고 번역·AI 산문만 손봤다.

- 56: 번역문 2곳 → "스택 트레이스가 남지 않는다" 한 줄.
      code 가 주인공이라 type note → snippet
- 243: CSS Font Loading API 소개 3단락 삭제 (코드가 이미
       보여주는 내용). 문장에 박혀 있던 MDN 링크는 참고로 남김
- 307: Suspense 동작 4단락 → 한 줄 (2·3 단락이 같은 말 반복)
- 149: 번역된 Given/When/Then 설명 4불릿 → 한 줄.
       Gherkin 예시 블록 2개는 유지

309·329 는 남겨둔다 — 저자 본인이 쓴 글이라 성격이 다르다.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qv2pQrCq9z1GaxxDu2vohK
mermaid 다이어그램 2개 + 번호 설명 10단계였다. AI 코드 워크스루로
보이는데(다이어그램 노드에도 "이벤트가 존재합니까?" 같은 존댓말),
재사용되는 건 API 두 개였다.

- Calendar.Freebusy.query 로 바쁜 일정 → 빈 슬롯 계산
- CalendarApp.getCalendarById 로 이벤트 생성 + 초대 메일
- Freebusy 를 두 번 조회하는 이유 (조회와 예약 사이의 간격)

다이어그램이 사라져 코드 블록이 없어졌으므로 type snippet → note.

309 는 저자 본인이 쓴 글이라 그대로 둔다.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qv2pQrCq9z1GaxxDu2vohK
309 를 그대로 두기로 하면서 도구의 성격이 확정됐다. 저자가 존댓말로
쓴 자기 글(309)과 번역문(268)이 같은 신호로 잡히므로, 판정은
사람이 한다. 예외로 남긴 것(309·218)을 기록해서 나중에
"왜 이건 안 고쳤지"로 되돌아가지 않게 한다.

- scripts/lint-voice.mjs: 헤더 주석에 한계와 두 사례 대비
- CLAUDE.md 압축 절: 검출 명령 + 예외 목록
- 변경 이력을 실제 수치로 갱신 (29 → 15, 공개 23 → 9)

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qv2pQrCq9z1GaxxDu2vohK
검출이 산문/각주로 나뉘면서 절반이 각주 문제였던 게 드러났다.

각주만 문제였던 4개 (83 과 같은 유형):
- 19: AI 도입문 + 부풀린 각주 4개 삭제. math.js 링크는 살림
- 24: 각주를 본문에 박혀 있던 기술 링크만 남김
      (Three.js/Mapbox, d3-geo, satellite.js)
- 221: 번역 요약은 삭제하되 피드백 사다리(산/볼더/자갈/모래/먼지)는
       이 메모의 실체라 유지. [^221-3]은 저자 본인 문장만 남김
- 312: 본문은 저자 평서체 기록이라 그대로. 번역문인 각주만 줄임

산문이 본문이었던 4개:
- 247: 링크 제목이 이미 말하는 설명 2개 삭제 → 링크만
- 271: deep diff 알고리즘 설명이 전부 AI 산문(코드도 없다).
       라이브러리 3개가 실체
- 340: UI 클릭 7단계 삭제, COUNTIF 수식이 주인공
- 23: AI 도입문 삭제. 도입문에 달려 있던 데모 URL은 링크로 옮김

19·24 는 마커를 먼저 지워 정의가 고아가 됐는데 lint:memo 가 잡았다.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qv2pQrCq9z1GaxxDu2vohK
archive 3개는 각주, draft 3개는 산문이 문제였다.

- 123: 번역된 라이브러리 소개 각주 5개를 "무엇인지"만 남김
- 125: Protobuf 소개 축약 + 중복 링크 제거
       (oreilly ch04 이 두 목록에 각각 들어 있었다)
- 171: 각주 1~4 는 이미 짧은데 5~7 만 번역체라 형태를 맞춤
- 182: README 번역 단락만 삭제. 저자 코멘트("그닥 부정적이다")와
       인용 표시된 블록은 그대로 — 출처를 밝힌 인용은 정직하다
- 256: 4단락 AI 비교문 → 실제 차이 한 줄
       (모듈 페더레이션은 빌드 레벨, single-spa 는 런타임 레벨)
- 267: 강의 목차 번역에서 "~하는 방법에 대해 다룹니다" 를 걷어내고
       항목만 남김 (구분자 목록 같은 구체적인 건 보존)

남은 1개는 309 — 저자 본인이 존댓말로 쓴 글이라 예외로 둔 것.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qv2pQrCq9z1GaxxDu2vohK
예외를 "린트 무시"가 아니라 사실로 적는다 — voice: author 는
"이 메모의 존댓말은 저자 본인의 것"이라는 선언이다. OKF 의
generated.by 와 같은 성격이고, 577 에서 유일하게 값이 있다고
본 provenance 축이다.

- content.config.ts: voice: z.enum(['author']).optional()
- 309.md: voice: author (본인의 Enzyme → RTL 작업 기록)
- lint-voice.mjs: 플래그가 붙은 메모는 검사에서 제외.
  release 는 오류, draft·archive 는 경고 — 작성 중에는 원문을
  붙여놓고 줄이는 단계가 있고 archive 는 공개되지 않는다.
  실패 시 무엇을 하라는지(줄이기/인용 표시/voice) 안내한다
- build: lint-memo && lint-voice --strict && astro build

픽스처로 게이트 동작 확인 — release 검출 exit 1,
draft·archive·voice:author 는 exit 0.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qv2pQrCq9z1GaxxDu2vohK
태그만 보면 놓치는 관계가 있다. 같은 글을 가리키는 메모쌍 29개 중
14쌍은 공유 태그가 0개여서 태그 IDF 로는 영원히 만나지 못한다.
같은 URL을 가리킨다는 건 태그보다 직접적인 증거다.

- 메모 ID 로 호출하면 태그 + URL 둘 다 본다 (태그로 호출하면
  파일이 아직 없을 수 있으므로 태그만)
- 어떤 URL이 겹치는지 함께 출력한다
- URL 정규화는 쿼리를 유지한다 (youtube.com/watch?v=... 가
  전부 같은 URL로 뭉치는 문제)
- --all: 비공개까지. 29쌍 중 16쌍은 양쪽 다 비공개라 기본으로는
  안 보이는데, 그건 계보가 아니라 통합 후보다
- IDF 는 공개 코퍼스로 고정해 --all 여부로 점수가 흔들리지 않게
- 남아 있던 STRUCTURAL_TAGS 잔해 제거 (태그는 이미 type 으로 옮겼다)

검증: 229 → 235 가 공유 URL 2개로 1위 (dotenv-expand, SO 질문).
106 → 365 는 --all 에서만 나온다 (365 가 archive).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qv2pQrCq9z1GaxxDu2vohK
태그 IDF 만 보면 놓치는 관계가 있다. getRelatedMemos 에 공유 URL
개수를 더한다 (find-related.mjs 와 같은 가중치).

공개 274개에 대한 영향:
- 관련 메모가 아예 없다가 생긴 메모 2개 (#197, #485 — 태그로는
  어디에도 걸리지 않는데 URL 로 연결된다)
- 1위가 바뀐 메모 11개
- URL 때문에 새로 목록에 든 항목 7개 = 4쌍

새로 연결된 4쌍 전부 확인했다:
- 51 ↔ 485  concurrently (CRA+tailwind 동시 실행 / 병렬·순차 실행)
  — 태그가 하나도 겹치지 않는다
- 315 ↔ 327 vitest 가짜 타이머
- 197 ↔ 255 vite JSX 파싱 discussion

- URL 정규화는 쿼리 유지 (youtube.com/watch?v=... 뭉침 방지)
- 본문 파싱은 memo.id 로 캐시 — 페이지마다 274개를 다시 훑지 않게
- 태그가 비어도 URL 이 있으면 후보를 찾는다 (이전엔 즉시 [] 반환)

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qv2pQrCq9z1GaxxDu2vohK
mtime 이 신선도가 아니라서(288개가 일괄값) 신선도는 링크로 판정한다.
코퍼스 절반이 bookmarks 이고 본문이 링크뿐이라, 죽은 링크는 곧
메모 전체가 무효라는 뜻이다.

빌드에는 연결하지 않는다 — 네트워크에 의존하고 느리고, 남의 서버
상태로 내 빌드가 깨지면 안 된다.

거짓 양성을 만들지 않는 데 신경 썼다:
- 죽음으로 단정하는 건 404·410·ENOTFOUND 뿐. 403·405·429·타임아웃은
  "확인 불가"로 따로 센다 (봇 차단이 흔하다)
- HEAD 를 막는 서버가 많아 405·501·403 이면 GET 으로 재시도
- 연결 실패가 절반을 넘으면 판정하지 않고 exit 2 (오프라인·프록시)
- 확인 불가가 절반을 넘으면 판정을 신뢰하지 말라고 알린다 —
  이 저장소의 원격 실행 환경이 CONNECT 를 막고 403 을 돌려준다
- 코드 블록 안의 URL 은 예시일 수 있어 제외
- 같은 URL 을 여러 메모가 가리키면 한 번만 확인하고 소유 메모를 함께 표시

로컬 서버로 상태 코드별 동작 검증: 404·410 → 죽음, 403 → 확인 불가,
405(HEAD) → GET 재시도 후 살아 있음, 302 → 따라간 뒤 살아 있음,
코드 블록 URL 제외, 중복 URL 1회 확인, --strict exit 1.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qv2pQrCq9z1GaxxDu2vohK
bookmarks 를 태그에서 type 으로 옮기면서 /tagged/bookmarks 가
사라졌는데 대체 경로가 없었다. tagged/[tag].astro 와 같은 구조로
형태별 목록을 만든다.

입구는 /tagged 인덱스에 뒀다 — 형태는 태그가 아니지만 훑어보는
자리는 같아서, 태그 구름 위에 개수와 함께 링크한다.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qv2pQrCq9z1GaxxDu2vohK
find-related 가 제시한 URL 겹침 후보 6쌍(release↔release)을 읽고
판단했다. 3쌍은 계보고 3쌍은 주제만 겹친다.

연결:
- 235 → 229  같은 문제(.env 버전 문자열)를 3주 뒤 이어감.
             235 는 timestamp 대안을 보고 결국 같은 결론
- 327 → 315  가짜 타이머. 315 는 vitest/jest 개요,
             327 은 Countdown 에 적용한 구체 사례
- 255 → 197  197 은 정규식으로 JSX 파일을 찾아 git mv 했고,
             255 는 같은 판별을 acorn-jsx 파서로 제대로 한다

연결 안 함 (주제만 겹침 — 자동 관련 메모로 충분):
- 145~148  일반 테스트 가이드 vs mocking. ctime 이 둘 다
           마이그레이션 일괄값이라 순서도 알 수 없다
- 28~510   캘린더 라이브러리 모음 vs 겹침 처리 알고리즘
- 51~485   같은 도구(concurrently)를 다른 목적으로 쓴 것

부수 수정: 발췌에 각주 참조 마커가 그대로 남아 있었다
([^235-1] 등). 계보 라벨에서 드러나 clean() 에서 제거 —
관련 메모 목록에도 같이 적용된다.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qv2pQrCq9z1GaxxDu2vohK
@bolt-new-by-stackblitz

Copy link
Copy Markdown

Review PR in StackBlitz Codeflow Run & review this pull request in StackBlitz Codeflow.

@vercel

vercel Bot commented Jul 28, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
cbcruk-github-io Ready Ready Preview, Comment Jul 28, 2026 3:30am

@cbcruk
cbcruk merged commit a86c6a0 into main Jul 28, 2026
2 of 3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants