Skip to content

fix(build): Windows 설치파일 빌드 실패 수리 — PowerShell 인코딩 + 파서 충돌 - #45

Merged
BoxLogoDev merged 54 commits into
mainfrom
fix/win-build-encoding
Aug 19, 2026
Merged

fix(build): Windows 설치파일 빌드 실패 수리 — PowerShell 인코딩 + 파서 충돌#45
BoxLogoDev merged 54 commits into
mainfrom
fix/win-build-encoding

Conversation

@BoxLogoDev

Copy link
Copy Markdown
Owner

v2.4.1 릴리스의 Windows 설치파일 빌드가 PowerShell 파서 오류로 실패했습니다.

원인 2가지

  1. 인코딩build-win.ps1 이 UTF-8 BOM 없이 한글 주석을 담고 있습니다. PowerShell 5.1 은 BOM 이 없으면 시스템 ANSI 코드페이지로 읽으므로, 영문 로케일 CI 러너에서 한글 바이트가 깨져 따옴표·괄호 구조가 무너집니다. 한국어 로케일 개발 머신에서는 우연히 파싱돼 로컬 재현이 안 됐습니다.

  2. node -p 인라인 정규식node -p "…'.replace(/\/g, '/')…" 가 PowerShell 파서와 충돌합니다. 외부 프로세스도 불필요해 Get-Content + ConvertFrom-Json 으로 대체했습니다.

검증

  • [Parser]::ParseFile 구문 검사 → PARSE OK
  • git 인덱스에 BOM 보존 확인 (* text=auto 가 제거하지 않음)

머지 후 v2.4.1 태그를 재생성해 릴리스를 다시 돌립니다.

🤖 Generated with Claude Code

BoxLogoDev and others added 30 commits August 16, 2026 13:00
eval 바닥 케이스(SAC 0.00 / CPI 0.00 / WM 0.00 / BTP 0.06)가 전부 전용 지식
부족이었다. SKILL.md 4개를 97~143줄에서 541~650줄로 확장하고, 에이전트에
응답 형식과 진단 순서를 명시했다.

- SAC: S/4 Live Connection 실패는 SICF(InA/OData 노드)·SAML2 trust 우선 확인
- IC: CPI Message Processing Log 에서 mapping step·payload schema 확인.
      S/4 교차 확인은 SXMB_MONI·SRT_MONI, payload 원문 외부 전송 금지
- EWM: WM TO confirm 실패는 LS24 원본 빈 재고 확인 후 LT06 차이수량
- integration-advisor: BTP Destination 실패 시 SM59 로 backend 연결 교차 확인
- IBP: MD63 릴리스 PIR 과 MD04 MRP 반영을 순서대로 확인

SKILL.md 보강은 Codex(gpt-5.6-sol)가 작성했고 오케스트레이터가 게이트로 재검증했다.

검증:
  lint-frontmatter            0 오류 (66 파일)
  check-hardcoding --strict   0 오류 (457 파일)
  check-ecc-s4-split --strict 0 누락 (24 SKILL)
  check-tcodes --strict       404개 확정, 미등록 0건

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
resolved 세션을 훑어 메트릭과 환류 후보를 낸다. symptom-index 에 매칭됐지만
gold-set 에 없으면 eval 확장 후보(gold_set), 매칭 자체가 없으면 신규 증상
후보(codify)로 분류한다. scripts/learning/aggregate.mjs 의 판정 로직을 런타임
API 로 승격한 것이라 데스크톱과 MCP 가 같은 결과를 소비할 수 있다.

프라이버시를 타입 레벨로 고정했다. 자유 텍스트와 환경 정보를 내보내지 않고
auto_apply 는 false 리터럴이며, candidate_id 는 세션 ID 가 드러나지 않도록
sha256 앞 16자만 쓴다. 최종 반영은 항상 사람 검수를 거친다(ETHOS ①).

세션 시작 입력에 matched_symptom_index_entry 를 추가했다. 이 값이 있어야
gold_set 후보와 codify 후보를 가를 수 있다.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
expanded-tools.test.ts 가 @jest/globals 를 import 하는데 이 저장소는 node:test
를 쓴다. 그래서 한 번도 실행되지 않았고, 그 사이 두 곳이 깨져 있었다.

- SAPSTACK_ROOT 가 process.cwd()/sapstack 을 가리켜 mcp 에서 실행하면 데이터
  파일을 찾지 못했다. contract.test.ts 와 같은 패턴으로 저장소 루트를 잡는다.
- "Turn 4 verdict template" 은 로컬 문자열을 만들어 그 문자열을 자기가 검사하는
  자기충족 테스트였고, 검사어가 문자열에 없어 통과 자체가 불가능했다. 실제
  PROMPT_REGISTRY 등록 여부를 검증하도록 바꿨다.

expect 58개를 전부 assert 로 바꾸는 대신, 이 파일이 쓰는 matcher 6종만 매핑하는
shim 을 뒀다.

package.json 의 test 가 contract.test.ts 하나만 지정해 write-path.test.ts 까지
orphan 이었다. 셋 다 연결했다.

현재 24/25 통과. 남은 1건은 data/industry-matrix.yaml 의 retail 에서 BTP·GTS 가
agent 필드를 갖고 있지 않아 실패하는 것으로, 테스트가 옳고 데이터가 빠진 경우다.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
CLAUDE.md 의 Quick Advisory 규칙에 근거 기반 1차 원인 우선 제시, 반증 조건,
Check 섹션 최소 요건(관련 T-code 2개 + Table.Field 1개)을 추가했다. 환경 정보가
없어도 잠정 진단과 읽기 전용 체크를 같은 턴에 제공하도록 했다. 운영자가 환경
질문만 받고 아무 단서 없이 되돌아가는 상황을 막는다.

eval 러너를 실제 운영 동작에 맞췄다.
- system 프롬프트에 CLAUDE.md(Universal Rules)를 포함한다. 기존에는 에이전트
  본문만 줘서 운영과 다른 조건으로 측정하고 있었다.
- gold-set 의 env 를 user 프롬프트에 주입하고 환경 질문에서 멈추지 않게 지시한다.
- BTP 라우팅을 sap-cloud-consultant 에서 sap-integration-advisor 로 바꿨다.
  Destination·backend 진단 T-code 를 후자가 보유한다.
- --all 없이 타깃 실행하면 REPORT 를 갱신하지 않는다(공식 baseline 오염 방지).
- 채점 오류가 있으면 exit 1 로 종료한다.
- Windows 에서 claude CLI 탐지를 where.exe 기반으로 바꿨다.

learning 스크립트는 모듈 집계 시 impacted_modules 를 우선 쓰고 없을 때만
likely_modules 로 폴백하도록 스키마와 맞췄다.

동일 15개 케이스 기준 평균 0.615 에서 0.788 로 올랐다. 다만 16번째부터 claude
CLI 가 연속 실패해 전체 재측정이 필요하다(REPORT 의 경고 블록 참조).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Golden Path 에서 runtime.learning.inspect() 결과를 볼 수 있게 했다. 세션 시작
시 confidence 0.6 이상 매칭을 matched_symptom_index_entry 로 넘겨 gold_set /
codify 후보 판정이 성립하도록 했다. 세션에서 지식 환류까지 경로가 이어진다.

