lt 스택 유지보수 (모든 기본 리포지토리)
7개의 기본 리포지토리는 의존성 순서에 따라 유지보수되고 릴리스됩니다. 정식 소스는 github.com/lenneTech이며, 릴리스에는 각 리포지토리의 로컬 클론이 필요합니다. 체크아웃 경로는 머신마다 다르므로 가정하지 않고 위치를 찾으세요:
find "$HOME" -maxdepth 5 -type d -name nest-server-starter -not -path '*/node_modules/*' 2>/dev/null
리포지토리가 어디에도 체크아웃되어 있지 않으면, 형제 리포지토리와 동일한 워크스페이스 디렉토리에 클론하세요. 최종 목표 상태: 모든 리포지토리가 최신이고, check가 모든 곳에서 통과하고, npm 패키지가 게시되고, 템플릿에 태그가 지정됨 — 전체 /lt-dev:fullstack:smoke-test 실행으로 입증됨.
의존성 그래프 (순서를 결정)
Wave 1 (병렬 가능): nuxt-extensions nest-server lt-monorepo cli
│ │
▼ (npm 게시 대기!)
Wave 2 (병렬 가능): nuxt-base-starter nest-server-starter
│
▼
검증: /lt-dev:fullstack:smoke-test (모든 리포지토리를 실시간으로 실행)
│
▼
Wave 3 (문제 발견 시에만): 패치 수정 → 영향받은 리포지토리 재릴리스 (nest-server-starter: 푸시만)
규칙: 스타터는 해당 npm 패키지가 npm에서 실제로 확인 가능할 때만 업데이트되며, GitHub 릴리스가 존재한다고 해서 업데이트되지 않습니다. publish.yml 액션은 시간이 걸립니다.
버전별 엔드포인트를 사용하세요. npm view를 사용하지 마세요. npm view와 https://registry.npmjs.org/<pkg>는 동일한 CDN 캐시된 packument를 읽으며, 게시 후 몇 분 지연됩니다:
curl -s -o /dev/null -w '%{http_code}' https://registry.npmjs.org/<pkg>/<version>
# 200 = 현재 확인 가능. 대안: -H 'Cache-Control: no-cache'와 쿼리 파라미터로 캐시 무효화.
2026-09-04 측정: 세션이 10분 동안 npm view를 폴링했으며 "아직 게시되지 않음"을 읽었지만, @lenne.tech/[email protected]은 그 전체 시간 동안 이미 게시되어 있었습니다. 버전별 엔드포인트는 즉시 200을 응답했습니다. 호출자 입장에서 "아직 도달하지 않음"과 "계측기가 캐시를 읽고 있음"은 동일하게 보이므로, 이것이 엔드포인트를 사용해야 하는 이유입니다.
병렬 세션 간 웨이브 실행
위의 웨이브는 "병렬 가능"이라고 명시되어 있으며, 이는 문자 그대로 의미합니다: 각 리포지토리별로 자체 터미널에서 하나의 Claude Code 세션을 실행하는 것이 스택 릴리스를 실제로 빠르게 진행하는 방법입니다. Wave 1은 4개의 독립적인 리포지토리를, Wave 2는 2개를 더 보유하므로, 벽시계 시간 하한은 6개를 순차적으로 처리하는 것이 아닌 하나의 리포지토리 릴리스 시간입니다.
이것은 세션이 네 가지 사항을 조정할 때만 작동합니다. 모든 것은 coordinating-peer-sessions 프로토콜에서 실행되며, 리포지토리별 분할은 정확히 그 프로토콜이 요구하는 경계입니다: 두 세션이 작업 트리를 동시에 건드리지 않습니다.
1. 시작하기 전에 리포지토리를 클레임하세요. 먼저 원장에 기록한 다음 알리세요:
bash "${CLAUDE_PLUGIN_ROOT}/scripts/peer-ledger.sh" read # 누가 무엇을 소유하고 있는지 확인
bash "${CLAUDE_PLUGIN_ROOT}/scripts/peer-ledger.sh" claim "repo:nest-server" "Wave 1 release"
원장 부분은 여기가 다른 곳보다 더 중요합니다: 릴리스 라운드는 몇 시간 동안 실행되고, 세션은 왔다 갔다 하며, 메시지에만 남은 클레임은 터미널이 닫히는 순간 사라집니다. claim은 다른 살아있는 세션이 보유한 리포지토리를 거부하고 그 이름을 밝힙니다; 세션이 종료된 클레임은 [stale]로 읽히고 가져갈 수 있으므로, 충돌이 리포지토리를 좌초시키지 않습니다. 리포지토리가 릴리스되면 peer-ledger.sh release "repo:nest-server" "<version>"으로 해제하세요. 이것 없이 두 세션이 nest-server를 릴리스하면 두 번째 세션이 첫 번째 세션 위에 버전을 만듭니다.
[CLAIM] nest-server — 이 리포지토리의 Wave 1 릴리스를 진행합니다.
영향: 스택 릴리스; 다른 누구도 버전을 지정하거나 게시해서는 안 됩니다.
필요: 다른 Wave 1 리포지토리를 선택하세요.
해제: 게시된 버전과 함께 READY를 보고할 때.
2. Wave 2에 READY로 신호를 보내고, 폴링하게 하지 마세요. 웨이브 사이의 대기는 실제 npm 전파 지연이며, 대기 중인 세션은 레지스트리에 계속 물어보는 것 외에는 지연이 해소되는 순간을 알 방법이 없습니다. 게시 세션은 정확히 알고 있습니다. 버전과 함께 하나의 READY를 보냅니다:
[READY] @lenne.tech/[email protected]이 게시되었고 레지스트리에서 확인 가능합니다.
영향: nuxt-base-starter — Wave 2 의존성 범프를 시작할 수 있습니다.
필요: pnpm add @lenne.tech/[email protected]을 실행하고 계속 진행하세요.
Wave 2 세션은 또한 폴링 대신 Wave 1 세션에 notify_when_idle을 구독할 수 있습니다. 어느 쪽이든, 아무도 레지스트리에 대해 sleep 루프 안에 있지 않습니다.
3. 환경적인 문제에 대해 SOLVED를 보내세요. 이 스킬의 경험적 함정은 거의 모두 머신 전체에 해당하며 리포지토리별로 한정되지 않습니다: 원격 읽기가 조용히 아무것도 반환하지 않음, 전파되지 않은 레지스트리, 백업된 CI 러너 큐, 고장난 툴체인 버전 등. 하나를 진단한 첫 번째 세션은 이미 비용을 지불했으며, 나머지 다섯 개는 동일한 벽에 부딪힐 것입니다. 하나의 SOLVED로 그들은 그렇지 않습니다.
[SOLVED] `git ls-remote`가 stderr에 실패하고 stdout이 비어 있어 태그 검사가 "태그 없음"으로 읽힙니다.
영향: 이 릴리스 라운드의 모든 리포지토리; 태그 확인이 같은 방식으로 거짓말합니다.
필요: 종료 코드를 확인하거나 HTTPS를 통해 읽으세요 — 푸시 채널 규칙 참조.
4. LANDED로 완료를 보고하여 스모크 테스트가 한 번만 시작되도록 하세요. 검증 게이트는 모든 리포지토리가 릴리스된 후 실행됩니다. 마지막으로 끝내는 사람이 시작합니다; 다른 사람들은 그렇게 말하고 중지합니다.
메시지를 통해 전달되지 않는 것: 존재하는 리포지토리와 그 순서(이 스킬이 말함), 리포지토리가 있는 버전(레지스트리와 태그가 말함). 그리고 어떤 세션도 다른 세션에 리포지토리를 할당하지 않습니다. 사용자가 누가 무엇을 가져갈지 결정하거나, 각 세션이 빈 것을 클레임하고 말합니다.
모든 리포지토리에 적용되는 교차 규칙
-
Wire-critical 패키지는 두 프레임워크 리포지토리 모두에서 한 번에 이동합니다. 일부 의존성은 하나의 리포지토리만의 문제가 아닙니다 — 그것은 두 개의 끝이 있는 하나의 프로토콜이며, nest-server와 nuxt-extensions이 각각 하나의 끝을 소유합니다.
better-auth가 우리에게 가르친 사례입니다; 두 프레임워크가 동일하게 가져오는 모든 패키지를 같은 방식으로 처리하세요.둘 다 이를 peer로 선언하며, 두 manifest에서 바이트가 동일하고 좁은 범위를 사용합니다:
>=<알려진 최저 호환 버전> <<다음 마이너>, 절대^를 사용하지 마세요. better-auth는 MINOR 릴리스에서 깨지므로, caret은 한 릴리스 후에 동일한 분할을 초대합니다. 여기서가 아닌 manifest에서 현재 범위를 읽으세요 — 그것은 이동하며, 이 스킬에 작성된 버전은 쓸모없어집니다:node -e 'for (const p of ["nest-server","nuxt-extensions"]) console.log(p, require(`${process.env.HOME}/code/lenneTech/${p}/package.json`).peerDependencies["better-auth"])'그것을 범프한다는 것은: nest-server와 nuxt-extensions 모두에서 범위를 올리고, nest-server-starter와 nuxt-base-starter 모두에서 새 버전을 고정하고, 네 개 모두를 릴리스하는 것을 의미합니다. 절반만 하지 마세요, "프론트엔드를 차단 해제하기 위해서라도" 하지 마세요. 하한만 올리는 것도 범프입니다 — 그것은 어떤 패치가 알려진 호환인지 말하며, 두 리포지토리 간에 하한이 일치하지 않는 것은 버전 분할과 동일한 결함입니다, 한 릴리스 더 일찍.
하한은 취향의 문제가 아닙니다: 형제 피어도 만족시켜야 합니다.
better-auth와 같은 패키지는 동반자(@better-auth/passkey,@better-auth/core)와 함께 제공되며, 이들은 차례로 그 버전을 피어 요구합니다. 동반자가 요구하는 것보다 낮은 하한은 실제로 설치할 수 없는 짝을 허용합니다 —>=1.7.0은 better-auth 1.7.0을 passkey 1.7.1 옆에 허용했으며, passkey 자체는 이를 거부합니다(^1.7.1). 범위가 일반적으로 틀리지는 않았습니다; 정확히 하나의 유효하지 않은 교차 조합을 허용했으며, 이는 어느 리포지토리에서도 설치가 절대 발생하지 않는 종류입니다. 직관이 아닌 동반자의 manifest에서 하한을 도출하세요:node -e 'console.log(require("./node_modules/@better-auth/passkey/package.json").peerDependencies)'nuxt-extensions은
test/peer-dependency-ranges.test.ts에서 이를 기계적으로 확인합니다 (동반자의 요구사항에 대한 하한, 그리고 실제로 테스트된 devDependency에 대한 모든 범위). 피어 범위에 동반자가 있는 곳마다 복사할 가치가 있습니다.분할의 비용, 측정됨: nest-server가 better-auth 1.6.26을 하드 의존성으로 고정한 반면 nuxt-extensions은 peer로 선언하여, 앱이 1.7.1로 이동하고 api가 따라갈 수 없었습니다. 1.7은
twoFactor.enable에method를 전달하는 식별된 결과를 제공하며, 1.6.26은 절대 보내지 않습니다 — 모든 풀스택 프로젝트의 모든 2FA 활성화가 실패했으며, 일반적인 클라이언트 오류와 서버 로그에 특이한 것이 없었습니다. 두 리포지토리의check는 전체 시간 동안 통과했습니다: 각각은 내부적으로 일관성이 있었고, 조립된 워크스페이스만 두 반쪽을 모두 가졌습니다.오늘날 그것을 잡는 세 가지 가드가 있으며, 그 중 어느 것도 이 규칙을 대체하지 않습니다 — 그들은 실수를 잡고, 규칙은 그것을 방지합니다:
-
manifest가 병합될 때 (lt CLI
hoist-workspace-pnpm-config.ts, 1.45.0 이후). 호이스트는 마지막 작성자가 승리하며, 이는 루트-대-하위에는 옳고 형제 간에는 함정입니다. 보고됨: 두 하위 프로젝트가 동일한 실행에서 하나의 키를 다르게 설정; 증분 사례(add-api다음add-app, 이전 실행이 이미 호이스트되고 소스를 비운 경우); 그리고 리포지토리가package.json#pnpm과 자체pnpm-workspace.yaml간에 스스로 모순되는 경우.allowBuilds는 단순히 보고될 뿐만 아니라 거부-승리로 병합됩니다 — 경고는 한 프로젝트가 명시적으로 거부한 설치 스크립트를 중지하지 않으며,overrides는 버전에 대한 안전한 방향이 없기 때문에 보고 전용으로 남습니다. -
조립된 워크스페이스에서 (
lt-monorepo/scripts/check-workspace-consistency.mjs, 3.10.0 이후). api와 app이 wire-critical 패키지를 다르게 해석하거나, 두 프레임워크가 다른 피어 범위를 약속하거나, 멤버가 패키지를 설치했지만 어디에도 고정하지 않은 경우 실패합니다 —autoInstallPeers는 기본적으로 true이므로, 고정되지 않은 피어는 오늘 동의하고 다음 설치 시 자유롭게 표류합니다. -
동작에서 (
nuxt-base-starter/.github/workflows/test.yml, 작업e2e-auth, 2.22.3 이후 통과). MongoDB와 nest-server-starter를 부팅하고 실제 API에 대해 인증 스위트를 실행합니다. 버전 차이가 아닌 깨진 계약을 보는 유일한 레이어 — 다른 하나는 선언을 비교하며, 선언은 분할 전체에서 통과했습니다.놓치기 쉬운 두 가지가 있으며, 모두 자체 첫 번째 실행에서 발견되었습니다: 템플릿의 의존성은 별도로 설치되어야 합니다 (자체 잠금 파일이 있으며, 체크 체인은
cross-env로 시작합니다 — 자체 devDependencies 중 하나), 그리고 빈 데이터베이스는 모든 방문자를/auth/setup으로 라우팅하므로, 스위트가 실행되기 전에POST /system-setup/init을 통해 첫 번째 관리자를 생성해야 합니다.
스모크 테스트는 종단 간 게이트입니다: 실제 풀스택 프로젝트를 빌드하므로, 와이어 분할이 버전 차이가 아닌 실패하는 흐름으로 나타나는 유일한 장소입니다.
-
-
해당 리포지토리의 릴리스 차이에 대해
/lt-dev:review가 실행되기 전에는 아무것도 게시되지 않습니다 (/lt-dev:review --base=<마지막 릴리스 태그>, 커밋되지 않은 파일과 추적되지 않은 파일 포함), 유지보수 후 버전 범프 전에. 태그는origin/main에서 가져오세요:git describe --tags --abbrev=0 origin/main. 릴리스 태그는 모든 기본 리포지토리의main에 있으며, nest-server의 태그는develop이 절대 포함하지 않는 병합 커밋이므로,HEAD에서 조회하면 여러 릴리스 이전의 태그를 반환합니다 (2026-10-27 nest-serverdevelop에서 측정:12.0.0대신11.33.1, 14개 파일 대신 851개 파일 검토). 체인의 모든 리포지토리는 자체 차이를 전달합니다; 검토가 수정할 수 없는 발견은 해당 리포지토리의 릴리스와 그 아래 모든 것을 중지합니다. Kai가 2026-10-21에 결정, 자동화된 릴리스 체인이 아무도 검토하지 않은 코드를 게시하지 않도록. -
변경 없음 → 릴리스 없음. 릴리스를 건너뛰는 유일한 경우는 리포지토리에서 정말로 아무것도 변경되지 않은 경우입니다 (작업 트리 깨끗함 AND 마지막 릴리스 버전 이후 커밋 없음) — 변경 사항을 전혀 포함하지 않는 버전을 절대 만들지 마세요. 실제 리포지토리 변경 — 의존성 범프, 코드, 스크립트, 잠금 파일, 도구 고정 — 어떤 것이든 새 버전을 정당화합니다; 변경이 "릴리스 가치가 있는지" 추측하지 마세요. 변경되지 않은 리포지토리는 "이미 최신 — 릴리스 없음"으로 보고되고 건너뜁니다. 한 가지 예외는 nest-server-starter입니다: 그 버전은 nest-server 버전과 함께만 이동하므로, 자체 변경 사항은 릴리스 없이 커밋되고 푸시되며 다음 릴리스와 함께 제공됩니다 (recipes.md, nest-server-starter). 참조는 게시된 아티팩트입니다: npm 패키지의 경우, tarball에 도달할 수 없는 커밋 (package.json
files세트 외부 — 예:.claude/agent-memory/**, CI 구성)은 자체 릴리스를 트리거하지 않습니다; 확실하지 않을 때는npm pack --dry-run으로 확인하세요. 이러한 커밋은 다음 실제 릴리스와 함께 그냥 탑니다. 템플릿의 경우 아티팩트는 리포지토리 자체이므로 모든 커밋이 중요합니다. -
푸시 채널: 기능적으로 결정하는 스크립트에 물어보세요:
bash "${CLAUDE_PLUGIN_ROOT}/scripts/check-push-channel.sh" [<repo-root>] # 리포지토리 내부에서는 생략; 절대 "$(pwd)"를 전달하지 마세요 # -> ssh<TAB>github.com<TAB>authenticated as: Hi kaihaase! … # -> https<TAB>github.com<TAB>no SSH authentication (…)https에서, HTTPS를 통해 푸시:git -c credential.helper='!gh auth git-credential' push https://github.com/lenneTech/<repo>.git <branch>.gh release create는 어느 쪽이든 영향을 받지 않습니다.권한 문제처럼 보이는 것으로 푸시가 실패할 때 확인을 실행하세요. 2026-09-08 nest-server-starter에서 측정:
git push origin main이올바른 액세스 권한이 있는지와 리포지토리가 존재하는지 확인하세요로 실패했지만, SSH를 통한 nest-server 푸시는 같은 시간에 작동했습니다. 계정, 협업자 상태 또는 원격 URL에 문제가 없었습니다 — SSH에서 github.com으로의 연결이 해당 경로에 대해서만 죽었으며, 확인은https<TAB>github.com<TAB>응답 없음이라고 말했습니다. 여전히 그렇다면...[SOLVED] SSH가 github.com:lenneTech/nest-server-starter에 대해 죽었고 HTTPS가 작동합니다. 영향: nest-server-starter만 해당; nest-server는 SSH를 통해 푸시됩니다. 필요: 없음; `check-push-channel.sh`는 같은 세션에서 읽은 내용을 이미 인쇄합니다.스크립트는 두 프로토콜을 모두 시도하고 작동하는 것을 반환합니다. 어느 것도 작동하지 않으면 실패합니다.
-
잠금 파일은 리포지토리 바이너리 호환성을 정의합니다. 종속성 버전을 범프한 후
pnpm install --fix-lockfile을 실행하세요. 매니페스트 호환성과 설치 가능성 사이의 차이는 lockfile에서 찾을 수 있습니다:# lockfile이 선언된 버전과 일치하는지 확인 pnpm ls --depth=0 --recursive 2>/dev/null | grep -E '^[a-z]' | head -20 # 또는 lockfile이 기존 설치와 일치하는지 확인 diff <(pnpm deploy --filter=. /tmp/ref-lock 2>/dev/null && cat /tmp/ref-lock/pnpm-lock.yaml) pnpm-lock.yaml
## 일반 릴리스 레시피 (리포지토리당)
리포지토리가 확인 가능하고 최신 상태이며 `check`가 통과했다고 가정합니다.
```bash
# 1. 작업 트리가 깨끗한지 확인
git status --short
# 2. main 병합/리베이스 (리포지토리에 따라 다름)
# 개발 브랜치가 있는 리포지토리(nest-server, nuxt-extensions):
git checkout develop && git pull origin develop
git checkout main && git pull origin main
git merge develop --no-edit # 또는: git rebase develop
# main만 있는 리포지토리(nuxt-base-starter, nest-server-starter, lt-monorepo, cli):
git checkout main && git pull origin main
# 3. 변경 사항 검토
git log --oneline <마지막 릴리스 태그>..HEAD
# 4. /lt-dev:review 실행
# (이것은 패키지.json 버전 파일 제외, 변경된 파일 목록을 제공합니다)
# 5. 버전 범프 (npm 버전 또는 수동)
# 시맨틱 버전 규칙 따르기
# 6. pnpm install --fix-lockfile 실행
# 7. check 실행
npm run check
# 8. 커밋 및 푸시
git add -A && git commit -m "chore: release v<version>"
git push origin main
# 9. npm에 게시
npm publish
# 10. GitHub 릴리스 생성
github release create v<version> --generate-notes
# 11. 해제 및 신호
peer-ledger.sh release "repo:<name>" "<version>"
# 그리고 Wave 2 세션에 READY 보내기
스모크 테스트 (릴리스 게이트)
모든 리포지토리가 릴리스된 후:
/lt-dev:fullstack:smoke-test
이것은 실제 풀스택 프로젝트를 빌드하고 다음을 실행합니다:
- MongoDB로 nest-server-starter 부팅
- nuxt-base-starter로 앱 부팅
- 인증 스위트 실행 (E2E)
- 모든 엔드포인트가 응답하는지 확인
스모크 테스트가 실패하면:
- 실패 원인 파악
- 영향을 받는 리포지토리 수정
- 패치 버전을 릴리스하고 스모크 테스트 재실행
문제 해결
일반적인 문제
git push가 권한 오류로 실패합니다
- 푸시 채널 확인 실행 (
check-push-channel.sh) - HTTPS로 전환하고
gh자격 증명 도우미 사용
npm 게시가 "패키지가 존재합니다"라고 말합니다
- 이미 게시된 버전입니다. 버전을 범프하고 다시 시도하세요.
npm view가 새 버전을 표시하지 않습니다
- 버전별 엔드포인트 사용:
curl https://registry.npmjs.org/<pkg>/<version> - 게시가 전파되기를 기다리세요 (보통 1-5분)
peer-ledger가 클레임을 거부합니다
- 다른 세션이 리포지토리를 소유하고 있습니다.
read로 확인하세요. - 클레임이
[stale]이면 해제하고 다시 클레임하세요.
스모크 테스트가 데이터베이스 오류로 실패합니다
- MongoDB가 실행 중인지 확인하세요
- 빈 데이터베이스: 먼저 POST /system-setup/init으로 첫 번째 관리자 생성