Skip to content

fix(plugin): migrate stale global codingbuddy-mode-detect.py on install#1389

Merged
JeremyDev87 merged 1 commit into
masterfrom
taskmaestro/1775394949/pane-1
Apr 5, 2026
Merged

fix(plugin): migrate stale global codingbuddy-mode-detect.py on install#1389
JeremyDev87 merged 1 commit into
masterfrom
taskmaestro/1775394949/pane-1

Conversation

@JeremyDev87

Copy link
Copy Markdown
Owner

Summary

Bundle fix for #1381 (primary) and #1384 (related hardening). Upgraded installs from plugin versions prior to the self-contained hook refactor kept a stale global ~/.claude/hooks/codingbuddy-mode-detect.py alive via ~/.claude/settings.json, causing every PLAN:/ACT: prompt to emit the old mandatory-MCP template even though the newer self-contained hook was shipped. This PR migrates those installs and makes the standalone fallback visibly diagnosable.

  • New packages/claude-code-plugin/scripts/migrate-legacy-hooks.js (CommonJS, no deps):
    • Deletes ~/.claude/hooks/codingbuddy-mode-detect.py when present.
    • Removes ~/.claude/hooks/lib only when it is a symlink (real directories are never touched).
    • Filters only UserPromptSubmit entries whose command references the legacy script file name, preserving every unrelated entry, and drops the UserPromptSubmit key entirely when it becomes empty.
    • Writes a timestamped settings.json.bak-codingbuddy-migration-<ts> backup before rewriting settings.json.
    • Idempotent, graceful on malformed/missing settings.json, graceful on missing ~/.claude.
    • Supports opts.dryRun and CODINGBUDDY_POSTINSTALL_DRY_RUN=1.
    • Emits clear [CodingBuddy Plugin] Migrated ... log lines so users can see exactly what changed.
  • Wires the migration into scripts/postinstall.js so every install runs it (including CI, since it is a no-op on clean systems). Migration failures never block npm install — they are surfaced as warnings.
  • Adds a # Backend: ... diagnostic marker to hooks/user-prompt-submit.py (hardening(plugin): eliminate stale mandatory-MCP hook behavior and make standalone fallback more visible #1384) so users can tell at a glance whether mode handling is backed by the MCP server or the self-contained engine. Covers the exception fallback branch too.