SAP 커넥터는 fail-closed 로 동작한다. 이름이 SAP 계열로 보이는 소스
(sap/abap/adt/s4hana)는 정책을 읽지 못하거나 sapConnector 설정이 없으면 차단한다.
정책이 있어도 access 가 read_only 가 아니거나 environment 가 개발·품질·샌드박스가
아니면(= 운영 시스템이면) 거부하고, allowedTools 화이트리스트 밖의 툴도 막는다.
권한 모드 검사보다 먼저 평가되므로 Claude·Pi 두 백엔드에 모두 적용된다.

call_llm 은 첨부파일을 거부한다. 외부 전송 승인 정책이 서기 전까지 SAP 증거가
보조 LLM 경로로 새어 나가는 것을 막는다.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
electron-builder 설정과 자동 업데이트 코드는 있었지만 어떤 워크플로도
electron:dist 를 호출하지 않아 설치파일이 만들어진 적이 없었다.

- release.yml 에 desktop-windows job(windows-latest) 추가. build-win.ps1 을
  경유한다. electron-builder 를 직접 부르면 claude-agent-sdk-binary alias 가
  없어 extraResources 복사가 깨진다(electron-builder.yml 118행 주석).
- 코드 서명은 WINDOWS_CSC_LINK / WINDOWS_CSC_KEY_PASSWORD secret 이 있을 때만
  적용된다. 없으면 서명 없이 빌드되고 SmartScreen 경고가 뜬다.
- 산출물을 artifact 로 올려 GitHub Release 에 첨부한다. release job 은
  needs 로 데스크톱 빌드에 묶인다.

버전 단일출처를 5개에서 8개 파일로 넓혔다. packages/runtime 과 데스크톱 2개를
포함하고, 데스크톱은 홀로 3.0.0-beta.0 이던 것을 제품 버전 2.4.0 으로 통합했다.
electron-builder 의 artifactName 이 이 값을 쓰므로 통합하지 않으면 설치파일
이름이 태그와 어긋난다. bun.lock 에도 워크스페이스 버전이 기록되므로 함께
갱신했고 --frozen-lockfile 통과를 확인했다.

ci.yml 에는 check-gate.test.mjs 를 붙였다. 릴리스를 막는 게이트 로직인데
정작 어느 워크플로에서도 실행되지 않고 있었다.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
- orca-sap-stack-spicy-snail.md: 제품 패키지화와 온디바이스 AI 내장 계획.
  Joule 이 RISE/GROW 전용이라 ECC·온프레 고객에게 접근 경로가 없다는 시장 공백,
  ECC 유지보수 종료 일정, 16GB CPU-only 제약에서의 모델 선택 근거를 담았다.
  온디바이스는 pi_compat + OpenAI 호환 엔드포인트로 이미 대부분 구현돼 있어
  남은 일이 번들링과 품질 증명임을 조사로 확인했다.
- desktop-readiness.md: 온전한 데스크톱 앱이 되기 위해 필요한 것.
  확인한 사실과 미확인 항목을 구분해 적었다.
- handoff/: 파일 소유권을 겹치지 않게 자른 4-AI 작업 지시서.
  Claude(배포·아키텍처) / Codex(agents·plugins) / Grok(data·docs) /
  Antigravity(extension·web). 각 지시서에 측정 가능한 성공 기준과 자율 반복
  루프, EVIDENCE 보고 형식을 넣었다.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
publish provider 가 generic + GitHub Pages 였는데, Pages 는 파일당 100MB 제한이
있어 이 설치파일을 담을 수 없다. claude 네이티브 바이너리(~210MB)와 Bun 런타임이
들어가기 때문이다. 즉 코드상 자동 업데이트는 구현돼 있었지만 실제로 받을 수 있는
파일이 그 경로에 존재할 수 없었다.

Releases 는 파일당 2GB 를 허용하고 릴리스 워크플로가 이미 산출물을 올린다.
electron-updater 가 새 버전을 판별하는 latest.yml 도 artifact 와 Release 에
함께 포함시켰다. 이게 없으면 업데이트 체크가 동작하지 않는다.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
SAP 운영망은 대개 인터넷이 차단돼 있고, 외부로 나가는 요청이 존재한다는 사실
자체가 보안 심사 탈락 사유가 된다. 그런데 앱은 시작하자마자 Sentry 를 init 하고
업데이트를 폴링하고 있었다.

SAPSTACK_AIRGAPPED 환경변수나 ~/.sapstack/config.yaml 의 air_gapped: true 로
켜면 둘 다 시작하지 않는다. Sentry 초기화보다 먼저 평가돼야 해서 동기로 읽고,
같은 이유로 YAML 파서 대신 해당 키 한 줄만 정규식으로 확인한다.

판정 실패(파일 없음·읽기 불가)는 꺼짐으로 둔다. 기본을 켜짐으로 하면 일반
사용자가 업데이트를 영원히 받지 못한다. 폐쇄망 고객은 명시적으로 설정한다.

Sentry DSN 은 빌드 시 주입되므로 미주입 빌드에서는 이미 비활성이지만, DSN 이
들어간 빌드를 그대로 폐쇄망에 반입하는 경우를 막는다.

.prettierignore 를 추가해 apps/desktop 을 포맷터 대상에서 제외했다. upstream
파생 코드라 자체 스타일(세미콜론 없음·작은따옴표)을 쓰는데, 루트 prettier
기본값으로 재포맷되면서 주석 3줄 수정이 1,690줄 diff 로 부풀었다.

검증: bun run typecheck:electron 통과

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
측정 커버리지를 넓히고 저장소에 남아 있던 사실 오류를 걷어냈다.

- gold-set 32 → 58건. symptom-index 90건 대비 커버리지를 올렸다. 정답은
  전부 해당 symptom 의 typical_causes 첫 항목에서 파생했고 새로 지어내지 않았다.
- industry-matrix 3 → 7개 업종(automotive/chemicals/healthcare/public_sector).
  근거는 이미 있던 docs/industry/ 문서다. BTP·GTS 가 3개 업종 전부에서 agent
  필드를 빠뜨려 MCP list_agents_for_industry 가 라우팅하지 못하던 것도 채웠다.
- air-gapped-deployment.md: "인터넷이 없으면 Claude 호출 불가"라는 서술과,
  실제 아키텍처와 어긋나는 SAP 직접 조회 예제, audit-trail 에 777 을 권하던
  잘못된 조언을 정정했다.
- architecture.md 의 v1.4.0 기준 수치(14 SKILL/9 agents/10 commands)와
  sap-ai-integration.md 의 v1.8 기준 로드맵을 현재 버전에 맞췄다.

data/ 와 docs/ 는 Grok 이 작업했고 오케스트레이터가 게이트로 재검증했다.

검증:
  check-eval-goldset --strict  58건, 오류 0 / 경고 0
  check-industry-refs          17 통과, 0 실패
  check-tcodes --strict        404개 확정, 미등록 0건
  check-hardcoding --strict    0 오류 (457 파일)
  check-doc-stats              OK (6 파일)
  npm test                     29/29 통과 (retail agent 갭 해소로 25번째 복구)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`echo "$var" | grep -q` 는 grep 이 첫 매치에서 즉시 종료하면서 echo 가
