unlazy는 Claude Code와 Codex CLI에 설치하는 “완료 규율” 스킬입니다. 한 문장으로 줄이면 이렇습니다 — 에이전트가 “끝냈습니다”라고 말하는 것과, 끝났다는 사실이 실제로 참인 것은 다른 일이라는 전제 위에 만들어진 도구입니다.
작동 방식은 의외로 단순합니다. 작업을 시작할 때 GATES.md라는 파일에 “이 일이 끝났다면 이 명령이 이렇게 나와야 한다”를 미리 적어 둡니다. 그리고 에이전트가 완료를 선언하려 할 때, gate-check.mjs라는 CLI가 그 명령을 진짜로 실행합니다. 종료 코드가 0이고 출력이 기대한 패턴과 맞아야만 그 항목이 “met(충족)”으로 바뀝니다.
GATES.md에 적힌 검증 항목 하나를 뜻합니다. 각 게이트에는 실행할 명령(CHECK:)과 기대하는 출력(EXPECT:)이 붙어 있습니다.GATES.md가 원장이고, 대화 기록이 아니라 파일이 진실의 원천입니다. 대화창이 초기화돼도 원장은 남습니다.말로 하는 보고는 검증할 수 없습니다. 그런데 “책상 위에 물건이 0개인 사진”이라는 조건을 일을 시키기 전에 못 박아 두면, 보고가 참인지 거짓인지를 사람의 판단 없이도 가릴 수 있습니다.
unlazy가 하는 일이 딱 이것입니다. 다만 사진 대신 셸 명령의 종료 코드와 출력을 증거로 씁니다. 그리고 그 조건은 일이 시작되기 전에 확정돼야 합니다 — 나중에 조건을 낮추면 아무 의미가 없기 때문입니다.
실제 사용은 이렇게 생겼습니다.
# 1) 스킬 설치
npx skills add Leonxlnx/unlazy
# 2) 에이전트에게 지시 (트리 깊이 5로 분해해서 하라는 뜻)
/unlazy tree 5 결제 모듈을 리팩터링하고 모든 마이그레이션 경로를 검증해줘
# 3) 게이트 상태만 확인 — 아무것도 실행하지 않음
node scripts/gate-check.mjs --status GATES.md
# 4) 사람이 검토한 뒤 승인 + 실제 실행
node scripts/gate-check.mjs --approve GATES.md
--status와 --approve가 나뉘어 있다는 점이 중요합니다. --status는 원장을 읽기만 하고 어떤 명령도 실행하지 않습니다. 실행은 사람이 명시적으로 승인한 뒤에만 일어납니다. 왜 이렇게 나눴는지는 4장에서 다룹니다.
이 저장소는 2026년 8월 9일에 만들어졌습니다. 이 글을 쓰는 8월 24일 기준 15일 만에 ⭐1,875 · 포크 110개입니다. 코드는 스크립트 5개, 총 1,570줄 남짓입니다. 무엇이 이 속도를 만들었을까요.
AI 코딩 에이전트를 써 본 사람이면 익숙한 장면이 있습니다. 파일 10개를 고쳐야 하는데 3개만 고치고 “주요 변경을 완료했습니다”라고 하거나, 테스트를 돌리지도 않고 “통과할 것으로 예상됩니다”라고 하거나, 어려운 부분에 // TODO: 나중에 구현을 남기고 끝내는 경우입니다.
| 접근 | 방식 | 한계 |
|---|---|---|
| “끝까지 해” 프롬프트 | 사람이 계속 재촉 | 사람이 매번 지켜봐야 함. 모델이 또 거짓 완료를 하면 다시 반복 |
| Plan 모드 · TodoWrite | 계획을 먼저 쓰게 함 | 계획을 세우는 것과 계획대로 했는지 확인하는 것은 별개. 체크박스는 모델이 스스로 체크함 |
| 멀티에이전트 프레임워크 (BMAD-METHOD, Claude-Flow류) | 검토자 에이전트를 따로 둠 | 프레임워크를 통째로 도입해야 함. 검토자도 결국 LLM이라 같은 방식으로 속을 수 있음 |
| unlazy | 검증 명령을 실제 프로세스로 실행 | 검증 명령을 사람이 잘 써야 함 (뒤에서 다룸) |
핵심 차이는 증거의 출처입니다. 앞의 세 가지는 모두 최종 판단을 언어 모델에게 맡깁니다. unlazy는 판단을 운영체제에게 맡깁니다. npm test가 exit 1을 내면 그건 협상의 여지가 없는 사실입니다.
더 흥미로운 건 선택 기능인 Stop hook입니다. Claude Code에는 세션이 끝나려 할 때 외부 프로그램에게 물어보는 훅이 있는데, unlazy는 여기에 scripts/stop-hook.mjs를 꽂습니다. 게이트가 아직 안 끝났으면 {"decision":"block"}을 돌려줘서 세션 종료 자체를 플랫폼 레벨에서 거부합니다 — 모델이 지침을 무시하기로 결심해도 소용이 없다는 뜻입니다.
프롬프트가 “퇴근 전에 정리하고 가세요”라는 메모라면, Stop hook은 출입문 잠금장치입니다. 메모는 무시할 수 있지만 문은 안 열립니다.
이 저장소가 개발자들 사이에서 화제가 된 또 하나의 이유는 문서 태도입니다. 초기 버전의 README에는 “6회 비교 실험으로 효과를 입증했다”는 주장이 있었는데, 저자가 스스로 그 주장을 철회했습니다. 원자료가 남아 있지 않아 재현할 수 없다는 이유였습니다.
철회로 끝내지 않고, 앞으로 어떻게 실험해야 재현 가능한지를 문서로 남겼습니다. 인용한 arXiv 논문들에 대해서도 “이들은 문제가 실재한다는 근거이지, unlazy가 그 문제를 해결한다는 근거가 아니다”라고 선을 긋습니다.
AI 도구 마케팅 문서 대부분이 검증되지 않은 수치를 인용하는 상황에서, 이 태도 자체가 신뢰 신호로 읽혔습니다.
커뮤니티 반응도 실제로 코드에 반영됐습니다. 외부 기여자 최소 5명의 PR이 머지됐고, 총 커밋 31개 중 상당수가 Windows 호환성·타임아웃 처리 같은 실사용 버그 수정입니다.
이 저장소의 package.json에는 dependencies 필드 자체가 없습니다. 실수가 아니라 설계입니다.
{
"name": "unlazy-skill",
"version": "2.1.0",
"private": true,
"type": "module",
"engines": { "node": ">=16" }
// dependencies · devDependencies 둘 다 없음
}
이 도구는 남의 컴퓨터에서 셸 명령을 실행하는 프로그램입니다. npm 패키지 하나를 끌어오면 그 패키지의 의존성, 또 그 의존성의 의존성이 전부 같은 권한으로 돌아갑니다. 공급망 공격의 표적이 되기 딱 좋은 자리입니다.
그래서 저자는 파일 잠금·원자적 쓰기·프로세스 그룹 종료 같은 까다로운 기능까지 전부 Node 표준 라이브러리로 직접 구현했습니다. 이 선택이 4장에서 볼 코드의 성격을 거의 다 결정합니다.
| 항목 | 값 | 메모 |
|---|---|---|
| 언어 | JavaScript 100% | 약 122KB. TypeScript 아님, 빌드 단계 없음 |
| 모듈 방식 | ESM (.mjs) | "type":"module" + 확장자 .mjs로 이중 명시 |
| 런타임 | Node.js 16 이상 | engines.node에 명시. CI가 16/20/24 세 버전 검증 |
| 런타임 의존성 | 0개 | 표준 모듈만: node:fs node:child_process node:worker_threads node:crypto node:path node:os node:url |
| 패키지 이름 | unlazy-skill@2.1.0 | private:true — npm 레지스트리에 올라가지 않음 |
| 라이선스 | MIT (2026, Leonxlnx) | 상업적 사용·수정·재배포 자유 |
npm이 아니라 npx skills add로 배포됩니다. 이건 vercel-labs가 만든 “AI 에이전트 스킬” 설치 CLI로, GitHub 저장소를 에이전트의 스킬 디렉토리에 직접 심어 줍니다.
npx skills add Leonxlnx/unlazy # 현재 프로젝트에 설치
npx skills add Leonxlnx/unlazy -g # 사용자 전역
npx skills add Leonxlnx/unlazy --all # 감지된 모든 에이전트에
# 수동 설치 시 위치
~/.claude/skills/unlazy # Claude Code
~/.codex/skills/unlazy # Codex CLI
.github/workflows/test.yml이 3(OS) × 3(Node 버전) = 9개 조합을 돌립니다. Windows를 진지하게 지원한다는 신호이고, 실제로 코드 곳곳에 Windows 전용 분기가 있습니다.
| 축 | 값 |
|---|---|
| OS | ubuntu-latest · windows-latest · macos-latest |
| Node | 16 · 20 · 24 |
| 액션 고정 | actions/checkout@3d3c42e...처럼 커밋 SHA로 핀 고정 (태그는 옮겨질 수 있으므로) |
테스트 프레임워크도 안 씁니다. Jest도 Mocha도 없이 tests/ 아래 .mjs 러너 4개가 child_process.execFile로 실제 CLI를 서브프로세스로 띄워 검증합니다.
| 스위트 | 테스트 수 | 확인하는 것 |
|---|---|---|
run-tests.mjs | 26 | 파싱·승인·실행 기본 동작 |
hardening-tests.mjs | 19 | 공격적 입력에 대한 방어 (ReDoS, 경로 탈출 등) |
stress-tests.mjs | 10 | 동시 실행·잠금 경합 |
self-check.mjs | 9 | 저장소 자체 무결성 (템플릿·문서 일관성) |
| 합계 64개 | 클론 후 npm test 실행 시 64/64 통과 확인 | |
데이터베이스가 없습니다. 상태는 전부 파일시스템에 있습니다.
| 경로 | 내용 |
|---|---|
GATES.md | 원장. 사람이 읽고 쓰는 Markdown |
~/.unlazy/approved/*.json | 승인 기록 (해시 기반, 5-2절 참고) |
.unlazy/<scope>/ | 병렬 작업 단위별 상태 |
.unlazy/locks/*.lease | 파일 소유권 리스(임대) 잠금 |
scripts/lib/gates.mjs의 parseGates()가 GATES.md를 읽습니다. 여기서 저자는 흔한 유혹을 거절했습니다 — 정규식으로 CHECK:를 찾아 긁어오지 않습니다.
GATES.md 안에 “이렇게 쓰면 됩니다”라며 코드블록으로 CHECK: rm -rf / 예시를 넣었다고 해봅시다. 단순 정규식 파서는 이 예시를 실제 게이트로 인식합니다.
그래서 parseGates()는 CommonMark의 코드 펜스 규칙(펜스 문자 종류·길이·들여쓰기가 열림/닫힘에서 일치해야 함)을 직접 구현해, 코드블록 안쪽을 확실히 건너뜁니다.
거부 규칙도 엄격합니다. 다음 중 하나라도 걸리면 파싱 에러로 전체를 거부합니다.
여기가 이 저장소에서 가장 배울 게 많은 부분입니다. 사용자가 게이트 하나를 승인하면, 그 승인은 명령 문자열이 아니라 실행 조건 전체의 SHA-256 해시에 묶입니다.
// scripts/gate-check.mjs
function oracle(file, gate) {
const cwd = resolvedGateCwd(gate, file);
return { schema: 1, check: gate.check, expect: gate.expect, cwd, shell,
timeoutMs: timeoutSeconds * 1000, maxOutputBytes: MAX_OUTPUT_BYTES,
regexTimeoutMs: REGEX_TIMEOUT_MS, platform: process.platform, path: pathValue };
}
function signature(file, gate) { return sha256(JSON.stringify(oracle(file, gate))); }
해시에 들어가는 항목을 보세요. 명령(check)과 기대값(expect)뿐 아니라 작업 디렉토리, 셸, 타임아웃, 플랫폼, 그리고 PATH 환경변수까지 들어갑니다. 하나라도 바뀌면 서명이 달라지고, 이전 승인은 자동으로 무효가 됩니다.
“이 열쇠로 이 문을 열어도 좋다”가 아니라 “이 열쇠로, 이 건물의, 이 층의, 이 시간대에, 이 문을 열어도 좋다”고 승인하는 것입니다. 건물이 바뀌면 승인이 안 따라옵니다.
npm test였는데, 실행 직전에 PATH가 바뀌어 다른 npm이 잡히는 경우. unlazy는 PATH를 서명에 포함시켜 이 창을 닫았습니다.EXPECT:에는 /pattern/flags 형태의 임의 정규식을 쓸 수 있습니다. 편리하지만 위험합니다 — 어떤 정규식은 특정 입력에서 사실상 영원히 안 끝납니다.
(a+)+b 같은 정규식에 aaaaaaaaaaaaaaaaaaaa를 넣으면 백트래킹이 폭발해 CPU가 몇 분씩 묶입니다. 정규식은 취소할 수 없기 때문에, 일단 시작하면 같은 스레드에서는 멈출 방법이 없습니다.해법은 우아합니다. 매 검사마다 워커 스레드를 새로 하나 띄우고, 250ms 타이머와 경쟁시킵니다. 타이머가 이기면 worker.terminate()로 스레드를 통째로 죽입니다. 워커 코드는 전부 9줄입니다.
// scripts/lib/regex-worker.mjs — 파일 전체
import { parentPort } from "node:worker_threads";
parentPort.once("message", ({ source, flags, output }) => {
try { parentPort.postMessage({ matched: new RegExp(source, flags).test(output) }); }
catch (error) { parentPort.postMessage({ error: error.message }); }
});
정규식뿐 아니라 무한 루프 가능성이 있는 사용자 제공 코드, 외부 라이브러리의 동기 파싱 등에 그대로 응용됩니다. 워커 생성 비용(수 ms)이 아깝지 않은 이유는, 그 대안이 “프로세스가 영원히 멈춤”이기 때문입니다.
병렬 서브에이전트 여러 개가 같은 파일을 동시에 고치면 서로 덮어씁니다. unlazy는 게이트에 OWNS: glob(파일 패턴)을 붙여 소유권을 선언하게 하고, .unlazy/locks/*.lease 파일로 조율합니다.
| 기법 | 구현 | 왜 |
|---|---|---|
| 배타적 생성 | fs.open(path, "wx") | wx는 파일이 이미 있으면 실패 — 이 실패가 곧 뮤텍스 |
| 원자적 쓰기 | writeAtomic(): 임시파일 → fsync → rename | 쓰다가 죽어도 원장이 반쪽 상태로 남지 않음 |
| Windows 재시도 | EBUSY/EACCES/EPERM에 지수 백오프 | 백신·인덱서가 핸들을 잠깐 잡는 현상 대응 |
| 동기 슬립 | Atomics.wait() on SharedArrayBuffer | 동기 함수 안에서 진짜로 블로킹해야 할 때 쓰는 정공법 |
겹침 판정도 보수적입니다. globsOverlap()은 두 패턴이 확실히 서로소임을 증명할 수 있을 때만 통과시킵니다. 소스 주석이 태도를 그대로 보여줍니다.
// scripts/lib/gates.mjs
// Prove disjointness only when literal path segments disagree. Everything else
// conflicts, including mid-segment pairs such as a* and ab*.
export function globsOverlap(left, right) { ... }
a*와 ab*는 사실 겹치지만, 겹치는지 아닌지 판정이 복잡합니다. 애매하면 충돌로 본다 — 잘못된 병렬 실행보다 불필요한 대기가 낫다는 판단입니다.
Stop hook은 강력한 만큼 위험합니다. 게이트가 영원히 충족되지 않으면 사용자는 세션을 끝낼 수 없게 됩니다. scripts/stop-hook.mjs는 이걸 원장 내용 해시로 감지합니다.
// scripts/stop-hook.mjs
const MAX_BLOCKS = 6;
// ...
if (sessionState.blocks > MAX_BLOCKS) {
allow("unlazy: releasing after " + MAX_BLOCKS + " blocks without gate progress" + where + "...");
}
차단할 때마다 원장의 해시를 기록해 두고, 6번 연속 해시가 그대로면 “진행이 없다”고 판단해 스스로 차단을 풉니다. 강제 장치가 사용자를 인질로 잡지 않게 하는 안전핀입니다.
README.md → 무엇을 주장하고 무엇을 주장하지 않는지references/gates.md → 원장 포맷. 이걸 알아야 코드가 읽힘scripts/lib/regex-worker.mjs → 9줄. 몸풀기scripts/stop-hook.mjs → 132줄. 훅 계약이 어떻게 생겼는지scripts/lib/gates.mjs → 575줄. 파서와 잠금의 본체scripts/gate-check.mjs → 690줄. 마지막에SECURITY.md → 코드를 본 뒤에 읽으면 “아 그래서 그렇게 짰구나”가 됨SKILL.md는 에이전트가 매번 컨텍스트에 싣는 파일입니다. 여기에 모든 규칙을 다 넣으면 토큰을 크게 잡아먹습니다. 그래서 핵심 지침만 SKILL.md에 두고, 상세 규칙은 references/로 빼서 필요할 때만 읽게 합니다.
references/token-economy.md가 이 프로젝트의 규칙을 명문화해 두었습니다.npm 패키지 없이 파일 잠금·원자적 쓰기·프로세스 그룹 종료를 구현한 실전 예제입니다. 특히 타임아웃 시 자식 프로세스를 죽이는 부분이 교육적입니다.
// scripts/gate-check.mjs
const stopChild = () => {
try {
if (process.platform === "win32") child.kill("SIGKILL");
else process.kill(-child.pid, "SIGKILL"); // 음수 PID = 프로세스 그룹 전체
} catch { try { child.kill("SIGKILL"); } catch {} }
};
process.kill(-pid)의 음수 PID는 “이 프로세스가 만든 자식들까지 통째로”를 뜻합니다. npm test를 죽였는데 그 안의 jest가 살아남는 흔한 버그를 이렇게 막습니다. Windows에는 프로세스 그룹 개념이 달라 분기가 필요합니다.
SECURITY.md의 한 문장이 이 저장소 전체를 요약합니다 — “승인은 동의이지 샌드박스가 아니다.”
4-2절의 오라클 해시 승인 모델은 MCP 서버, 코드 실행 도구, CI 러너 등 “LLM이 뭔가를 실행하는” 모든 프로젝트에 그대로 옮길 수 있는 설계입니다. 핵심은 승인 대상을 명령 문자열이 아니라 실행 컨텍스트 전체로 잡는 것입니다.
대부분의 Node 프로젝트가 대충 넘어가는 부분을 정직하게 처리합니다. 백신·검색 인덱서가 파일 핸들을 잠깐 쥐면 rename이 EBUSY로 실패하는데, unlazy는 EBUSY/EACCES/EPERM을 구분해 지수 백오프로 재시도합니다.
그리고 동기 함수 안에서 진짜 슬립이 필요할 때 SharedArrayBuffer + Atomics.wait()를 쓰는 이유 — setTimeout은 비동기라 동기 흐름을 못 멈추고, busy-wait 루프는 CPU를 태웁니다.
Markdown에서 값을 뽑을 때 /^CHECK:\s*(.+)$/m 한 줄로 끝내고 싶은 유혹이 큽니다. parseGates()는 CommonMark 펜스 규칙을 직접 구현해 그 유혹을 이겼습니다.
설정 파일·프론트매터·로그 파서를 쓸 일이 있다면, 이 함수가 “어디까지 엄격해야 하는가”의 좋은 기준선이 됩니다.
Stop hook은 stdin으로 JSON을 받고 stdout으로 JSON을 내보내는 단순한 계약입니다. stop-hook.mjs 132줄이 그 계약의 살아 있는 문서 역할을 합니다. install-hooks.mjs는 settings.json을 안전하게 수정·복원하는 법(멱등 설치, --uninstall)을 보여 줍니다.
기술이 아니라 태도지만, 배울 값어치가 있습니다. research/validation-protocol.md는 “우리 주장은 재현 불가능하다”를 명시하고, 인용 논문이 무엇을 뒷받침하고 무엇을 뒷받침하지 않는지를 구분합니다. 사내 기술 문서나 성능 개선 보고서를 쓸 때 참고할 만한 형식입니다.
| 항목 | 요구사항 | 근거 |
|---|---|---|
| 런타임 | Node.js 16 이상 | package.json의 engines.node, CI가 16/20/24 실증 |
| OS | Windows · macOS · Linux | CI 매트릭스에서 3종 모두 통과 |
| 메모리 | 사실상 무관 | 짧게 사는 자식 프로세스 + 검사당 워커 스레드 1개 |
| GPU | 불필요 | 모델 추론을 하지 않는 순수 검증 도구 |
| 디스크 | 수백 KB | 저장소 전체가 약 122KB의 JS + 문서 |
| 컨테이너 | 불필요 | Dockerfile 없음 (확인함) |
| 전제 도구 | Claude Code 또는 Codex CLI | 스킬로 쓰려면 필요. node만 있으면 gate-check.mjs 단독 실행 가능 |
| 상수 | 값 | 의미 |
|---|---|---|
REGEX_TIMEOUT_MS | 250ms | EXPECT: 정규식 매칭 제한. 넘으면 워커 강제 종료 |
MAX_OUTPUT_BYTES | 1 MiB | 검사당 캡처하는 출력 상한. 로그 폭탄 방어 |
MAX_BLOCKS | 6 | 진행 없이 연속 차단 횟수. 넘으면 서킷브레이커 발동 |
Windows에서는 process.env.ComSpec(보통 cmd.exe)이 기본 셸이라 grep·tail·tr 같은 유닉스 도구가 없습니다. CHECK:에 이런 명령을 쓰면 실패합니다 — 문서가 이 함정을 명시적으로 경고합니다.
또 Git Bash에서 승인하고 PowerShell에서 실행하면 PATH가 달라 승인이 무효화됩니다. 이건 버그가 아니라 4-2절의 의도된 동작입니다.
아무 프로젝트에나 GATES.md를 만들고 게이트 하나를 넣습니다.
# GATES.md
- ID: tests
CHECK: npm test
EXPECT: /(\d+) passing/
그다음 순서대로 해봅니다: ① --status로 파싱만 확인 → ② 일부러 테스트를 깨뜨리고 --approve → ③ 고친 뒤 --reverify. 목표는 “실행 없는 상태 조회”와 “승인 후 실행”이 어떻게 다른지 몸으로 아는 것입니다.
게이트 하나를 승인한 뒤, 다음을 각각 바꿔 보고 승인이 유지되는지 깨지는지 기록합니다.
cwd를 다른 디렉토리로PATH에 디렉토리 하나 추가그리고 ~/.unlazy/approved/를 열어 파일 이름이 어떻게 생겼는지 봅니다. 목표: 해시 기반 권한 모델이 실제로 어떤 변화를 감지하는지 체감.
EXPECT:에 파국적 백트래킹 정규식을 넣고, CHECK:가 그 패턴을 자극하는 긴 출력을 내게 만듭니다.
- ID: redos
CHECK: node -e "console.log('a'.repeat(40))"
EXPECT: /(a+)+b/
250ms 뒤에 워커가 죽는지 확인하고, 그다음 REGEX_TIMEOUT_MS를 크게 늘려서 어떻게 되는지 봅니다. 여유가 되면 regex-worker.mjs를 워커 없이 동기 실행하도록 고쳐 프로세스가 멈추는 걸 재현해 보세요. 목표: 격리가 없을 때 무슨 일이 벌어지는지 직접 목격.
gate-check.mjs의 오라클 해시 승인 모델만 떼어 내, 본인이 쓰는 도구에 붙여 봅니다. 후보:
어려운 지점은 해시에 무엇을 넣을지 고르는 일입니다. 너무 적게 넣으면 TOCTOU가 열리고, 너무 많이 넣으면 승인이 계속 깨져서 아무도 안 씁니다. 목표: 보안과 사용성 사이의 실제 트레이드오프를 손으로 겪기.
| 주차 | 주제 | 할 일 |
|---|---|---|
| 1주차 | Node 표준 라이브러리 정복 | node:fs의 플래그(wx·a·r+) 차이, fsync가 왜 필요한지, child_process의 spawn/exec/execFile 차이. 작은 원자적 쓰기 유틸을 직접 구현 |
| 2주차 | 동시성과 잠금 | 뮤텍스·리스·낙관적 잠금의 차이. worker_threads와 Atomics 실습. “DB 없이 조율하기”의 한계가 어디인지 정리 (references/parallel.md가 좋은 출발점) |
| 3주차 | 파싱과 신뢰 경계 | CommonMark 스펙의 코드 펜스 절 읽기. ReDoS 실습(regex101의 백트래킹 카운터 활용). “신뢰할 수 없는 입력”의 목록을 본인 프로젝트에서 뽑아 보기 |
| 4주차 | 에이전트 스킬·훅 설계 | Claude Code 훅 종류(PreToolUse·PostToolUse·Stop 등)와 각 계약. 본인 워크플로에 맞는 스킬 하나를 SKILL.md + references/ 구조로 직접 작성 |
CLAUDE.md 모음집류)와 코드로 푸는 unlazy를 나란히 놓고, 각각 어떤 상황에서 깨지는지 표로 정리| 용어 | 뜻 |
|---|---|
| 게이트 (gate) | GATES.md의 검증 항목 하나. ID·CHECK·EXPECT·OWNS로 구성 |
| 원장 (ledger) | 미완 작업을 기록하는 파일. 대화가 아니라 파일이 진실의 원천 |
| 오라클 (oracle) | “정답이 무엇인지 알려주는 것”. 여기서는 명령·기대값·cwd·셸·플랫폼·PATH를 묶은 실행 조건 전체 |
| fail-closed | 애매하면 막는 쪽으로 실패. 보안 도구의 기본 자세 |
| TOCTOU | 검사 시점과 사용 시점 사이에 조건이 바뀌어 생기는 취약점 |
| ReDoS | 백트래킹이 폭발하는 정규식으로 CPU를 묶는 공격 |
| 워커 스레드 | Node에서 별도 스레드로 JS를 실행하는 기능. terminate()로 강제 종료 가능한 게 핵심 |
| 원자적 쓰기 | 임시파일 → fsync → rename. 중간에 죽어도 반쪽 파일이 안 남음 |
| 리스 (lease) | 기한이 있는 소유권. 여기서는 파일 패턴에 대한 배타적 작업 권한 |
| 서킷브레이커 | 같은 실패가 반복되면 그 경로를 자동으로 끊는 패턴 |
| Stop hook | Claude Code 세션 종료 시 외부 프로그램에게 허용 여부를 묻는 훅 |
| Depth Tree | 작업을 N단계로 쪼개는 unlazy의 분해 방법론 (references/method.md) |
| 점진적 공개 | 핵심만 먼저 싣고 상세는 필요할 때 읽게 하는 토큰 절약 설계 |
ESM / .mjs | import/export를 쓰는 최신 JS 모듈 방식과 그 확장자 |
npx skills add의 정체| 스타 / 포크 | ⭐1,875 · 🍴110 · 열린 이슈 10 |
| 생성일 | 2026-08-09 |
| 최근 커밋 | 754d9a6 “fix: keep hook lock waits within host timeout” (2026-08-23) |
| 커밋 수 | 31개 · 외부 기여자 최소 5명 |
| 릴리스 | 없음 — 태그 0개. README가 “정확한 커밋을 pin하라”고 권고 |
| 버전 | package.json 2.1.0 (CHANGELOG상 Unreleased) |
① 샌드박스가 아닙니다. CHECK: 명령은 호출자의 권한·자격증명·네트워크를 그대로 물려받습니다. 검토 없이 --approve하면 위험합니다. 신뢰할 수 없는 코드에는 컨테이너 같은 별도 격리를 쓰라고 문서가 권고합니다.
② 효과는 미검증입니다. “깊이에 비례해 효과가 곱해진다”는 초기 주장은 저자가 철회했습니다. 게이트가 실행된다는 사실은 확실하지만, 그것이 결과물 품질을 얼마나 올리는지는 증명되지 않았습니다.
③ 아주 최근 저장소입니다. 생성 15일, 정식 릴리스 없음. 급성장은 사실이지만 장기 검증된 트랙 레코드는 아닙니다. 저장소 설명(topics)에는 아직 철회된 구버전 문구가 남아 있어 본문 문서와 미묘하게 어긋납니다.