187 lines
41 KiB
Markdown
187 lines
41 KiB
Markdown
# Webmaker 1.8.0 직접 편집(시각 편집기) 도입 — Ariweb Suite 0.2.0 TDD RED→GREEN 기록
|
||
|
||
- 작업 트리: `/home/master/work/Ariweb-suite-visual-editor-v020`, 브랜치 `feature/visual-editor-v0.2.0`, 기준 `origin/main 4b3714e37c543fcfa8862dbc68c76f62a9b0bf97`
|
||
- 참조 저장소 작업 트리: `/home/master/work/WebHoting_AI_Agent-visual-editor-v180`(`feature/visual-editor-v1.8.0`, 기준 `3b10859`) — 그쪽 RED→GREEN 은 `modules/webmaker/../docs/verification/visual-editor-1.8.0-tdd.md`(참조 저장소 문서)에 있다.
|
||
- 날짜: 2026-10-28 · Node v24.22.1 · 커밋·push·병합·배포·운영 접근 없음(Neo 검토 전 인도). 출력은 실제 실행 결과를 그대로 옮겼다(스택 줄만 줄임).
|
||
- 서버를 띄우는 테스트는 모두 `CLAUDE_BIN` 을 즉시 실패하는 가짜 실행 파일로 두고 실행했다(사이트 생성이 AI 작업을 시작하므로 개발 PC 의 진짜 claude 가 실행되지 않게). 첫 Suite 테스트 실행에서는 그 설정이 빠져 진짜 claude 프로세스가 하나 시작되어 즉시 종료시켰고, 테스트를 고쳤다.
|
||
|
||
## RED — `node --no-warnings test/webmaker-visual-editor.test.mjs` (도입 전: 모듈·화면·경로 없음)
|
||
```
|
||
✘ 도입 모듈에 직접 편집 순수 모듈·런타임·화면 파일이 있고 Webmaker 1.8.0 · Suite 0.2.0 이다
|
||
Error: lib/visual-editor.js (실제 false)
|
||
✘ lib/visual-editor: 캔버스 변환은 스크립트를 실행하지 않게 하고, 저장 검증은 새 스크립트·핸들러를 거절한다
|
||
Error: Cannot find module '/home/master/work/Ariweb-suite-visual-editor-v020/modules/webmaker/lib/visual-editor.js'
|
||
✘ visual-editor-core: 실행 취소/다시 실행·저장 상태·메시지 검증·구성 요소가 Suite 체크아웃에서도 같다
|
||
Error: Cannot find module '/home/master/work/Ariweb-suite-visual-editor-v020/modules/webmaker/public/visual-editor-core.js'
|
||
✘ 화면(index.html)에 직접 편집 전환·저장·실행 취소·다시 실행·버리기·팔레트·속성 조작이 접근성 속성과 함께 있고 Suite 배너는 남아 있다
|
||
Error: 직접 편집 전환 버튼 (실제 false)
|
||
✘ server.js [Ariweb Suite 변경]: 직접 편집 저장도 동시 편집 rev 충돌(409)을 돌려주고, 맞는 rev 면 저장된다 (실제 서버)
|
||
Error: 기대 200 실제 404
|
||
✘ 통과 0 · 실패 5
|
||
```
|
||
|
||
## 도입 작업
|
||
1. 참조 저장소에서 1.8.0 을 TDD 로 구현(위 참조 문서). 새 파일 6개는 `cp` 로 그대로 가져옴(`lib/visual-editor.js`, `public/visual-editor-core.js`, `public/wm-ve-canvas.js`, `public/visual-editor-ui.js`, `test/visual-editor.test.js`, `test/visual-editor-server.test.js`), `public/admin.js` 는 Suite 변경이 없어 upstream 그대로.
|
||
2. Suite 블록(`[Ariweb Suite 변경]`)이 있는 `server.js`·`public/app.js`·`public/index.html`·`public/style.css` 는 **같은 변경을 손으로 적용**(덮어쓰지 않음) + `server.js` 직접 편집 PUT 에 `revConflict` 추가. `diff` 로 upstream 과의 차이를 확인: server.js 107줄(모두 0.1.1 Suite 블록 + rev 검사 1줄), app.js 4줄, index.html 2줄, style.css 5줄(배너) — 1.8.0 공통 변경은 양쪽 동일.
|
||
3. 중간 실행 1 — 4/5: rev 충돌 테스트가 "AI 작업 실행 중" 409 를 받음(진짜 claude 가 생성 작업으로 실행 중) → 테스트에 가짜 `CLAUDE_BIN`·작업 종료 대기 추가.
|
||
4. `WEBMAKER_SRC=<참조 작업 트리> node scripts/import-sources.mjs --only webmaker --allow-dirty` 로 `IMPORTED_SOURCES.json/.md` 재생성(처음 182 파일 → 최종 184 파일: 0.1.1 의 176 + 신규 8, 수정 8 = 0.1.1 과 같은 Suite 블록 파일, upstream 1.8.0·workingTreeDirty).
|
||
|
||
## GREEN — `node --no-warnings test/webmaker-visual-editor.test.mjs`
|
||
```
|
||
✔ 도입 모듈에 직접 편집 순수 모듈·런타임·화면 파일이 있고 Webmaker 1.8.0 · Suite 0.2.0 이다 (1ms)
|
||
✔ lib/visual-editor: 캔버스 변환은 스크립트를 실행하지 않게 하고, 저장 검증은 새 스크립트·핸들러를 거절한다 (13ms)
|
||
✔ visual-editor-core: 실행 취소/다시 실행·저장 상태·메시지 검증·구성 요소가 Suite 체크아웃에서도 같다 (2ms)
|
||
✔ 화면(index.html)에 직접 편집 전환·저장·실행 취소·다시 실행·버리기·팔레트·속성 조작이 접근성 속성과 함께 있고 Suite 배너는 남아 있다 (3ms)
|
||
✔ server.js [Ariweb Suite 변경]: 직접 편집 저장도 동시 편집 rev 충돌(409)을 돌려주고, 맞는 rev 면 저장된다 (실제 서버) (1323ms)
|
||
✔ 통과 5 · 실패 0
|
||
```
|
||
|
||
## 전체 묶음
|
||
- `modules/webmaker`: `CLAUDE_BIN=<가짜 exit 1> npm test` → `ℹ tests 81 · pass 81 · fail 0` (0.1.1 의 59 + 새 22)
|
||
- Suite: `CLAUDE_BIN=<가짜 exit 1> node test/run.mjs` → **`✔ 테스트 파일 23개 중 실패 0개`**(agent 9·auth 7·backup 8·commerce 11·dashboard 4·domains 3·e2e 11·groups 5·hal-cli 3·hal-mailimport 7·hal-ui 7·hal 16·lib 10·lifecycle 6·mail-discover 2·mail 9·relocation 6·sites 11·transfer 4·usage 4·webmaker-adapter 2·webmaker-inventory 5·webmaker-visual-editor 5). 로컬 E2E 하네스가 `docs/verification/local-e2e-2026-10-28.md/.json` 을 새로 썼다(결함 0).
|
||
|
||
## 요구 ↔ Suite 테스트 대응
|
||
| 요구 | 테스트 |
|
||
|---|---|
|
||
| 도입 파일·버전(1.8.0/0.2.0)·IMPORTED_SOURCES 불변 기준 커밋·upstream 1.8.0 | 도입 모듈 파일·버전 |
|
||
| 스크립트 비실행 변환·저장 검증(새 스크립트 거절·머리 보존) | lib/visual-editor |
|
||
| 실행 취소/다시 실행·저장 상태·메시지 규약(세션 불일치 거절)·구성 요소 안전 | visual-editor-core |
|
||
| 필수 접근성 조작 + Suite 배너·SSO 변경 보존 | 화면(index.html) |
|
||
| Suite 전용 rev 충돌 409 → 맞는 rev 저장 200·버전 visual-edit·rev 증가(실제 서버) | server.js [Ariweb Suite 변경] |
|
||
| 권한·CSRF·허용 페이지·오래된 저장·AI 작업 중·캐시 미차감 | `modules/webmaker/test/visual-editor-server.test.js`(81개 묶음 안) |
|
||
|
||
## 검토 수정(Neo 독립 검토, 2026-10-28) — 동시 저장 경쟁(출시 차단) · 원자적 저장(안전 강화)
|
||
|
||
참조 저장소에서 RED→GREEN 으로 고친 뒤(참조 문서 "검토 수정" 절: 경쟁 테스트 actual 200/expected 409, 원자성 테스트 actual 200/expected 500, helper 없음 TypeError → 모두 GREEN) Suite 에 반영했다.
|
||
- 그대로 가져옴(`cp`, diff 동일): `modules/webmaker/lib/visual-editor.js`(`writeTextAtomic` 추가), `test/visual-editor.test.js`(16건), `test/visual-editor-server.test.js`(9건 — 경쟁·원자성 추가).
|
||
- 손으로 반영: `modules/webmaker/server.js` 직접 편집 PUT — 본문을 읽은 뒤 `store.load` 로 사이트 기록과 파일을 다시 읽고(**Suite 의 `revConflict` 도 다시 읽은 최신 rev 로 비교**), hash 검사·검증·`writeTextAtomic` 까지 await 없이 이어 간다. 쓰기 실패 500 한국어 안내. upstream 과의 차이는 여전히 Suite 블록만(107줄).
|
||
- Suite 전용 추가 테스트는 두지 않았다: 공유 테스트 파일(`modules/webmaker/test/visual-editor-server.test.js`)이 Suite 모듈 서버로도 실행되어 경쟁·원자성을 검증하고, `test/webmaker-visual-editor.test.mjs` 의 rev 충돌 테스트가 다시 읽은 rev 경로를 그대로 지나기 때문이다.
|
||
|
||
GREEN(Suite 체크아웃)
|
||
```
|
||
cd modules/webmaker && node --test test/visual-editor-server.test.js test/visual-editor.test.js
|
||
ℹ tests 25
|
||
ℹ pass 25
|
||
ℹ fail 0
|
||
node --no-warnings test/webmaker-visual-editor.test.mjs
|
||
✔ server.js [Ariweb Suite 변경]: 직접 편집 저장도 동시 편집 rev 충돌(409)을 돌려주고, 맞는 rev 면 저장된다 (실제 서버) (1213ms)
|
||
✔ 통과 5 · 실패 0
|
||
```
|
||
전체 묶음(수정 뒤, 모두 `CLAUDE_BIN=<가짜 exit 1>`):
|
||
- 참조 저장소 `npm test` → `ℹ tests 77 · pass 77 · fail 0`
|
||
- Suite `modules/webmaker` `npm test` → `ℹ tests 84 · pass 84 · fail 0`
|
||
- Suite `node test/run.mjs` → `✔ 테스트 파일 23개 중 실패 0개`(webmaker-visual-editor 5/5 포함, 로컬 E2E 가 `local-e2e-2026-10-28.md/.json` 을 다시 씀, 결함 0)
|
||
|
||
## 실제 브라우저 시각 검증 수정(Neo 의 브라우저 게이트, 2026-10-28) — 직접 편집 중 배치(출시 차단)
|
||
- 발견: 독립 Webmaker 코드의 실제 브라우저 검증(1280×633·390×844)에서 직접 편집을 켜도 옛 작업 칸(`.left` 460px)이 남아 캔버스 iframe 이 114.6px, 390px 에서는 캔버스가 y≈2228 로 밀림. 원인은 `body.ve-on` 의 CSS 배치 규칙 부재(JS 전환은 정상).
|
||
- 참조 저장소에서 RED(`test/visual-editor.test.js` +3: CSS 계약 2건 실패 `직접 편집 중 옛 작업 칸 숨김`·`좁은 화면 body.ve-on .app 규칙`, JS 전환 동작 1건은 통과 — 진단 확인) → `public/style.css` 수정 → GREEN 19/19. Suite 에 **동일하게** 반영: `modules/webmaker/public/style.css` 같은 블록을 손으로 추가(upstream 과의 차이는 여전히 배너 5줄), 테스트 파일은 `cp`(diff 동일). Suite 모듈에서 `node --test test/visual-editor.test.js` → `ℹ tests 19 · pass 19 · fail 0`.
|
||
- 전체 묶음(배치 수정 뒤, 모두 `CLAUDE_BIN=<가짜 exit 1>`): 참조 저장소 `npm test` → `ℹ tests 80 · pass 80 · fail 0` · Suite `modules/webmaker` `npm test` → `ℹ tests 87 · pass 87 · fail 0` · Suite `node test/run.mjs` → `✔ 테스트 파일 23개 중 실패 0개`(webmaker-visual-editor 5/5 포함). 하네스가 다시 쓴 `local-e2e-2026-10-28.*` 보고서 파일은 이번 작업의 산출물이 아니라 보존하지 않았다.
|
||
- 문법·차이 확인: `node --check public/visual-editor-ui.js`(두 저장소) 통과, `style.css` upstream 대비 차이 5줄(포털 배너)만, `test/visual-editor.test.js` 두 저장소 동일.
|
||
|
||
## 실제 브라우저 시각 검증 수정 2(배치 수정 뒤 게이트, 2026-10-28) — 선택 전 도구 줄 노출(출시 차단)
|
||
- 발견: 직접 편집을 켜고 아무 요소도 고르지 않았는데 어두운 선택 도구 줄 `.wm-ve-tools` 가 페이지 위에 이미 그려짐(DOM 은 `tools.hidden = true`, 속성 창은 "선택한 요소 없음"). 원인은 캔버스 런타임(`wm-ve-canvas.js`)의 작성자 규칙 `.wm-ve-tools{…display:flex…}` 가 UA 규칙 `[hidden]{display:none}` 을 덮어쓰는 것.
|
||
- 참조 저장소에서 RED(`test/visual-editor.test.js` +1: 런타임 소스의 CSS 블록을 파싱해 `hidden` 인 `.wm-ve-tools`·`.wm-ve-drop` 의 최종 display 를 cascade 모형으로 계산 — `AssertionError: hidden 인 .wm-ve-tools 의 최종 display`, actual 'flex' / expected 'none', 20건 중 19 통과 1 실패) → `public/wm-ve-canvas.js` 런타임 CSS 에 한 줄 추가 `.wm-ve-tools[hidden],.wm-ve-drop[hidden]{display:none !important}` → GREEN 20/20. Suite 에는 `modules/webmaker/public/wm-ve-canvas.js`·`test/visual-editor.test.js` 를 `cp`(두 파일 모두 upstream 과 byte-identical, `cmp` 확인). Suite 모듈에서 `node --test test/visual-editor.test.js` → `ℹ tests 20 · pass 20 · fail 0`. 다른 동작 변경 없음(JS 의 hidden 전환·보일 때 flex 배치 그대로, 테스트가 함께 확인).
|
||
- 전체 묶음(도구 줄 수정 뒤, 모두 `CLAUDE_BIN=<가짜 exit 1>`): 참조 저장소 `npm test` → `ℹ tests 81 · pass 81 · fail 0` · Suite `modules/webmaker` `npm test` → `ℹ tests 88 · pass 88 · fail 0` · Suite `node test/run.mjs` → `✔ 테스트 파일 23개 중 실패 0개`(webmaker-visual-editor 5/5 포함). 하네스가 다시 쓴 `local-e2e-2026-10-28.*` 보고서 파일은 보존하지 않았다.
|
||
- 문법·차이 확인: `node --check public/wm-ve-canvas.js`(두 저장소) 통과, `wm-ve-canvas.js`·`test/visual-editor.test.js` 두 저장소 동일, `style.css` upstream 대비 차이는 여전히 배너 5줄.
|
||
|
||
## 실제 브라우저 게이트 3(Playwright 자동 게이트, 2026-10-28) — 팔레트 → 캔버스 끌어다 놓기 실패(출시 차단)
|
||
- 발견: 실제 Chrome 에서 frameLocator 로 캔버스 iframe 을 직접 조작한 게이트가 선택·contenteditable 글 편집·실행 취소/다시 실행·Alt+↑/↓ 이동·팔레트 표시(19/19)를 통과시킨 뒤, 부모 팔레트 "문단" 의 실제 HTML5 끌어다 놓기에서 실패(`끌어다 놓기 뒤 요소 수 증가` 없음). 원인: 부모가 전용 MIME `text/wm-ve-component` 만 넣는데 캔버스는 `sandbox="allow-scripts"`(allow-same-origin 없음) 출처 'null' 문서라 Chromium 이 다른 출처의 전용 MIME 끌기 자료를 넘겨주지 않음. 제품 결함.
|
||
- 참조 저장소에서 RED(`test/visual-editor.test.js` +3 — 순수 추출 규칙 `core.dragComponentId` 미존재, 팔레트 dragstart 에 text/plain 없음, **동작 테스트**: 가짜 DOM 위에서 실제 `wm-ve-canvas.js` 를 부트하고 전용 MIME 이 없는 DataTransfer 로 dragover/drop → `dragover preventDefault` actual false; 23건 중 20 통과 3 실패) → 수정 → GREEN 23/23. 수정: `public/visual-editor-core.js` 에 `COMPONENT_MIME`·`componentDragText`·`isComponentDrag`·`dragComponentId`(전용 MIME 우선, 없으면 `wm-ve-component:<id>` 정확 일치 + 알려진 id 만), `public/visual-editor-ui.js` dragstart 가 text/plain 도 넣음, `public/wm-ve-canvas.js` dragover/drop 이 종류로 인식하고 drop 때 검증된 id 만 삽입(아니면 삽입·표식 없음, 기본 drop 차단). sandbox·CSP·메시지 검증·저장 검증·"추가" 버튼 대안 불변.
|
||
- Suite 에는 `modules/webmaker/public/visual-editor-core.js`·`visual-editor-ui.js`·`wm-ve-canvas.js`·`test/visual-editor.test.js` 를 `cp`(네 파일 모두 upstream 과 byte-identical, `cmp` 확인). Suite 모듈에서 `node --test test/visual-editor.test.js` → `ℹ tests 23 · pass 23 · fail 0`.
|
||
- 전체 묶음(끌어다 놓기 수정 뒤, 모두 `CLAUDE_BIN=<가짜 exit 1>`): 참조 저장소 `npm test` → `ℹ tests 84 · pass 84 · fail 0` · Suite `modules/webmaker` `npm test` → `ℹ tests 91 · pass 91 · fail 0` · Suite `node test/run.mjs` → `✔ 테스트 파일 23개 중 실패 0개`(webmaker-visual-editor 5/5 포함). 하네스가 다시 쓴 `local-e2e-2026-10-28.*` 보고서 파일은 보존하지 않았다.
|
||
- 문법·차이 확인: `node --check` 세 런타임 파일(두 저장소) 통과, 공유 파일 두 저장소 동일, `style.css` upstream 대비 차이는 여전히 배너 5줄, `server.js`·`app.js`·`index.html` 의 Suite 블록 차이 불변.
|
||
|
||
## 실제 브라우저 게이트 4(살균 DnD 진단, 2026-10-28) — HTML5 끌기가 sandbox iframe 으로 넘어가지 않음 → 포인터 끌기 다리(출시 차단)
|
||
- 발견: MIME 보완 뒤 같은 게이트 재실행도 끌어다 놓기 단계에서 실패. 부모·캔버스 양쪽 살균 리스너(종류·플래그만) 진단: 부모 dragstart 2건(text/plain·전용 MIME 모두 유효), **캔버스 dragenter·dragover·drop 0건**. Chromium 은 HTML5 끌기 자체를 부모 → 출처 'null' sandbox iframe 으로 넘기지 않는다. 제품 결함.
|
||
- 참조 저장소에서 RED(`test/visual-editor.test.js` +3 — 규약·좌표 순수 규칙 `paletteDragAt 는 부모→캔버스 종류`, 부모 포인터 순서 동작 `pointerdown/move/up 처리기`, 캔버스 수신 동작 `놓기 표시 보임`; 26건 중 23 통과 3 실패) → 수정 → GREEN 26/26. 수정(sandbox 그대로, allow-same-origin 없음): `public/visual-editor-core.js` 에 toCanvas 종류 `paletteDragAt`·`paletteDragCancel`·`paletteDropAt` + `isComponentId`·`POINTER_DRAG_THRESHOLD`(4px)·`pointInFrame`(테두리·스크롤바 보정 상대 좌표)·`canvasPoint`(유한 숫자·뷰포트 안); `public/visual-editor-ui.js` 팔레트 항목 포인터 다리(pointerdown — "추가" 버튼·터치·보조 버튼 제외 → 포인터 캡처 → 문턱 → iframe 안에서만 상대 좌표 메시지, 밖·Escape·pointercancel·끝내기 → 취소, 포인터 끌기 중 네이티브 dragstart 차단, HTML5 DnD 는 대체 경로); `public/wm-ve-canvas.js` 가 메시지 규약 뒤 id·좌표·모드를 다시 검사해 스스로 `dropTargetAt` → 놓기 표시 / `insertComponent`(잘못된 입력은 삽입·표식 없이 표시만 숨김); `public/style.css` 에 `.ve-comp { user-select:none; touch-action:pan-y }`·끌기 중 class 2줄.
|
||
- Suite 에는 `modules/webmaker/public/visual-editor-core.js`·`visual-editor-ui.js`·`wm-ve-canvas.js`·`test/visual-editor.test.js` 를 `cp`(네 파일 모두 upstream 과 byte-identical, `cmp` 확인), `modules/webmaker/public/style.css` 에는 같은 2줄을 손으로 추가(upstream 대비 차이 여전히 배너 5줄). Suite 모듈에서 `node --test test/visual-editor.test.js` → `ℹ tests 26 · pass 26 · fail 0`.
|
||
- 전체 묶음(포인터 다리 뒤, 모두 `CLAUDE_BIN=<가짜 exit 1>`): 참조 저장소 `npm test` → `ℹ tests 87 · pass 87 · fail 0` · Suite `modules/webmaker` `npm test` → `ℹ tests 94 · pass 94 · fail 0` · Suite `node test/run.mjs` → `✔ 테스트 파일 23개 중 실패 0개`(webmaker-visual-editor 5/5 포함). 하네스가 다시 쓴 `local-e2e-2026-10-28.*` 보고서 파일은 보존하지 않았다.
|
||
- 문법·차이 확인: `node --check` 세 런타임 파일(두 저장소) 통과, 공유 파일 두 저장소 동일, `server.js`·`app.js`·`index.html` 의 Suite 블록 차이 불변.
|
||
|
||
## 독립 검토(읽기 전용) 차단 항목 12건 수정(2026-10-28) — Suite 쪽 기록
|
||
참조 저장소 쪽 상세(항목 1~6·8~10 의 결함·수정·RED 증거 표)는 `modules/webmaker/docs/verification/visual-editor-1.8.0-tdd.md`(upstream 과 동일). 여기에는 Suite 반영과 Suite 전용 항목(7·11)을 적는다.
|
||
|
||
- **공유 파일 반영**: `lib/visual-editor.js`·`lib/gitstore.js`·`public/visual-editor-core.js`·`visual-editor-ui.js`·`wm-ve-canvas.js`·`test/visual-editor.test.js`·`test/visual-editor-server.test.js`·새 `test/visual-editor-app.test.js` 를 `cp`(모두 upstream 과 byte-identical, `cmp`). Suite 블록이 있는 `server.js`·`public/app.js`·`lib/store.js` 는 참조 저장소의 수정 전/후 diff 를 `patch` 로 적용하고(server.js 의 소스/직접 편집 PUT 두 hunk 는 Suite rev 줄 때문에 손으로 반영) Suite 블록을 보존했다. upstream 대비 차이: `server.js` 115줄(107 → 115: 항목 7 의 rev 필수 2줄·잠금 안으로 옮긴 `revConflict` 줄·주석), `app.js` 4줄·`style.css` 5줄·`index.html` 2줄 불변, `lib/store.js` 4줄(rev 증가 + restore 때 rev 보존).
|
||
- **항목 7(Suite 전용) — 직접 편집 저장은 유한한 숫자 `rev` 필수**: `PUT /api/sites/<id>/visual/<페이지>` 는 `typeof rev === 'number' && Number.isFinite(rev)` 가 아니면 400 `{ revRequired: true }`(없음·문자열 "3"·null·"NaN"·""). 잠금 안에서 다시 읽은 사이트 기록의 rev 와 다르면(다른 파일 저장으로 오른 rev 포함) 409 `conflict{ yourRev, currentRev }`. 소스 편집기 PUT 의 옛 화면 호환 규칙(rev 없으면 통과)은 이 경로에 적용하지 않는다(fail-closed). 공유 서버 테스트는 GET 이 준 rev 를 모든 PUT 에 함께 보낸다(단독 Webmaker 는 0 을 주고 무시) — 처음 Suite 에서 `npm test` 를 돌리자 rev 없는 PUT 8건이 400 으로 떨어져 이를 드러냈고, 뒤처진 저장의 409 는 Suite 에서 hash(stale) 가 아니라 rev(conflict) 로 먼저 잡히므로 테스트가 둘 중 하나를 받는다(`expectStale`).
|
||
- **항목 6 의 Suite 부분 — 되돌리기 저장은 rev 를 올리지 않는다**: `lib/store.js save(site, { restore: true })` 는 rev 를 그대로 둔다. 실패한 직접 편집 저장(사이트 기록 폴더 읽기 전용 → store 실패)은 500 뒤 페이지·rev 가 저장 전 값 그대로이고 같은 rev·baseHash 로 재시도하면 200.
|
||
- **항목 11(Suite 전용) — 도입 소스 출처 fail-closed**: 새 `lib/provenance.mjs checkProvenance(manifest)`: 소스마다 40자리 `commit`·비어 있지 않은 파일 목록(from·to·origHash), `upstream` 이 있으면 version·branch·headCommit(= commit)·`workingTreeDirty === false` 를 요구하고, 모르는 형태·빈 목록은 불완전으로 거절. `upstream` 칸이 없는 0.1.x 기록(AriMail)은 깨끗한 고정 커밋만 받던 스크립트가 만든 것이라 legacy 로 받아들인다. `scripts/release.mjs build` 는 검사를 테스트보다 먼저 하고 깨끗하지 않으면 artifact 를 만들지 않고 종료 1(이유 출력), manifest.json 에 `checks.provenanceClean`·`provenanceProblems`·`imported[].upstream/legacy` 를 남긴다. `register --approve` 는 `checks.provenanceClean !== true`(false·옛 빌드의 없음)면 DB 를 열기 전에 종료 1. `hal/core.approveRelease` 는 `checks.provenanceClean === false` 면 `release_checks_failed`. 테스트용 `ARIWEB_IMPORTED_SOURCES` 로 manifest 경로를 바꿀 수 있다. 개발 중 기록(`workingTreeDirty: true`, --allow-dirty)으로는 build 가 멈췄다(의도). 참조 저장소 1.8.0 이 `4b8c4d0de0f69025001a9e0a28a58a94e1a1f088` 으로 커밋·검증된 뒤 `scripts/import-sources.mjs` 의 pin 을 그 커밋으로 올렸고, 그 pin 으로 `--allow-dirty` 없이 다시 만든 기록(`commit = headCommit = 4b8c4d0`, `workingTreeDirty:false`, 184 파일·수정 8)이면 build·approve 가 된다 — 이 작업 트리의 `IMPORTED_SOURCES` 파일은 아직 개발 중 기록이라 Neo 가 다시 만든다.
|
||
- **항목 12** — 이 문서·VERSION_HISTORY·upstream 문서의 수치를 최종값으로, 가져온 파일 수는 생성된 manifest(`IMPORTED_SOURCES.json`)에서 읽어 적었다(VERSION_HISTORY).
|
||
|
||
- **수정 뒤 브라우저 게이트 재실행 조사(2026-10-28)**: Neo 의 재실행이 `edit-text` 에서 "저장 상태 dirty" 시간 초과. 원인은 게이트 멱등성 결함(같은 저장 폴더에 이전 실행의 같은 제목이 이미 있어 변경 없음 = clean, 제품 규칙은 옳음)이며 로컬에서 결정적으로 재현·수정(실행마다 다른 제목 + 사전 단언). 같은 로컬 실행에서 실제 틈 하나(가운데 클릭 `auxclick` 이 sandbox 에서도 새 페이지를 만듦)를 찾아 `wm-ve-canvas.js` 에 auxclick 차단을 더했다(단위 RED→GREEN, upstream 과 byte-identical 로 반영). 로컬 Chrome 156 게이트 61/61(연속 2회), 이어서 **Neo 호스트의 실제 Chrome 재실행 61/61 통과**(2026-10-28). 상세: `modules/webmaker/docs/verification/visual-editor-1.8.0-tdd.md`.
|
||
|
||
## 독립 검토 2차(종결 검토) 차단 항목 3건 + 문서 수정(2026-10-28) — Suite 쪽 기록
|
||
참조 저장소 쪽 상세(항목 1·2 의 결함·수정·RED 증거)는 `modules/webmaker/docs/verification/visual-editor-1.8.0-tdd.md`(upstream 과 동일). 여기에는 Suite 반영과 Suite 전용 항목 3 을 적는다.
|
||
- **공유 파일 반영**: `lib/visual-editor.js`·`lib/gitstore.js`·`public/visual-editor-core.js`·`test/visual-editor.test.js`·`test/visual-editor-server.test.js`·`docs/verification/visual-editor-1.8.0-tdd.md`·`README.md` 를 `cp`(모두 upstream 과 byte-identical, `cmp`). `server.js` 는 참조 저장소의 수정 전/후 diff 를 `patch` 로 적용하되 직접 편집 PUT 의 트랜잭션 hunk 는 Suite rev 주석 때문에 손으로 반영(되돌리기의 `store.save(prev,{restore:true})` 줄에 `[Ariweb Suite 변경]` 주석 — rev 보존). upstream 대비 차이: `server.js` 115줄(변함없음), `app.js` 4줄·`style.css` 5줄·`index.html` 2줄·`lib/store.js` 4줄 불변. 도입 기록 재생성: 184 파일·수정 8·workingTreeDirty=true(의도).
|
||
- **항목 1 — SVG SMIL 저장형 XSS(공유 코드)**: Suite 는 같은 `lib/visual-editor.js` 를 쓰므로 그대로 적용된다 — 새 SMIL 요소(animate·set·animateMotion·animateTransform·animateColor·discard·mpath, 대소문자·`svg:` 접두어 포함)는 400, 원본에 있던 것은 그 수까지만(삭제 허용), 캔버스는 `wm-ve-x-` 비활성 이름. Suite 모듈 단위 테스트 38/38.
|
||
- **항목 2 — Git 저장 트랜잭션(공유 코드 + Suite rev)**: `gitstore.recordChangeStrict` 가 로컬 커밋 → 사이트 기록(Suite 는 여기서 rev +1 한 번) → 원격 `push --atomic`(main + 변경 브랜치) 순서로 끝내고, 실패하면 `reset --mixed`·브랜치 삭제/복원·페이지 원본·`store.save(prev,{restore:true})`(rev 보존) 뒤 검증한 결과로만 `rolledBack` 을 준다. 1차 수정 경로가 하던 두 번의 `store.save`(manualEdits 저장 + gitRecord 안 저장)가 한 번으로 줄어 rev 는 성공한 저장마다 정확히 1 오른다(기존 `test/webmaker-visual-editor.test.mjs` 의 `currentRev >= …` 검사 그대로 통과). 푸시 전에 저장되는 기록은 `pushed:false, remote:'pending'` 이고(원격 반영 미주장 — 훅이 푸시 순간에 복사한 기록으로 확인), 트랜잭션 밖 `reconcileSite`(잠금 안, 저장 뒤·기동 때)가 `ls-remote` 로 확인한 뒤에만 `verified` 로 바꾸며 그 저장은 `{restore:true}` 라 rev 를 올리지 않는다(재기동 조정 테스트가 `saved.rev === site.rev` 를 확인). 공유 서비스 테스트(bare 원격 + 훅 실패 주입, 첫 커밋 전 되감기, 기동 조정, 모호한 푸시 판정, 잠금 경쟁)가 Suite 모듈에서도 그대로 돈다(18/18).
|
||
- **항목 3(Suite 전용) — 릴리스 승인 fail-open**: `hal/core.approveRelease` 가 `checks.testsPassed === false`·`provenanceClean === false` 만 거절해 checks 가 없거나(옛 빌드·수동 등록) `testsPassed: null`(--skip-tests)·`dirty` 미기록인 릴리스를 승인했고, `registerRelease(approve:true)`·운영자 API `POST /ops/api/releases/:version/approve` 도 같은 함수를 지나 같은 구멍이 있었다. 새 `hal/core.releaseApprovalProblem(checks)` 한 규칙: `testsPassed === true`·`dirty === false`·`provenanceClean === true` 가 모두 있어야 하며(없음·null·문자열 "true"·1·"false" 모두 거절) 이유를 `release_checks_failed` 메시지로 돌려준다. `scripts/release.mjs register --approve` 도 같은 함수로 DB 를 열기 전에 종료 1(이유 출력). `--skip-tests` 빌드(testsPassed null)는 등록만 되고 승인은 불가. 기존 테스트 픽스처(hal-cli 2곳·hal 1곳 `registerRelease`, e2e `buildFakeRelease` manifest)는 깨끗한 checks 를 넣도록 고쳤다 — 현재 요구사항에 관용 정책이 없어 옛 정책은 남기지 않았다.
|
||
- **문서 수정** — 이 문서·VERSION_HISTORY·upstream 문서의 "재실행 필요/미실행"·"자동화하지 않음"·옛 수치(103/110·182 파일·HTML5 주 경로·release.mjs 변경 없음)를 현재 증거(Neo 호스트 실제 Chrome 61/61, 106/113/24, 184 파일·수정 8, 포인터 다리 주 경로 + HTML5 대체, release.mjs·hal/core 변경됨)로 고쳤다.
|
||
|
||
## 독립 검토 3차(최종 검토) 차단 항목 4건(2026-10-28) — Suite 쪽 기록
|
||
- **항목 1(Suite 전용) — 승인된 릴리스 불변**: `hal/core.registerRelease` 는 기존 행이 approved 면 같은 내용(main 커밋·빌드 시각·artifact·checks 를 키 순서 무관하게 비교, 메모는 준 경우)의 재등록만 멱등하게 돌려주고(approve:true 여도 승인자·시각 그대로), 다르면 `release_immutable` 로 거절하며 UPDATE 를 실행하지 않는다. 기존 행이 승인되지 않았으면 내용을 갱신하되 `status='registered', approved_by=NULL, approved_at=NULL` 로 두고, approve 는 `releaseApprovalProblem` 규칙을 지나야 한다. `hal/index.mjs register-release` 는 거부를 `릴리스 등록 거부 (release_immutable): …` 로 출력하고 종료 1(DB 불변). 운영자 API 의 승인 경로(`POST /ops/api/releases/:v/approve`)는 같은 규칙(앞 절 테스트). RED → GREEN: `test/release-provenance.test.mjs` +2 — core(8가지 변경 × approve true/false 모두 거절·readback 불변, 미승인 버전의 승인 미승계) · CLI(같은 manifest 재등록 멱등, 불량 checks·다른 artifact 재등록 종료 1 + DB readback 불변).
|
||
- **항목 2·3(공유 코드)**: `modules/webmaker/server.js` 에 라우터 수준 잠금·재적재(본문 메모 읽기 → 잠금 안 재적재 → 짧은 경로 전체 잠금 / 긴 경로 재적재)와 재진입 잠금, `changeLanguages` 순서, 직접 편집 PUT 의 `remoteState:'ambiguous'` 응답을 upstream 과 같은 블록으로 반영(Suite 블록 보존). `lib/gitstore.js` 는 byte-identical. Suite 의 직접 편집 rev 충돌 검사는 잠금 안에서 재적재된 최신 기록으로 이루어진다.
|
||
- **항목 4**: 이 문서·VERSION_HISTORY 의 합계(107/114 등)를 현재 증거로 바꿨다.
|
||
- **항목 5(공유 코드 + Suite 블록) — 배경 작업 저장 합치기**: `modules/webmaker/lib/store.js` 의 `load` 가 읽은 상태를 `__base`(열거 안 됨)로 기억하고 `save` 가 디스크 `saveId` 변화를 보고 이 객체가 바꾼 최상위 항목만 합친다. Suite 블록: 합칠 때 `rev` 는 디스크 값을 기준으로 올리고, `keepRev`(원격 반영 조정)는 합치되 rev 를 올리지 않는다(`restore` 는 스냅샷 그대로, rev 불변). upstream 대비 차이 5줄. 되돌리기 검증(`server.js`)은 `updatedAt`·`saveId` 만 제외하고 기록을 비교한다. 공유 서비스 테스트 +1(작업 중 PATCH 제목·PUT 등급 → 중단 → 유지)이 Suite 모듈에서도 그대로 돈다(19/19).
|
||
- **항목 6(공유 코드 + Suite 블록) — 같은 잎 동시 변경 fail-closed**: `store.js` 합치기를 잎 단위 3자 병합으로(일반 객체는 안으로, 배열·기본값은 잎), 같은 잎을 둘 다 다르게 바꾼 경우만 `SaveConflict`(code `site_save_conflict`, status 409, `fields`)로 거절·디스크 보존. `store.update(id, mutate, opts)`·`store.adopt(site, fresh)` 추가(Suite: `update` 도 `keepRev` 를 받아 조정 저장은 rev 불변). `server.js`: 배경 작업 저장 전부 `saveJob(site, reapply)`(충돌이면 최신 기록 위에 작업 항목만 재적용 + adopt), `gitRecord` 충돌 때 버전만 추가·옛 실패분 재시도는 잠금 안 조정으로, `/git/retry`·`reconcileSite` 는 `applyPushState` 를 `store.update` 로 적용. 그 밖의 화면 경로의 충돌은 409 로 드러난다(조용한 손실 없음). RED → GREEN: 공유 서비스 테스트 +2(저장소 규칙 6가지 / 실제 서버 — git 래퍼로 commit 2초 지연 → 작업 종료의 버전 기록 창에 다시 푸시 → 다시 푸시의 verifiedAt 그대로·작업의 새 버전도 기록); 중간 RED 에서 충돌이 다시 푸시 경로에 409 로 드러난 것을 확인한 뒤 그 경로를 update 로 옮겼다. Suite 모듈 21/21.
|
||
|
||
## 독립 검토 4차 차단 항목 4건(2026-10-28) — Suite 쪽 기록
|
||
- **항목 1(Suite 전용) — artifact 설치 불변·fail-closed**: `hal/core.installArtifacts(d, {version, mainCommit, builtAt, checks, artifacts:{mod:{file, sha256, size, src}}})` — ① 원본 바이트 sha256·크기를 선언값과 대조(불일치 → `artifact_hash_mismatch`, 아무것도 쓰지 않음) ② 승인된 버전이면 `releaseImmutableProblem`(메타데이터) + 설치된 파일 해시 모두 같을 때만 멱등(복사 없음), 아니면 `release_immutable`(기존 파일·DB 불변) ③ 새·미승인 설치는 `.<file>.tmp-…` 에 쓰고 해시를 다시 확인한 뒤 rename. `hal/index.mjs register-release`·`scripts/release.mjs register` 는 설치 → `registerRelease` 순서로 바뀌었고 거부는 종료 1(release.mjs 는 DB 를 닫고 종료). RED → GREEN: `test/release-provenance.test.mjs` +2 — HAL CLI(A 승인 → B 유효 해시·B 바이트에 A 해시 거짓 선언 × approve 유무 → 종료 1, 대상 바이트/해시/DB 가 A, 임시 파일 없음, A 재등록 멱등, 새 버전 해시 불일치 미설치) · release.mjs(같은 4 경우 + 멱등).
|
||
- **항목 2·3·4(공유 코드)**: `modules/webmaker/server.js` — 읽기 요청 부작용 제거(언어 저장은 잠금 안 재적재에서만), 폐기 규약(`destroySite(siteId, by, {ownerId, confirm})`: 잠금 안 재검사·표식 claim → 바깥 정리 → 같은 표식 확인 뒤 삭제; 라우터 재적재·`claimJob` 의 폐기 중 409; 실패는 `store.update` 로 deleteFailed) — 사용자 경로는 Suite 의 포털 영향 확인 블록(`ariweb.impactFromPortal`·`acknowledgeImpact`·`unlink`)을 그대로 두고 마지막 호출만 바꿨다(upstream 대비 118줄 차이 — 기존 Suite 블록 + 폐기 전 검사 2줄). `lib/visual-editor.js` 스캐너의 외래 콘텐츠 깊이는 byte-identical.
|
||
|
||
- **잔여 위험 해소 — 폐기 때 포털 연결 보상(Suite 블록 + 공유 훅)**: 포털 연결 등록부(`lib/ariweb.js` 의 `ariweb-links.json`, 로컬 파일)의 해제(`unlink`)는 되돌릴 수 있고(`link` 로 되살림, `linkedAt` 보존 인자 추가), 포털 자체에는 영향 조회(`impactFromPortal`, 읽기)만 한다. 공유 `destroySite` 의 최종 잠금 단계를 "임시 이름으로 옮김(rename) → `beforeIrreversible` → 기록 unlink(되돌릴 수 없는 한 단계) → 최선의 뒷정리" 로 바꾸고, Suite 사용자 경로는 `beforeIrreversible` 에서 `ariweb.unlink` + readback(남아 있으면 502), `compensate` 에서 `ariweb.link(원래 연결)` + readback 을 한다. 순서상 정리 실패·연결 해제 실패에는 되돌릴 수 없는 변경이 하나도 없고, 기록 삭제 실패에는 되살리기가 있으며 되살리지 못하면 `recoveryRequired`(응답·`deleteFailed`·감사 `site.destroy.failed`)로 운영자에게 보인다. 뒷정리 실패는 성공 응답의 `warnings` + 감사 `site.destroy.cleanup`(남은 `.deleting-…` 폴더). RED → GREEN: `modules/webmaker/test/ariweb.test.js` +3(가짜 ssh·가짜 배포 대상·가짜 포털) — ① 정리 실패(SSH_FAIL): 연결·파일·기록 그대로 + deleteFailed, 정리가 되면 폐기 성공·연결 없음 ② 정리 성공 뒤 연결 등록부 쓰기 실패(DATA_ROOT 읽기 전용): ssh 호출 있음, 연결·파일·기록 그대로(임시 폴더 없음), 쓸 수 있게 되면 성공 ③ 되살리기 readback + 뒷정리 실패(지울 수 없는 하위 폴더): 200 + warnings, 연결·기록·원래 폴더 없음, 남은 폴더 1개 확인 뒤 정리.
|
||
|
||
### RED — Suite(4차 검토)
|
||
- `node test/release-provenance.test.mjs`(수정 전 CLI): `✘ 통과 9 · 실패 2` — `B 유효 해시 + approve: 대상 바이트 A 그대로: 기대 "artifact-A-bytes" 실제 "artifact-B-bytes-different"`, release.mjs `기대 "rel-A" 실제 "rel-B-other"`(승인된 artifact 바이트가 덮임).
|
||
- 공유 테스트(참조 저장소, 수정 전): 단위 38/39(`거절되어야 함: …<svg><title><script>alert(1)</script></title></svg>`), 서비스 21/23(`읽기 요청은 기록 파일을 바꾸지 않는다`, 폐기 중 저장이 "폐기" 로 거절되지 않음).
|
||
- Suite 모듈 `test/ariweb.test.js`(수정 전 순서 — unlink 먼저): `ℹ tests 10 · pass 7 · fail 3` — `포털 연결이 풀리지 않았다(연결 해제는 정리 성공 뒤에만): null`, `정리는 먼저 수행됐다`(연결 해제 실패가 정리 전에 남), `연결이 되살아났다(readback): null`.
|
||
|
||
### GREEN — Suite(4차 검토 뒤, 현재 증거)
|
||
```
|
||
node test/release-provenance.test.mjs ✔ 통과 11 · 실패 0
|
||
modules/webmaker: CLAUDE_BIN=<가짜 exit 1> npm test ℹ tests 125 · pass 125 · fail 0 (단위 39 · 서비스 23 · 화면 전환 1 · 원본 59 + ariweb 폐기 보상 3)
|
||
CLAUDE_BIN=<가짜 exit 1> node test/run.mjs ✔ 테스트 파일 24개 중 실패 0개 (생성된 local-e2e-2026-10-28.{md,json} 은 삭제)
|
||
```
|
||
참조 저장소: `npm test` 115/115(단위 39 + 서비스 23 + 화면 전환 1 + 원본 52). 실제 Chrome 게이트(Neo 호스트) 61/61 그대로(게이트가 보는 화면 동작 불변).
|
||
|
||
### RED — Suite(최종 검토)
|
||
- `node test/release-provenance.test.mjs`(수정 전 `hal/core.mjs`): `✘ 통과 7 · 실패 2` — `Error: 승인자·시각도 그대로: 기대 {"status":"approved","by":"op",…} 실제 {"status":"approved","by":"op2",…}`(승인된 행이 재등록으로 바뀜), CLI 재등록 뒤 `approved_by` 가 "test" → "test2".
|
||
- 공유 서비스 테스트(수정 전 코드, 참조 저장소): `ℹ tests 18 · pass 15 · fail 3` — 잠금 경쟁(`설정 저장이 직접 편집 버전을 덮어쓰지 않는다: ["1:create"]`), 모호한 푸시(`원격이 받았으면 성공: {…"rolledBack":true,"stage":"push"}`), 준비 단계 실패(`stage`·`rollback` 없음, 페이지 미복원). 항목 5(수정 전 store.js): `ℹ tests 19 · pass 18 · fail 1` — `작업의 종료 저장이 화면의 제목 변경을 덮어쓰지 않는다: {"title":"VE site","status":"idle"}`.
|
||
|
||
### GREEN — Suite(최종 검토 뒤)
|
||
```
|
||
node test/release-provenance.test.mjs ✔ 통과 9 · 실패 0
|
||
modules/webmaker: CLAUDE_BIN=<가짜 exit 1> npm test ℹ tests 119 · pass 119 · fail 0 (단위 38 · 서비스 21 · 화면 전환 1 · 원본 59)
|
||
CLAUDE_BIN=<가짜 exit 1> node test/run.mjs ✔ 테스트 파일 24개 중 실패 0개 (생성된 local-e2e-2026-10-28.{md,json} 은 삭제)
|
||
```
|
||
참조 저장소(그때): `npm test` 112/112(단위 38 + 서비스 21 + 화면 전환 1 + 원본 52). 현재 증거는 위 "4차 검토 뒤" 절.
|
||
|
||
### RED — Suite(종결 검토)
|
||
- `node test/release-provenance.test.mjs`(수정 전 `hal/core.mjs`·`scripts/release.mjs`): `✘ 통과 4 · 실패 3` — `Error: checks 없음 (approveRelease)`(승인됨), `Error: 7.7.0: 거절 200 {"ok":true,"release":{…}}`(운영자 API 가 checks 없는 릴리스를 승인), `Error: tests-null: HAL 릴리스 등록: 0.2.0 (approved) … 기대 1 실제 0`(`register --approve` 가 testsPassed null 을 승인).
|
||
- 공유 서비스 테스트(수정 전 코드, 참조 저장소에서 실행): `ℹ tests 14 · pass 11 · fail 3` — Git 실패 되돌리기 `rolledBack:false`(옛 boolean 은 상태를 검증하지 않음), 원격 트랜잭션(옛 코드는 pre-receive 거부를 200 + `rec.error` 로 삼킴). 단위: SMIL `거절되어야 함: …<animate attributeName="href" values="javascript:alert(1)"…`.
|
||
|
||
### GREEN — Suite(종결 검토 뒤)
|
||
```
|
||
node test/release-provenance.test.mjs ✔ 통과 7 · 실패 0 (checks 13가지 불량 × approveRelease/registerRelease 거절, 운영자 API 거절/승인, register --approve 4가지 거절 + 깨끗한 manifest 승인)
|
||
modules/webmaker: CLAUDE_BIN=<가짜 exit 1> npm test ℹ tests 114 · pass 114 · fail 0 (단위 38 · 서비스 16 · 화면 전환 1 · 원본 59)
|
||
CLAUDE_BIN=<가짜 exit 1> node test/run.mjs ✔ 테스트 파일 24개 중 실패 0개 (생성된 local-e2e-2026-10-28.{md,json} 은 삭제)
|
||
```
|
||
참조 저장소(그때): `npm test` 107/107(단위 38 + 서비스 16 + 화면 전환 1 + 원본 52). 현재 증거는 위 "최종 검토 뒤" 절.
|
||
|
||
### RED — Suite(1차 검토)
|
||
- `node --no-warnings test/release-provenance.test.mjs`(구현 전): `✘ 통과 0 · 실패 4`(`lib/provenance.mjs` 없음 → 네 테스트 모두 실패).
|
||
- `node --no-warnings test/webmaker-visual-editor.test.mjs` 를 **rev 필수 줄과 `{ restore: true }` 를 뺀 임시 서버 복사본** 으로 실행: `✘ 통과 4 · 실패 1` — `Error: rev 없음: {"path":"index.html",…,"rev":11,…}`(rev 없는 저장이 200 으로 파일을 바꿈). 실행 뒤 복사본은 수정본으로 되돌렸다(`cmp` 확인).
|
||
- Suite `modules/webmaker` `npm test`(공유 테스트가 rev 를 보내기 전): `ℹ tests 110 · pass 102 · fail 8` — 모두 `{"error":"저장본 개정 번호(rev)가 없거나 올바르지 않습니다…","revRequired":true}`(항목 7 이 동작함을 보임) → 공유 테스트에 rev 추가 → `pass 108 · fail 2`(409 가 stale 대신 conflict) → `expectStale` → 110/110.
|
||
|
||
### GREEN — Suite(1차 검토 뒤)
|
||
```
|
||
node --no-warnings test/release-provenance.test.mjs ✔ 통과 4 · 실패 0
|
||
node --no-warnings test/webmaker-visual-editor.test.mjs ✔ 통과 5 · 실패 0 (rev 없음/문자열/null/NaN/빈 → 400, 교차 파일 rev → 409, store 실패 → 500·rev 보존·재시도 200)
|
||
modules/webmaker: CLAUDE_BIN=<가짜 exit 1> npm test ℹ tests 110 · pass 110 · fail 0
|
||
CLAUDE_BIN=<가짜 exit 1> node test/run.mjs ✔ 테스트 파일 24개 중 실패 0개 (release-provenance 포함; 생성된 local-e2e-2026-10-28.{md,json} 은 삭제)
|
||
```
|
||
참조 저장소: `npm test` 103/103(단위 37 + 서비스 13 + 화면 전환 1 + 원본 52).
|
||
|
||
## 브라우저 검증 상태 · 자동화하지 않은 것
|
||
- 실제 브라우저 검증(Neo 의 게이트, 실제 Chrome)은 다섯 차례 수행되었다: 배치 결함 → 선택 전 도구 줄 노출 → 끌어다 놓기 실패 → 살균 진단으로 "HTML5 끌기가 sandbox iframe 에 도달하지 않음" 확정(3·4차는 선택·글 편집·실행 취소/다시 실행·키보드 이동 통과) → 포인터 다리 뒤 네이티브 포인터 순서 게이트 **37/37**. 독립 검토 수정 뒤 재실행은 게이트 멱등성 결함을 고친 뒤 **Neo 호스트에서 61/61 통과**(연결 규약·이동 활성화 6종·위조 창 메시지 단언 포함) — 캔버스 안 실제 포인터 조작(글 입력·실행 취소/다시 실행·이동·팔레트 끌어다 놓기·저장·저장 파일·390px)은 이 게이트로 자동 검증했다. Suite 쪽 화면은 독립 Webmaker 와 같은 `style.css`·`visual-editor-ui.js`·`wm-ve-canvas.js` 를 쓰므로 같은 수정이 적용된다(style.css 포털 배너 5줄만 추가). 종결 검토 3건은 브라우저 동작을 바꾸지 않아 추가 재실행은 필요 없다.
|
||
- 자동화하지 않은 것: 포털 SSO 로 들어간 실제 화면 확인, 운영 서버 배포, Chrome 외 브라우저. 사람 확인 목록은 Webmaker `Dev_Order_v1.8.0.md` 끝.
|