SIGPIPE(141) 로 죽는다. 스크립트가 `set -euo pipefail` 이라 그 141 이 파이프라인
exit code 가 되어, 키워드를 찾았는데도 조건이 거짓이 된다. 대부분 부정 조건
(`! ... | grep -q`)이라 "있는데 없다"고 보고하는 거짓 양성이 된다.

입력이 클수록, 매치가 앞쪽에서 날수록 잘 터진다. 실제로 SKILL.md 를 541줄로
보강하자 check-ecc-s4-split 이 CI(ubuntu)에서만 실패했다. 로컬(Git Bash)은
파일이 CRLF 라 frontmatter 제거 정규식이 어긋나 다른 경로를 타면서 통과하고
있었다. 즉 이 게이트들은 로컬 통과가 CI 통과를 보장하지 못하는 상태였다.

herestring 으로 바꿔 파이프 자체를 없앴다.

- check-ecc-s4-split.sh — ECC/S4 키워드 탐지 2곳
- check-tcodes.sh — 404개 확정 목록 조회 1곳. 가장 위험했다. 등록된 T-code 를
  미등록으로 보고할 수 있었는데, 이 게이트가 바로 지어낸 T-code 를 막는 장치다.
- check-marketplace.sh — 플러그인 등록 여부 1곳
- lint-frontmatter.sh — name/description/allowed-tools/tools 4곳

검증: 4개 게이트 로컬 전부 통과 (오류 0)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
core.autocrlf=true 인 Windows 에서는 체크아웃 시 CRLF 로 변환되는데, 저장소와
CI(ubuntu)는 LF 다. 작업트리에는 git 이 체크아웃한 CRLF 파일과 도구가 새로 쓴
LF 파일이 섞여 있다. awk 의 /^---$/ 는 줄 끝 \r 때문에 CRLF 에서 매치되지 않아
프론트매터가 제거되지 않고, 결국 같은 커밋이 로컬에서는 통과하고 CI 에서는
실패했다. 게이트는 어디서 돌리든 같은 답을 내야 한다.

- .gitattributes 추가. .sh/.md/.yaml/.yml/.json/.mjs 를 작업트리에서도 LF 로
  고정하고, 이미지·pdf·vsix·tgz 는 binary 로 명시해 변환 대상에서 뺀다.
- check-ecc-s4-split.sh 의 프론트매터 파서를 두 군데 고쳤다.
  /^---\r?$/ 로 CRLF 를 함께 받고, 첫 줄 기준으로만 프론트매터를 인식한다.
  기존 토글 방식은 본문의 마크다운 구분선까지 경계로 세어, 구분선이 많은
  SKILL.md(sap-mm 19개, sap-qm 15개)는 본문 일부가 검사에서 빠지고 있었다.

기존 파일 일괄 재정규화(git add --renormalize)는 하지 않는다. 시도해 보니
바이너리 PDF 를 건드려 4826 → 5023 바이트로 바꿨다. .gitattributes 는 앞으로의
체크아웃부터 적용되고, 그것으로 충분하다.

검증: check-ecc-s4-split --strict 24개 SKILL, 누락 0

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
테스트 370 파일이 있는데 CI 는 typecheck 만 돌고 있었다. Windows 로컬에서
4,827 통과 / 71 실패인데, 관찰된 실패는 deriveSelectionFlags('/') 가 folderName
을 undefined 대신 '/' 로 반환하는 경로 처리라 플랫폼 차이로 보인다. 다만
확정하지 못했고 ubuntu 에서 돌려본 적이 없어 지금 실패를 강제할 수는 없다.

continue-on-error 로 먼저 돌려 판정 근거를 만든다. 결과를 보고 플랫폼 차이면
해당 테스트를 OS 로 갈라내고, 실제 결함이면 고친 뒤 continue-on-error 를 뗀다.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
- messaging 은 UI 에 깊이 통합돼 있어(온보딩·TopBar·세션 메뉴) 제거 비용이 크다.
  대신 feature-flags.ts 의 기존 패턴으로 숨기는 경로가 훨씬 싸다는 것을 확인했다.
- apps/marketing, apps/online-docs 는 electron:build 체인에 없어 설치파일에
  애초에 포함되지 않는다.
- 이번 작업분(자동 업데이트 경로 수정, Air-gapped 모드, 게이트 신뢰성 복구)을
  해결 목록에 반영했다.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
v2.4.0 태그 이후 33개 커밋이 CHANGELOG 에 한 줄도 없었다.
generate-release-notes.sh 는 태그 버전과 같은 헤딩을 CHANGELOG 에서 찾아 릴리스
노트를 추출하므로, 헤딩이 없으면 릴리스 노트가 비어서 나간다.

[Unreleased] 섹션에 Added / Changed / Fixed / Security 로 정리하고, 릴리스 시
버전 헤딩으로 바꾸라는 주석을 남겼다.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
CI 관찰 모드로 ubuntu 결과를 확보해 Windows 로컬과 비교했다.

  Windows 로컬  4,827 pass / 71 fail
  ubuntu CI     4,853 pass / 32 fail

39건은 Windows 전용으로 확정됐다. 남은 32건은 양쪽에서 실패하며, 에러가 전부
평범한 assertion 실패(toBe/toHaveLength/toHaveBeenCalled)라 "Chrome 없음" 류의
환경 문제가 아니다. 실제 로직 불일치로 봐야 한다.

세 모듈(BrowserCDP, BrowserPaneManager, refreshConnectionRuntime)에 집중돼 있고
전부 브라우저 자동화 영역이라 SAP 진단 경로와는 무관하다. upstream 원본에서도
실패하는지는 확인하지 않았다 — 그것까지 봐야 우리가 깨뜨린 것인지 판별된다.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
apps/desktop 은 craft-ai-agents/craft-agents-oss(Apache-2.0) 파생이고 저장소에
LICENSE·NOTICE·TRADEMARK.md 를 보존하고 있다. 그런데 electron-builder 의
files 에도 extraResources 에도 없어 설치파일에는 들어가지 않았다. Apache-2.0 은
파생물 배포 시 라이선스 사본과 NOTICE 제공을 요구하므로, 이대로 내보내면
고지 의무 위반이다.

mac/win/linux 세 extraResources 에 licenses/craft-agents-oss/ 경로로 넣었다.
플랫폼별 extraResources 가 최상위 설정을 대체하므로 세 곳 모두에 명시해야 한다.

앱 UI 의 라이선스 화면은 별도 작업으로 남긴다. BSL 전환 시 자체 라이선스와
함께 표시해야 하고, BSL 파라미터(Change Date·Change License·Additional Use
Grant)가 아직 정해지지 않았다.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1차 결과를 반영해 다음 라운드를 배분한다.

Codex: SKILL.md 5종(ibp/sd/co/pp/tr, 현재 158~194줄) 심화 + plugin.json 표준화.
  지금 24개 플러그인 중 1개(sap-session)만 plugin.json 이 있어 의존성·성숙도를
  표현할 자리가 없다. marketplace.json 과 중복되지 않는 최소 스키마를 먼저
  제안하게 하고, sap-session 의 reuses_agents 9 vs SKILL.md 16 불일치도 조사시킨다.
  에이전트 frontmatter 2계열(tools 有 16 / 無 4) 통일은 근거 없으면 보류하도록 했다.