Test plan

  • 21 new vitest cases in scripts/migrate-legacy-hooks.spec.ts, covering:
    • Stale script removal + unrelated files preserved + no-op when absent.
    • Symlink lib removal vs. real-directory safety (target dir never deleted).
    • Legacy entry filtering in top-level and nested UserPromptSubmit arrays.
    • UserPromptSubmit key dropped when empty; sibling keys preserved.
    • Backup created on change; no backup when nothing changes.
    • Dry-run via both opts.dryRun and CODINGBUDDY_POSTINSTALL_DRY_RUN=1 env var.
    • Idempotency: second run is a clean no-op, no extra backup.
    • Missing ~/.claude, missing settings.json, malformed JSON all degrade gracefully.
    • Log visibility (#1384): stale-script and settings-update log lines; quiet on clean systems.
    • LEGACY_SCRIPT_NAME constant exposed for callers.
  • 2 new pytest cases in hooks/test_user_prompt_submit.py::TestBackendDiagnosticMarker asserting the new # Backend: standalone / # Backend: mcp-enhanced markers.
  • Pre-push checks (codingbuddy-claude-plugin):
    • yarn workspace codingbuddy-claude-plugin lint
    • yarn workspace codingbuddy-claude-plugin format:check
    • yarn workspace codingbuddy-claude-plugin typecheck
    • yarn workspace codingbuddy-claude-plugin test:coverage144/144 passed
    • yarn workspace codingbuddy-claude-plugin circular → no circular deps
    • yarn workspace codingbuddy-claude-plugin build
  • python3 -m pytest packages/claude-code-plugin/hooks/test_user_prompt_submit.py39/39 passed
  • Cross-cutting npm audit --severity high for codingbuddy, codingbuddy-claude-plugin, landing-page — all clean.

Closes #1381
Closes #1384

Cleans up the legacy mandatory-MCP hook left behind by plugin versions
prior to the self-contained refactor so upgraded installs no longer keep
the old script alive via global settings.json.

- Add scripts/migrate-legacy-hooks.js: deletes
  ~/.claude/hooks/codingbuddy-mode-detect.py, removes the hooks/lib
  symlink when it is a symlink (never a real directory), filters only
  UserPromptSubmit entries whose command references the legacy script
  (preserving unrelated entries), drops the UserPromptSubmit key when
  it becomes empty, and writes a timestamped
  settings.json.bak-codingbuddy-migration-<ts> backup before rewriting.
- Idempotent, graceful on malformed/missing files, supports
  CODINGBUDDY_POSTINSTALL_DRY_RUN=1 and opts.dryRun.
- Wire the migration into scripts/postinstall.js so every install runs
  it (including CI); failures never block the install.
- 21 new vitest cases in scripts/migrate-legacy-hooks.spec.ts covering
  stale script removal, symlink vs real-dir safety, nested
  UserPromptSubmit filtering, backup behavior, dry-run (both opts and
  env var), idempotency, malformed JSON, missing settings.json, and
  log visibility.
- Add a Backend diagnostic marker to hooks/user-prompt-submit.py so
  standalone-vs-mcp fallback is obvious to users (#1384 visibility),
  plus two pytest regression tests.

Closes #1381
Closes #1384
@JeremyDev87 JeremyDev87 added bug Something isn't working sub-issue 상위 이슈의 하위 작업 plugin packages/claude-code-plugin priority:must Must Have - 반드시 필요, 없으면 릴리즈 불가 P1 Priority 1: First Impression labels Apr 5, 2026
@vercel

vercel Bot commented Apr 5, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
codingbuddy-landing Ready Ready Preview, Comment Apr 5, 2026 1:47pm

@JeremyDev87 JeremyDev87 left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review: APPROVE

CI Status: PASS

29/29 check 전체 통과 — build, lint, typecheck, test, security, circular, e2e (hooks 3.11/3.12, docker), vercel preview, plugin-validate-commands, rules-validation 포함.

Local Verification: PASS

yarn workspace codingbuddy-claude-plugin lint         ✅
yarn workspace codingbuddy-claude-plugin typecheck    ✅
yarn workspace codingbuddy-claude-plugin test         ✅ 144/144 (21 new migrate cases)
python3 -m pytest hooks/test_user_prompt_submit.py    ✅ 39/39 (2 new backend marker cases)
src/utils.ts coverage                                  100% stmt/line

Real-environment smoke test (bonus)

리뷰어 로컬 머신에는 실제로 #1381이 타겟팅하는 stale 환경이 존재:

  • ~/.claude/hooks/codingbuddy-mode-detect.py (2026-03-30)
  • ~/.claude/hooks/lib (monorepo로의 symlink)
  • ~/.claude/settings.json에 legacy UserPromptSubmit 엔트리 1개

migrateLegacyHooks({ dryRun: true }) 실행 결과:

{
  "removedScript": true,
  "removedSymlink": true,
  "strippedSettingsEntries": 1,
  "removedUserPromptSubmitKey": true,
  "backupPath": null,
  "dryRun": true,
  "warnings": []
}

세 아티팩트 모두 정확히 탐지, dry-run 플래그가 원본을 전혀 건드리지 않음. 파일/심링크/설정 모두 사전 상태 그대로 유지 확인.

Severity Findings

  • Critical (blocking): 없음
  • High (must fix): 없음
  • Medium (should fix): 없음
  • Low (nice to have): 없음

Spec Compliance

#1381 (Stale hook migration) — 제안된 3단계 + 4가지 safety consideration 모두 구현:

요구사항 구현 위치
Stale 글로벌 스크립트 삭제 migrateStaleScript()
lib/ 삭제 심링크일 때만 migrateStaleSymlink()isSymbolicLink() 가드 + real-dir 보호 테스트
settings.json의 legacy UserPromptSubmit 필터 stripLegacyFromSettings() + filterGroup()
빈 UserPromptSubmit 키 제거 removedUserPromptSubmitKey 로직
타임스탬프 백업 settings.json.bak-codingbuddy-migration-<ts>
legacy 파일명만 매칭 isLegacyCommandString('codingbuddy-mode-detect.py')
멱등성 "running twice" 테스트로 검증
Dry-run (env + opt) 두 경로 모두 테스트
명확한 로그 라인 [CodingBuddy Plugin] prefix + 액션별 메시지

#1384 (Standalone diagnosability + mandatory-MCP 제거) — 3가지 acceptance criteria 모두 충족:

요구사항 구현
업그레이드 설치에서 stale mandatory-MCP 제거 postinstall.js에서 migrateLegacyHooks 자동 호출
Standalone fallback 진단 용이성 # Backend: mcp-enhanced / # Backend: standalone (self-contained, no MCP required) / # Backend: standalone-minimal (import failure) 세 개의 구별 마커
마이그레이션 + 가시성 테스트 21 vitest + 2 pytest

리뷰어 노트 (정보 공유 — 블로커 아님)

안전성 설계 하이라이트:

  1. Backup-first 순서: migrateSettings()는 백업을 먼저 쓰고 성공을 확인한 뒤에만 원본을 덮어씀. 백업 write가 실패하면 rewrite 자체를 abort (return; // do not rewrite settings.json if we could not back it up). 이슈 스펙을 넘어서는 안전장치로, 디스크가 꽉 차거나 권한 이슈가 있을 때 settings.json이 손상되는 최악의 시나리오를 방어함.

  2. Symlink API 정확성: lstatSync().isSymbolicLink() 사용 (statSync가 아닌). stat은 심링크를 따라가므로 심링크 구별에 쓰면 안 되는데, 여기서는 lstat로 심링크 자체를 검사. 실수하기 쉬운 포인트를 정확히 잡음.

  3. Graceful degradation 3중 방어:

    • ~/.claude 없음 → early return, 에러 없음
    • settings.json 없음 → ENOENT 무시, null 반환
    • malformed JSON → 원본 유지, warning 추가, migration skip
      세 가지 모두 테스트로 커버됨.
  4. Nested filtering: filterGroup이 그룹 전체가 아니라 그룹 내부의 개별 hook entry까지 필터링. 한 UserPromptSubmit 그룹에 legacy와 사용자 hook이 섞여 있어도 사용자 것만 살림. 테스트 filters nested hooks arrays where only some sub-entries are legacy로 검증.

  5. Import-failure 브랜치에도 standalone-minimal 마커: user-prompt-submit.py의 Exception fallback 경로에도 # Backend: standalone-minimal (import failure) 출력. lib/ 심링크 깨짐 같은 실제 breakage 시나리오를 사후 진단 가능하게 함 — #1384의 가시성 목표를 스펙 이상으로 만족.

  6. Dry-run semantics: 플래그를 result에 먼저 기록한 뒤 write를 gate. dry-run으로도 "무엇이 바뀔지"를 결과 객체로 확인 가능 (removedScript: true, backupPath: null). 테스트 does not write any files when dryRun is true + "Result reports what would change even in dry-run"로 명시적 검증.

  7. postinstall 통합: migration 실패는 npm install을 블록하지 않고 warning만 출력 — deployment-friendly. CI 환경(CI=true)에서도 migration은 실행하되 banner만 스킵. 멱등성 덕분에 CI에서 매번 실행해도 안전.

좋은 점 — 테스트 품질:

  • Mocking 없음. 실제 tmp 디렉토리, 실제 심링크, 실제 JSON 파일로 픽스처 구성. 이 덕분에 리뷰어 로컬 머신의 실제 stale 환경에서 smoke test가 가능했음.
  • afterEach 기반 리소스 정리: 누락된 cleanup으로 인한 테스트 간 간섭 없음.
  • 환경변수 테스트 위생: CODINGBUDDY_POSTINSTALL_DRY_RUN 테스트가 prev 값 보존/복원을 finally에서 수행.
  • 양성/음성 양방향: MCP 브랜치 테스트가 "mcp-enhanced 포함" 뿐 아니라 "standalone wording이 leak되지 않음"까지 assert — 회귀 방지 방향까지 커버.

사소한 관찰 (수정 불필요):

  • 백업 파일명이 Date.now() ms 정밀도 — 이론적으로 동시 실행 시 충돌 가능하지만, postinstall 단일 진입점이라 실질적으로 불가능.
  • 마이그레이션 스크립트 JSDoc에 반환 타입이 명시되지 않음 — IDE hint 개선 여지 있으나 테스트 타입 정의에서 MigrateResult로 이미 잡혀 있어 실무 지장 없음.

어느 것도 블로커/리그레션이 아님.

Recommendation: APPROVE

스펙 초과 달성 (#1381 안전장치 + #1384 가시성 3단계 마커), CI 29/29 전체 통과, 로컬 lint/typecheck/test/pytest 전체 통과, 실환경 dry-run smoke test 통과, 코드 품질 이슈 없음, 안전성 설계 모범적. 머지 가능.

@JeremyDev87 JeremyDev87 self-assigned this Apr 5, 2026
@JeremyDev87 JeremyDev87 merged commit aab1fd4 into master Apr 5, 2026
29 checks passed
@JeremyDev87 JeremyDev87 deleted the taskmaestro/1775394949/pane-1 branch April 5, 2026 13:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working P1 Priority 1: First Impression plugin packages/claude-code-plugin priority:must Must Have - 반드시 필요, 없으면 릴리즈 불가 sub-issue 상위 이슈의 하위 작업

Projects

None yet

1 participant