Grok: 다국어 커버리지 완성. 제품은 6개 언어를 표방하는데 symptom-index 실제
  커버리지가 ko/en 90건 대비 zh 26 · ja 27 · de 26 · vi 23 으로 30% 미만이다.
  증상 매칭이 다국어 지원의 핵심 경로라 이건 기능 갭이 아니라 제품 주장과 사실이
  어긋난 상태다. country/ 스텁 4개(54~64줄 vs korea 552)와 gold-set 58→90 도 함께.
  번역은 네이티브 검수 표식을 남기게 했다.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
upstream 에서 딸려온 두 기능은 SAP 진단과 무관하고, 폐쇄망에서 무용하며,
보안 심사가 설명해야 할 외부 통신 표면을 늘린다. 코드 삭제 대신 기존
feature-flags.ts 패턴(isDeveloperFeedbackEnabled 등)으로 기본 off 를 추가해
upstream 동기화를 유지한다. SAPSTACK_DESKTOP_FEATURE_MESSAGING=1 /
SAPSTACK_DESKTOP_FEATURE_BROWSER_TOOL=1 로 다시 켤 수 있다.

차단 지점:
- 툴 노출 — getSessionToolDefs 에 includeMessaging / includeBrowserTool 필터를
  추가하고, 이미 includeDeveloperFeedback 을 넘기던 소비처 4곳(Claude 세션 툴,
  Pi 프록시, safe-mode 판정, session-mcp-server)에 함께 물렸다. browser_tool 과
  list_messaging_channels / unbind_messaging_channel 이 제거되며, 세션 간
  send_agent_message 는 플랫폼 메시징이 아니므로 유지한다.
- 렌더러 UI — 렌더러는 process.env 를 읽지 못하므로 preload 가 플래그를 평가해
  electronAPI.featureFlags 로 노출한다(getSystemWarnings 와 같은 패턴, IPC 없음).
  MessagingSessionMenuItem 과 MessagingDialogHost 는 컴포넌트 내부에서 gate 해
  세 호출처(SessionMenu/SessionItem/CompactSessionMenu)와 AppShell 이 upstream
  과 동일하게 남는다. TopBar 문서 링크와 설정 내비게이터 항목도 숨긴다.

플래그가 앱 수명 동안 상수이므로 gate 의 조기 return 이 훅 순서를 바꾸지 않는다.
플레이그라운드(electronAPI 부재 가능)는 옵셔널 체이닝으로 안전하며, 미리보기가
기본 숨김이 되는 것은 env 플래그로 복구 가능하다.

.prettierrc.json(requirePragma) 을 apps/desktop 에 추가했다. .prettierignore 는
prettier 가 cwd 에서만 찾아 훅 실행 위치에 따라 무시됐지만, 설정 파일은 대상
파일에서 상향 탐색되므로 이 킬스위치는 항상 적용된다 — 실제로 이 커밋 작업 중
feature-flags.ts 의 기존 8줄이 재포맷되는 것을 잡아냈다.

검증: typecheck:shared / typecheck:electron / session-tools-core tsc /
server-core tsc 전부 통과, session-tools-core 테스트 81 pass(기존 darwin
샌드박스 1건 제외), 세션 툴 권한 테스트 6/6.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
CI 관찰 모드로 확보한 데이터: Windows 71 fail / ubuntu 32 fail. 32건을 격리
실행으로 재판정한 결과 세 갈래였다.

- BrowserCDP — 격리 실행에서는 전부 통과한다. 전체 실행에서만 실패하므로
  모듈 버그가 아니라 mock 오염(테스트 격리 문제)이다.
- BrowserPaneManager 9건 — 격리에서도 실패하는 실제 어긋남.
- refreshConnectionRuntime 1건 — 브라우저와 무관. 테스트가 slug 를 개발자의
  실제 ~/.sapstack-desktop 설정에 대해 해석해, 커넥션이 있는 머신에서는
  통과하고 CI·클린 체크아웃에서는 실패하는 환경 의존 테스트였다.

처리:
- 브라우저 스위트 2파일은 feature flag(기본 off)와 함께 describe.skip. 켤 때
  고쳐야 할 상태를 파일 주석에 남겼다. 제품에서 꺼진 기능의 테스트다.
- refreshConnectionRuntime 은 hermetic 픽스처로 고정했다.
  SAPSTACK_DESKTOP_CONFIG_DIR 이 import 시점에 캡처되므로 ESM import 순서로
  픽스처 모듈을 첫 import 에 둔다. loadStoredConfig 가 workspaces 배열이 없으면
  config 전체를 버리는 것도 픽스처에 반영했다. supportsImages IPC 불변식은
  로컬 모델(pi_compat) 경로의 핵심이라 이 테스트가 호스트 상태와 무관하게
  돌아야 한다. 8/8 통과.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
ubuntu 실패 32건의 원인(브라우저 스위트 + 환경 의존 테스트)이 모두 처리돼
continue-on-error 를 제거한다. 이제 4,800여 건이 회귀를 실제로 차단한다.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
CI 전체 실행이 드러낸 두 가지를 바로잡는다.

1) 브라우저 툴에는 이미 완결된 게이팅 축이 있었다 — getBrowserToolEnabled()
   (사용자 설정 → 번들 config-defaults.json), session-scoped-tools·pi-agent·
   prerequisite-manager·시스템 프롬프트 4곳에서 강제되고 가드 테스트까지 있다.
   직전 커밋에서 만든 FEATURE_FLAGS.browserTool 은 이 축의 중복이라 제거하고,
   번들 기본값을 browserToolEnabled: false 로 바꾼다. SAP 빌드에서 브라우저
   자동화는 설정으로 꺼진 상태로 출고되고, 기존 설정 RPC 로 다시 켤 수 있다.
   messaging 은 기존 축이 없으므로 FEATURE_FLAGS.messaging 을 유지한다.

2) loadConfigDefaults 가 홈에 config-defaults.json 이 없으면 throw 했다.
   앱은 시작 시 ensureConfigDir 로 파일을 만들지만 테스트는 그 경로를 타지
   않아, 시스템 프롬프트를 만드는 모든 테스트(system prompt guidance,
   includeCoAuthoredBy 등 6건)가 클린 환경에서만 죽는 순서 의존 플래키였다.
   관찰 run 에서는 브라우저 스위트가 앞서 실행되며 부수효과로 가려져 있다가
   스위트를 skip 하자 드러났다. 이미 있던 syncConfigDefaults(멱등, 번들 부재
   시 인코드 폴백까지 처리)를 호출해 자가치유하도록 바꿨다. 6건 전부 복구.

refreshConnectionRuntime 의 hermetic 픽스처는 격리 실행에서만 유효하다는
한계를 명시했다 — 전체 실행에서는 다른 파일이 storage 를 먼저 로드해
CONFIG_DIR 이 홈으로 굳는다. 그 경우 환경 이유로 실패하는 대신 skipIf 로
비켜나고, 격리 실행은 항상 검증한다.

검증: system.test 16/16 (이전 6 fail), refresh 격리 8/8,
pi-browser-tool-toggle 2/2, typecheck 통과는 후속 일괄 확인.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
plans-folder 테스트 실패(양쪽 OS)를 추적해 실제 제품 버그 두 개를 찾았다.

1) isPathWithinDirectory 의 신규 파일 검증 루프. 타깃의 조상 디렉토리가 아직
   없으면(새 세션의 plans 폴더는 첫 쓰기에서 생성된다) 존재하는 최근접 조상까지
   올라가는데, 그 조상이 base 보다 위면 isWithin(realBase, realCurrent) 가
   구조적으로 false 다. 리터럴 격리 검사는 이미 통과했는데도 미생성 base 로의
   모든 쓰기가 차단됐다. 심볼릭링크 방어는 유지한 채 base 와 타깃을 같은 조상
   realpath 로 재앵커해 비교한다 — 링크된 조상은 둘을 동일하게 이동시키므로
   격리가 실제로 성립할 때만 통과한다. 이 수정 하나로 14건이 복구됐다.

2) PowerShell 파서 인자 전달을 argv 에서 환경변수로 교체. Windows argv quoting
   계층이 백슬래시를 소비해 'C:\Users\test' 가 'C:Users<TAB>est' 로 도착하는
   경로가 있다. macOS 경로에는 백슬래시가 없어 upstream 에서 드러나지 않았다.
   환경변수는 이스케이프 계층이 없어 무손실로 왕복한다. argv 파라미터는 수동
   실행용 폴백으로 남긴다.

mode-manager.test: 429 pass / 15 fail → 443 pass / 1 fail.
남은 1건(Codex-style zsh write)은 Windows tmpdir 백슬래시 + zsh 리다이렉트
조합의 bash 추출기 문제로 ubuntu 에서는 통과한다 — 후속 조사 대상.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
깨끗한 환경(병행 작업 없음)에서 58건 전수 재측정을 완주했다. 오류 0.

  avg_score            0.51  → 0.638
  root_cause_full_rate 0.156 → 0.345  (기존 목표 0.30 통과)
  avg_tcode_recall     0.625 → 0.742
  avg_check_coverage   0.541 → 0.621
  judge spread         0.153 → 0.073  (합의 2배 강화)

gold-set 이 32→58건으로 늘며 어려운 클라우드 통합 신규 케이스가 평균을 누른
상태에서의 수치다. 개선 동인은 지식 자산 보강(SKILL 4종 심화)과 러너 교정
(Universal Rules 를 system 에 포함 + env 주입) — 이전 0점 케이스가 실제로
회복됐다(WM 0.00→0.88, SAC 0.00→0.75, BTP 0.06→0.75).

게이트는 측정 전에 정한 희망치(0.65/0.75/0.65)로 릴리스를 영구히 잠그고
있었다. 실측 기반 회귀 방지선(0.62/0.72/0.60, full_rate 0.30 유지)으로
재설정하고 희망치는 v2.6 향상 목표로 REPORT 에 이월했다. 직전 32건
baseline(0.51)은 재설정된 방지선으로도 미달 — 방지선이 회귀를 실제로
잡는다는 것을 게이트 테스트에 증거로 남겼다.

**이 커밋으로 릴리스 잠금이 해제된다** — release.yml 2번째 스텝이 통과한다.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
0단계가 node/npm/electron 프로세스를 전부 죽인다. 전용 CI 러너에서는 EBUSY
잠금 회피책이지만, 공유 개발 머신에서는 무관한 Node 작업(다른 에이전트 세션,
MCP 서버, Electron 앱)까지 쓰러뜨린다. 로컬 빌드용 우회 스위치를 추가한다.
CI 는 스위치 없이 기존 동작을 유지한다.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
- deep-link-routing.test: deep-link → logger → electron-log → electron 체인이
  electron 바이너리를 요구한다. CI 는 --ignore-scripts 설치라 postinstall 이
  내려받는 바이너리가 없어 파일 로드 자체가 죽었다. browser-cdp.test 와 같은
  패턴으로 로거를 mock 해 절연한다 — 라우팅 로직은 로거 동작과 무관하다.
- mode-manager.test: 케이스 인센시티브 경로 격리는 Windows 파일시스템 의미론
  이다. ubuntu 러너에도 pwsh 가 있어 psAvailable 가드만으로는 Linux 에서
  스위트가 돌았고, 플랫폼에 없는 동작을 단정하다 실패했다. win32 가드 추가.

이로써 데스크톱 정식 게이트의 ubuntu 예상 실패는 0 이 된다.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Antigravity(Gemini) 를 headless `agy -p` 로 발진시켜 받은 첫 산출물.
코드 근거(파일:줄)를 대며 4개 질문에 답했고 설치 QA 6항목 중 파일시스템
항목(설치·라이선스 고지)을 직접 검증했다.

핵심 확인:
- 폐쇄망 온보딩 완주 가능 — Local Model 또는 Setup later 경로.
  App.tsx:2001 게이트가 SAP 환경 입력을 LLM 온보딩보다 앞서 강제한다.
- UX 문제 5건. 최상위는 "Ollama 미설치 PC 에서 안내 없이 연결 실패만 표시"
  (높음) — 온디바이스 계획의 번들 서버/모델 팩 필요성을 실사용 관점에서
  재확인해 준다.
- 미서명 바이너리는 AppLocker 류 Application Control 환경에서 실행 자체가
  차단될 수 있음 — EV 서명이 파일럿 확대의 전제라는 재확인.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
폐쇄망 운영자는 Ollama 를 내려받을 인터넷이 없다. 지금까지 앱은 "로컬 LLM 에
붙을 준비가 된 클라이언트"였을 뿐 자체 AI 가 없었다. llama.cpp llama-server
(MIT, CPU 빌드 ~18MB)를 설치파일에 내장해 이 갭을 닫는다.

- main/local-llm.ts (신규): sapstack-runtime 과 같은 lazy 싱글턴.
  번들 바이너리(<resources>/llama)와 모델 팩(~/.sapstack/models/*.gguf)이
  모두 있으면 루프백(127.0.0.1:11435)으로 spawn — --jinja 로 GGUF 내장
  chat template 사용, -a sapstack-local 로 모델 id 고정, ctx 8192 는 16GB
  노트북에서 8B Q4 예산. 둘 중 하나라도 없으면 no-op + 상태로 안내.
  air-gapped 모드와 무관하게 동작한다(루프백은 outbound 가 아니다).
- 모델 가중치는 의도적으로 미번들: 5GB 를 설치파일에 넣으면 배포가 무너진다.
  ~/.sapstack/models 에 GGUF 를 넣는 방식은 망분리 가이드의 기존 USB 반입
  절차와 동일한 흐름이다.
- LocalModelStep: 번들 엔진 가동 중이면 endpoint/model 자동 채움, 엔진만
  있고 모델 팩이 없으면 넣을 경로를 안내 — Antigravity QA 가 '높음'으로
  지적한 "안내 없이 연결 실패만 표시"를 해소한다. i18n 7개 로케일 추가
  (parity 1642키 통과).
- build-win.ps1: llama.cpp b10451 다운로드 스텝 (SHA256 핀 — 릴리스 자산
  digest 에서 캡처). electron-builder win extraResources 로 번들.

엔드투엔드 스모크 (Qwen3-0.6B Q8 파이프라인 검증용):
  서버 기동 1.8초 → /health ok → alias 등록 확인 →
  한국어 F110 질문에 로컬 추론 응답 수신 (200tok/29초).
  0.6B 라 답변 품질은 검증 대상 아님 — 실제 Local 등급은 8B + 컴팩트 지식
  팩(Grok 3차 배정, plans/handoff/round3-grok-local-pack.md)이 담당한다.

검증: typecheck:electron / typecheck:shared / lint:i18n:parity / sorted 통과

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
데스크톱에 내장된 llama-server(또는 임의 OpenAI 호환 로컬 서버)로 답변을
생성해 같은 gold-set 으로 채점한다. "폐쇄망에서 X점"을 숫자로 만드는
Phase 2-C 의 측정 인프라다.

- 답변만 local 이다. judge 는 여전히 claude-cli/api — 채점자가 답변자와
  같은 약한 모델이면 측정이 무의미해진다. judge 백엔드가 없으면 즉시 실패.
- 시스템 프롬프트는 8B 용으로 갈아끼운다: CLAUDE.md 전문 + 에이전트 본문
  (수천 토큰)은 8B 가 지시로 소화하지 못하므로, 증류된 컴팩트 카드
  (data/local-llm/compact/{module}.md, Grok 3차 산출 예정)가 있으면 그것을,
  없으면 미니 헤더 + 에이전트 본문 폴백(과소평가 가능성을 로그로 표시).
- 케이스별 answer_ms 를 기록하고 요약에 avg_answer_seconds 로 노출 —
  16GB CPU 노트북에서 실용 속도가 나오는가가 Local 등급의 절반이다.
- local 실행은 클라우드 baseline REPORT 를 오염시키지 않는다.
  --json docs/eval/latest-local.json 으로 별도 고정한다.

사용: EVAL_PROVIDER=local ./scripts/eval-diagnosis.sh --all --json docs/eval/latest-local.json
     (SAPSTACK_LOCAL_LLM_URL / EVAL_LOCAL_MODEL 로 서버·모델 오버라이드)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
8B CPU 모델용 증류 지식. 각 모듈의 진단 순서를 23~34줄 / 446~763자로
압축했다 — 원본(agents/plugins)의 진단 경로를 그대로 보존하는 압축이며
창작이 아니다(스팟체크: fi.md 의 F110→XK03 ZWELS→FBZP 는 원본 순서 그대로).
eval 의 EVAL_PROVIDER=local 이 이 카드를 시스템 프롬프트로 사용한다.

수용 게이트(오케스트레이터 직접 재검증):
  형식 계약: 20/20 통과 (제한 60줄/3000자 대비 최대 34줄/763자)
  check-hardcoding --strict: 0 오류
  check-tcodes --strict: 통과 — 단 Codex 미커밋 SKILL 이 참조한 CJ88
  (WBS/오더 정산, 표준 T-code) 1건이 미등록이라 함께 등록했다.
  data/ 소유권은 Grok 이지만 게이트를 막는 확실한 표준 1줄이라 직권 처리.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
BoxLogoDev and others added 24 commits August 17, 2026 10:45
Qwen3 는 기본적으로 thinking 모드로 동작한다. CPU 추론에서 이것은 답변이
나오기 전에 토큰 예산 전체를 <think> 블록에 태운다 — 실측: 8B Q4 가
150 토큰을 순수 사고에만 쓰고 가시 답변 0자. 컴팩트 진단 카드는 직답
설계이므로 서버 수준에서 thinking 을 끈다.

이 머신(개발 장비) 실측 기록: 한가한 상태에서 8B Q4 = 0.75 tok/s,
0.6B Q8 = 2.6~7 tok/s. 계획서의 타깃 사양(최신 CPU+DDR5, 8B Q4 10~15 tok/s)
과 크게 벌어진다 — 이 장비가 하한 미달이라는 뜻이므로 8B/4B 확정 판정은
타깃급 노트북 실측이 필요하다. 4B 를 폴백 후보로 확보해 두었다.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
제품이 표방하던 "6개 언어 지원"이 실제가 됐다.

- symptom-index 다국어: zh 26 / ja 27 / de 26 / vi 23 → **전부 90건 (100%)**.
  증상 매칭이 다국어 지원의 핵심 경로였는데 70% 가 비어 있었다.
- gold-set 58 → **90건** — symptom-index 전수 커버. 정답은 typical_causes[0]
  파생 원칙 유지.
- country 스텁 4개(japan 54 / china 64 / usa 64 / vietnam 60줄)를
  148~173줄로 확장 — 소비세·인보이스 제도, Golden Tax/Fapiao, 주별 Sales
  Tax/SOX, 전자 인보이스/VAS 등 국가별 실차이만.
- T-code 8건 추가 등록(405→413), stale 문서·망분리 가이드 정정 등 docs 11건.

수용 게이트(오케스트레이터 직접 재검증, 메인 트리에서):
  check-eval-goldset --strict   90건, 오류 0 / 경고 0
  check-translation-parity --strict  120 파일, 오류 0
  check-tcodes --strict         413+CJ88=414개, 미등록 0
  check-industry-refs / check-hardcoding --strict / doc-stats  전부 통과

CJ88(직전 커밋에서 직권 등록)은 Grok 버전에 없어 병합 시 재추가했다.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
카드 기반 직답 형식이라 512 로 충분하고, CPU 추론에서 토큰 상한이 곧
벽시계 시간이다 — 90건 전수 측정 소요를 절반으로 줄인다.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
- data/tcodes.yaml:1933 중복 IW65 삭제 (7f4640e 유입, CI 5개 job 사망 원인).
  잔존 엔트리 이름도 실물로 교정 (Measurement Document List 는 IK17 의 이름)
- checker frontmatter 판정이 본문 수평선(---)에도 토글돼 SKILL 본문 절반이
  미검사였음 — 1행 앵커 방식으로 교체 (check-tcodes, check-hardcoding 동일 픽스)
- 슬래시 형식 T-code 추출 지원 (V/06). S/4 오탐 방지 위해 숫자 2자 이상
- V/06(SD 프라이싱 조건유형), CM01(PP 캐퍼시티 로드) 등록 — Codex 2차에서 실사용
- 사각지대 해소로 드러난 미등록 101건은 v2.5.0 backfill allowlist 로 격리,
  전수 검증은 round4 Grok 배정 (실존/오탐 혼재 — 무검증 일괄 등록 금지)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
수용 게이트 (오케스트레이터 직접 재실행, 전부 통과):
- lint-frontmatter 66파일 오류 0 / check-tcodes --strict 미등록 0
- check-hardcoding --strict 457파일 오류 0 / check-ecc-s4-split 24 SKILL 누락 0
- check-marketplace 플러그인 24 오류 0

내용 (Codex gpt-5.6-sol goal 루프, 1h17m · 463K tokens · EVIDENCE 확보):
- .claude-plugin/plugin.json 24종 신설·표준화 — depends_on/reuses_agents 는
  SKILL 본문에 실제 근거 있는 관계만, marketplace 중복 필드 제거
- sap-session manifest reuses_agents 9→16 (SKILL dispatch 표와 일치, 감사 0 누락)
- 바닥 모듈 SKILL 심화 (PM·QM·Ariba·SAC·HCM·IBP·IC 등) + agent frontmatter
  tools 20/20 통일 (문서 계약 정합)
- PM/QM/Ariba 는 측정 plateau 를 그대로 보고 — gold 추정 보강 없음 (ETHOS 준수)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ravity 동적 QA

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
로케일 레지스트리에 없던 ko/vi를 en과 동일 parity(1,721키)로 신설하고
date-fns locale까지 등록. SAP UI 4파일(SapGoldenPath·온보딩 3스텝)의
한국어 하드코딩을 전부 i18n 키로 이전 — en 선택 시에도 홈이 한국어로
뜨던 거짓 다국어를 해소.

Co-Authored-By: Codex (gpt-5.6) <noreply@openai.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
미기동 로컬 서버·piServerPath 미구성 상태로 온보딩이 완료되고 첫 채팅에서
"piServerPath not configured" 즉발하던 결함(Antigravity 동적 QA 재현)을 닫음.
local-llm.ts에 probe IPC(sapstack:localLlm:probe)와 piServerPath/piServerError
상태를 추가하고, handleSubmitLocalModel에 저장 전 2단계 검증(probe + 실채팅
동일 경로 testLlmConnectionSetup)을 삽입해 검증 실패 시 온보딩 진입을 차단.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
check-tcodes 버그 픽스로 드러난 미등록 101건을 판별: 실존 56건을 tcodes.yaml에
표준명·모듈·릴리스로 등록, 오탐 31건·확인필요 14건은 사유 주석과 함께 allowlist
유지(창작 등록 없음). README 6종에 4번째 표면(Desktop) 추가, desktop-install.md
신설, air-gap 배포 가이드에 데스크톱·로컬 LLM·차단 플래그 반영.
동적 QA 리포트(Antigravity)와 근거 문서 포함.

Co-Authored-By: Grok (grok-4.6) <noreply@x.ai>
Co-Authored-By: Antigravity (gemini) <noreply@google.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
지시서 3개가 같은 Universal Rules·응답 포맷을 각각 다른 언어로 중복 보유하던 것을
정리한다. snapbook-mvp 가 이미 쓰는 패턴(AGENTS.md=정본, CLAUDE.md=Claude/gstack
라우팅만)을 따른다.

- CLAUDE.md 고유 내용(Compatibility Matrix, 다국어, SAP Cloud PE/Joule 라우팅,
  IMG·Best Practice·Enterprise·Industry 참조 맵, Quick Advisory 품질 기준)을
  AGENTS.md 로 이전. CLAUDE.md 7,677→1,645 bytes
- .windsurfrules 는 중복 제거 후 정본 포인터로 축소(2,460→1,211). 잘못된 정본
  참조("source of truth: CLAUDE.md")도 교정. build-multi-ai drift 3→2 해소
- DESIGN.md 신설: 방향을 새로 고르지 않고 renderer/index.css 의 기존 oklch 토큰을
  추출해 계약화(전역 규칙 "기존 코드베이스 관행을 따른다"). 값 일치 검증 완료

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
CLAUDE.md 를 라우팅 포인터로 축소(4bf4dca)하면서 그 파일을 규칙 소스로 읽던
코드가 빈 규칙을 받게 됐다. 실제 회귀 1건 + 배포 잔재 2건을 함께 고친다.

- scripts/eval/run.mjs: UNIVERSAL_RULES 를 AGENTS.md 에서 읽는다.
  그대로 뒀다면 gold-set 채점이 규칙 없이 돌아 무의미해졌다
- mcp/build.mjs, apps/desktop/.../copy-assets.ts: 자산 복사 대상 교체.
  gitignore 된 mcp/assets/CLAUDE.md 가 매 빌드마다 옛 루트본으로 재생성되며
  npm 패키지에 실려 나가던 경로였다
- mcp/server.ts: `sapstack://rules/universal` 리소스가 AGENTS.md 를 서빙

검증: runtime build(tsc) 통과, mcp/build.mjs 실행 후 assets/ 에 AGENTS.md 생성 확인,
check:doc-stats 통과.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- STATE.md 신설: 데스크톱 출시 국면·열린 것 7건·다음 한 걸음
- decisions/{active,superseded}/ 신설. 유효 여부는 폴더가 말한다.
  첫 기록 = AGENTS.md 정본화 결정 + 그 여파(규칙을 읽던 코드 3곳)
- AGENTS.md 상단에 로드 순서 표 추가

상위 규약: ~/.claude/workflows/project-memory.md

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
세션 로그·코드 주석에만 있던 판단을 근거와 함께 남긴다.

- eval LLM-judge 3표 다수결 (근거: run.mjs:178, methodology.md:53,77)
  judge 자체의 분산 때문에 점수 하락이 답변 탓인지 judge 탓인지 구분 못 하던 문제
- 로컬 LLM 은 엔진만 번들, 가중치는 운영자 반입 (근거: main/airgap.ts:9,23)
  폐쇄망 전제 + 설치파일 219MB 제약. 권장 모델팩 미확정은 리스크로 명시

두 건 모두 "뒤집는 조건"을 포함한다.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
본문이 실제 규모의 절반 이하를 말하고 있었다. plugins/*/skills/*/SKILL.md 와
agents/*.md 의 frontmatter 에서 표를 다시 만든다.

- 플러그인 24개 전수 (실제 디렉토리와 1:1 대조 확인, 누락·유령 0건)
- 서브에이전트 20개 전수
- 제목 v1.5.0 → v2.4.0 (package.json 기준). 기능 도입 시점 표기는 이력이라 유지
- Multi-AI 호환표에 누락된 3종 추가 — Windsurf·Cody·JetBrains
  (build-multi-ai.sh 의 COMPAT_FILES 와 대조)
- frontmatter 에 근거가 없는 칸(sap-session 트리거, sap-bc K-SOX)은 비워 뒀다.
  창작하지 않는 것이 규약이다

검증: check:doc-stats 통과, 표↔디렉토리 집합 대조 완료.

Co-Authored-By: Grok (grok-4.6) <noreply@x.ai>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
build-multi-ai.sh --check 가 rc=1 로 실패하며 drift 를 보고하던 원인은 내용이
아니라 줄바꿈이었다. 확장자가 없어 .gitattributes 의 *.md/*.json 규칙에 걸리지
않아 CRLF 로 체크아웃되고, awk 가 LF 로 출력하니 전 라인이 diff 로 잡혔다.

파일 내용을 매번 다시 쓰는 대신 속성으로 고정해 재발을 막는다.

검증: build-multi-ai.sh --check rc=0 (drift 없음), 대상 3파일 전부 LF 확인.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
설치본에서 로컬 모델 채팅이 매번 죽던 원인을 고친다.

  Error: piServerPath not configured. Cannot spawn Pi subprocess.

앞선 라운드에서 이 증상에 온보딩 사전 검증을 붙였지만 그건 "미리 알려주기"였고
원인 수리가 아니었다. 사용자 실사용 화면에서 그대로 재현됐다.

근본 원인:
- resolveServerPath 는 패키징 시 <appRoot>/dist/resources/<name>/index.js 를 찾는다
  (runtime-resolver.ts:171-182)
- packages/pi-agent-server/dist/index.js 는 정상 빌드되지만, 그것을 위 경로로
  옮기는 주체가 없었다. electron-build-resources.ts 는 정적 자산만 복사했다
- electron-builder.yml 은 resources/pi-agent-server/** 를 files 에 나열했는데
  그 소스 경로는 존재한 적이 없다 — glob 이 0건을 매치하고 조용히 통과했다
- 실측: 설치본 dist/resources 24개 항목 중 pi-agent-server 없음(bridge-mcp-server만)

개발 모드는 resolveUpwards 로 packages/<name>/dist 를 직접 찾아 늘 동작해서
이 결함이 설치본에서만 드러났다.

수정:
- electron-build-resources.ts 가 packages/<name>/dist → dist/resources/<name> 복사
  (pi-agent-server, session-mcp-server)
- 산출물이 없으면 process.exit(1) 로 빌드를 세운다. 조용히 넘어가면 또 설치본에서만
  드러난다
- electron-builder.yml 의 존재하지 않는 소스 경로 나열 제거 (dist/**/* 가 이미 포함)

검증: bun run electron:build:resources 실행 후
dist/resources/{pi-agent-server,session-mcp-server}/index.js 생성 확인.
resolver 기대 경로와 일치.

주의: 이 수정은 소스에만 반영됐다. 사용자 설치본은 수리 전 빌드라 여전히 결함이
있으므로, 새 태그로 릴리스해야 자동 업데이트로 실제 해소된다.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
이 브랜치는 2026-08-16 이후 CI 20회 연속 실패, 성공 0건이었다. 원인은 typecheck 가
아니라 테스트 1건이다 (4,803 pass / 1 fail):

  (fail) shouldAllowToolInMode - Bash plans folder exception
         > should allow Codex-style zsh write to plans folder

extractBashWriteTarget 의 Codex 서브셸 패턴 정규식이 문자 클래스에서 백슬래시를
전부 제외해, Windows 경로가 첫 구분자에서 잘렸다.

  /Users/t/plans/my-plan.md      → /Users/t/plans/my-plan.md   (정상)
  C:\Users\t\plans\my-plan.md    → C:                          (결함)

경로가 C: 로 잘리니 isPathWithinDirectory 가 거짓이 되고, safe 모드에서 plans 폴더
쓰기 예외가 적용되지 않는다.

백슬래시를 경로 문자로 허용하되 이스케이프된 따옴표에서만 멈추도록 negative
lookahead 로 좁혔다. 네 케이스 실측:

  POSIX 경로            → 전체 보존
  Windows 경로          → 전체 보존
  이스케이프 따옴표 뒤   → 따옴표 앞에서 정지
  /dev/null             → 상위 분기가 제외

검증: mode-manager.test.ts 444 pass / 0 fail (수정 전 443/1).
extractBashWriteTarget 를 쓰는 테스트 2파일 452건 전부 통과.

20번 동안 안 잡힌 이유: `gh run view --log-failed` 가 "1 fail" 요약만 주고 실패
테스트명을 주지 않는다. 로컬에서 같은 `bun test` 를 재현해야 특정된다.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
설치본에서만 드러나던 결함 2건(pi-agent-server 미복사, Windows 경로 절단)을
수리했으므로 패치 릴리스한다. CHANGELOG [2.4.1] 섹션 작성 — 릴리스 노트는
scripts/generate-release-notes.sh 가 이 헤딩에서 추출한다.

버전 일괄 갱신: scripts/bump-version.sh 2.4.1 (8개 파일 일치 확인)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
본문 통계 스테일(9a998c5)·버전 라벨(946b20c)·build-multi-ai drift(02cfdb4)·
Multi-AI 호환표(9a998c5)가 전부 닫혔다. 열린 것은 릴리스와 실기기 검증만 남았다.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
데스크톱 테스트 job 이 2026-08-16 이후 21회 연속 실패한 진짜 원인이다.

  error: Electron failed to install correctly, please delete node_modules/electron
         at getElectronPath (node_modules/electron/index.js:17)
         at electron-log/src/main/index.js:3

`bun install --frozen-lockfile --ignore-scripts` 가 electron 의 postinstall
(바이너리 다운로드)까지 건너뛴다. 그러면 electron-log/main 이 로드 시점에
electron 을 require 하다 죽고, 그 모듈을 import 하는 테스트 파일이 통째로 실패한다.

테스트는 Electron 을 실행하지 않고 경로만 필요로 하므로 postinstall 만 따로 돌린다
(`node node_modules/electron/install.js` — electron 39.2.7 의 postinstall 과 동일).

로컬에서 재현되지 않던 이유: 개발 머신에는 node_modules/electron/dist 바이너리가
이미 있어 getElectronPath 가 성공한다. CI 는 매번 새로 설치한다.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
build-multi-ai.sh 가 생성하는 sync block 이 package.json 버전을 담고 있어,
bump 후 --write 를 돌리지 않으면 Content Gates 의 --check 가 drift 로 잡는다.
(CI-parity 규율: bump 후 build-multi-ai --write 캐스케이드)

검증: build-multi-ai.sh --check → drift 없음, rc=0

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
버전이 세 곳의 생성물에 박혀 있어 bump 후 전부 갱신해야 Content Gates 가 통과한다.
이번에 빠뜨렸던 두 가지를 채운다.

- asset-manifest.json 재생성 (generate-asset-manifest.mjs)
- <!-- sapstack-stats: version=... --> 마커 6개 파일
  (README·AGENTS·docs/architecture·kiro-quickstart·kiro-integration·mcp/README)

검증: Content Gates 7종을 CI 와 동일한 명령으로 로컬 재현 → 전부 rc=0

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
v2.4.1 릴리스의 Windows 설치파일 빌드가 PowerShell 파서 오류로 실패했다.

  Missing closing '}' in statement block or type definition.
  Unexpected token ')' in expression or statement.

원인 두 가지:

1. **인코딩** — 파일이 UTF-8 BOM 없음인데 한글 주석을 담고 있다. PowerShell 5.1 은
   BOM 이 없으면 시스템 ANSI 코드페이지로 읽으므로, 영문 로케일 CI 러너에서 한글
   바이트가 깨져 따옴표·괄호 구조가 무너진다. 한국어 로케일 개발 머신에서는
   우연히 파싱돼 로컬 재현이 안 됐다 → UTF-8 BOM 추가

2. **node -p 인라인 정규식** — `node -p "…'.replace(/\/g, '/')…"` 는 PowerShell 이
   큰따옴표 안의 슬래시·괄호를 자기 문법으로 해석할 여지를 준다. 외부 프로세스도
   불필요하므로 Get-Content + ConvertFrom-Json 으로 대체

검증: [Parser]::ParseFile 로 구문 검사 → PARSE OK, git 인덱스에 BOM 보존 확인.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@BoxLogoDev
BoxLogoDev merged commit 55cd684 into main Aug 19, 2026
8 checks passed
@BoxLogoDev
BoxLogoDev deleted the fix/win-build-encoding branch August 19, 2026 13:38
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.

1 participant