• ARCA 0.1.25

    ARCA 0.1.25 변경 사항

    이전 버전: 0.1.24. stop()·fresh 재시작마다 instance가 남던 메모리 누적과 native exit trap을 runtime에서 고치고, runtime toolchain·upstream과 sandbox CRuby를 올린 runtime release다. native runtime(mkxp-z core JS·WASM)이 0.1.21~0.1.24와 달라진다. 공개 API·타입·기본값·오류 code와 contracts schema는 바뀌지 않았다. runtime, Builder base image, SDK 의존성과 version 문자열이 바뀌어 runtime ZIP·contracts tgz·Builder archive·npm tgz bytes와 release pin을 모두 새로 고정한다.

    포함 변경 요약

    runtime·SDK: stop()·fresh 재시작 instance 해제

    • 0.1.24 이하에서는 stop()과 restart({ presentation: "fresh" })를 할 때마다 내린 WASM instance(Module, WebAssembly.Memory, AudioContext, page hook)가 page unload까지 남았다. RTP를 쓰는 VX Ace 게임은 회당 약 450~500 MB씩 늘었다.
    • runtime patch retroarch-instance-release가 재시작 transaction에 UNLOAD(operation 4)를 더한다. 일시정지한 core를 frame 경계에서 CMD_EVENT_CORE_DEINIT으로 내리고, 한 번만 실행되는 release hook(Module.rgss23Release, rgss23ReleaseProtocol 1)이 main loop, browser hook, Emscripten event listener, AudioWorklet port·node, AudioContext를 정리한다. release 뒤에 끝나는 getBattery()·AudioWorklet module load 같은 비동기 continuation도 native code를 부르지 않는다.
    • SDK는 이 protocol을 알리는 runtime에서 PAUSE → UNLOAD → release → exit() 순서로 종료한다. Nostalgist가 남긴 전역 override를 되돌리고 handle이 쥔 내린 runtime 참조를 놓는다. UNLOAD는 restart transport에서 한 번만 쓴다. protocol이 없는 runtime에서는 0.1.24 경로를 그대로 쓴다.
    • 자동 근거(Chrome 155, heap VX Ace + pinned RTP, runtime:smoke:instance-release): fresh 재시작 3회와 stop()+재launch 3회 모두 회차마다 완료된 exit 하나, trap 없음. 열린 AudioContext·살아 있는 Wasm memory는 fresh 중 1개, stop() 뒤 0개. OS private은 fresh 1055→1060 MB, stop() 478→489 MB였다(0.1.24 SDK와 0.1.21 runtime은 같은 page에서 fresh 1560→2581 MB, stop() 2562→4091 MB로 FAIL).

    runtime: native exit trap 수정

    • 0.1.24의 알려진 문제: 강제 exit()가 static mkxp_retro::fs 소멸자의 PHYSFS_deinit을 audio 정리보다 먼저 실행했다. 그래서 PHYSFS_mountHandle archive(RTP, 암호화 Game.rgssad·.rgss2a·.rgss3a)에 열린 항목이 있으면 closeFileHandleList에서 use-after-free trap이 났다(Chrome "table index is out of bounds", Firefox "indirect call to null").
    • 이제 UNLOAD를 거친 종료는 audio가 열린 항목을 닫은 뒤 PhysFS를 정리하므로 이 경로를 밟지 않는다. 방어용 core patch mkxp-z-physfs-deinit-close-list는 각 항목을 list에서 뗀 뒤 destroy한다. 0.1.24 SDK의 abort 격리는 실패 fallback으로만 남는다.
    • runtime:smoke:instance-release -- --scenario exit-archive: BGM 재생 중 stop()이 RTP 없는 암호화 Game.rgss2a와 RTP archive 모두에서 ExitStatus로 끝난다(수정 전에는 둘 다 trap).

    runtime: sandbox CRuby 4.0.9

    • RGSS interpreter를 CRuby 3.3.12에서 4.0.9(3e93b23a)로 올렸다(ARCA patch mkxp-z-cruby-4.0: syntax-transform/4.0, 4.0용 user threads·WASI sockets patch, Ruby 3.3의 32-bit shape 용량 유지).
    • parse.y를 기본 parser로 configure했다. Prism parser·compiler 진입점(compile_prism, compile_file_prism, load_iseq hook, --parser=prism, RUBYOPT)은 모두 NotImplementedError로 막았다. 그래서 legacy 문법 변환(VX syntaxTransform: 2)은 그대로다.
    • 바뀌지 않는 4.0 차이: 예외 backtrace의 'Class#method' 표기, Hash#inspect 형식, Encoding inspect, 알 수 없는 pack 지시자의 오류 등.

    runtime: pointer 회귀 수정

    • RetroArch 갱신으로 RETRO_DEVICE_POINTER가 화면 왼쪽 위만 보고하던 회귀를 게시 전에 고쳤다(0.1.24 게시본에는 없던 회귀). retroarch-browser-input은 unpack한 pointer_x·pointer_y를 canvas CSS 크기 × DPR로 정규화한다. offscreen 여부는 양자화 전에 정수 CSS 좌표를 CSS box와 비교해 정한다.

    library·toolchain 갱신

    • RetroArch master a159f9ce(upstream에 들어간 Firefox patch 2개를 제거하고 나머지 10개를 rebase), wrapper 0b0a9527, zlib·libsndfile·PhysicsFS·stb 등 core dependency는 branch HEAD 기준으로 올렸다.
    • emscripten/emsdk 6.0.13, FreeType VER-2-15-0(CVE-2026-50811·CVE-2026-95512 수정 포함), runtime image Node 26.12.1, 기본 runtime build image tag rgss23-mkxp-build:emscripten-6.0.13-v2.
    • Builder base node:26.12.1-alpine, player SDK image node:26.12.1-trixie-slim(corepack 0.36.0을 hash로 고정), nostalgist 0.23.0, @types/node 26.6.5.
    • 제공 VX browser fixture 글꼴은 NeoDunggeunmo v1.601이고, RPGVX RTP 추출 image는 다시 게시된 debian:13.7-slim digest다. runtime ZIP에는 들어가지 않는다.

    재배포 정책 재승인

    • runtimes/mkxp-z/redistribution-policy.json을 2026-10-23에 runtime lock dc792e75…로 재승인했다(status: approved, redistribution: restricted 유지).
    • 고지 전체를 runtime ZIP LICENSES/로 내보낸다: CRuby 4.0.9 COPYING·COPYING.ja·LEGAL(ext/json vendor 코드 포함), RetroArch libarchive 유래 RAR reader, frontend·core 내장 허용 license 고지(frontendSourceNotices·coreSourceNotices), frontend link closure의 header 고지 311개(frontend-notice-inventory.json, 정규화 기준 23개 고지), 이식 코드 upstream 고지(frontendDerivedNotices: libvpx·libwebp·Code Review Stack Exchange attribution), Emscripten sysroot, nostalgist가 inline하는 ini·path-browserify.

    release 도구

    • npm facade packer가 100 byte를 넘는 경로를 ustar prefix(155 byte)에 나눠 쓴다. 0.1.25 고지 export가 package/runtime/LICENSES/core/mkxp-z/subprojects/freetype/include/freetype/internal/fthash.h.NOTICE.txt(104 byte) 같은 경로를 더해 release:build가 npm 단계에서 멈췄기 때문이다. 100 byte 이하 경로의 header bytes는 그대로다.

    문서

    • 남은 일과 알려진 한계·위험을 로드맵과 프로젝트 상태(알려진 한계와 위험)에 기록했다. LINKS 가이드에 0.1.25 절을 더했다.
    • release bytes에 들어가는 packages/contracts/README.md, packages/packager-rgss/README.md, tools/release/npm/README.md는 0.1.24 이후 바뀌지 않았다.

    자동 근거(2026-10-22~23, 같은 PC, 로컬 runtime clean export = 0.1.25 runtime)

    • 최종 runtime(main a5e3af7, JS 97f21a30…, WASM 18848c93…, provenance stamp가 lock dc792e75…·patch hash와 일치), Chrome 155·Firefox 153·Edge 155, 자동 PASS:
      • runtime:smoke:dpr 기본 행렬(DPR 1·2·3 × 두 viewport), Chrome·Firefox.
      • 지연 resize-restart --hold-reads-during-relaunch.
      • shutdown, restart, instance-release(memory·exit-archive·release-async), audio-controls, context-lifecycle, isolated-player, presentation-quality.
      • syntax-transform(Prism 음성 대조 포함), ruby-untaint, host-storage-metrics(VX Ace Chrome heap, lazy+preboot).
      • rgss23-lazy-smoke.js game·faults·reject-duplicate·rollback.
    • browser smoke 자동 PASS: edge:vx-ace(title), chrome:vx-ace:title, firefox:vx-ace:title, edge:vx-ace:gamepad, edge:vx-ace:touch-pad, edge:vx(title), firefox:vx:title, edge:vx:action-event, Chrome VX resize-input.
    • edge:vx-ace:save-load·movement-cancel·audio-map004·action-event와 edge:vx:save-load는 동작·화소 검사를 모두 통과했고 바뀐 screenshot도 없다. 다만 gold 검토 browser가 Edge 154여서 runner 결과는 NOT REVIEWED다(사람 gold 검토 NOT RUN).
    • release 전 built release artifact로 browser smoke를 다시 실행하지 않았다(위 근거와 같은 runtime bytes·SDK 코드).

    알려진 문제와 위험

    • 전체화면: explicitSwapControl 아래에서 Emscripten 전체화면 helper가 getCanvasElementSize로 초기화되지 않은 OffscreenCanvas record를 읽는다. 그래서 전체화면 크기가 잘못될 수 있다. 전체화면은 후순위 범위이며 검증하지 않았다.
    • CRuby 4.0 sandbox는 Ractor를 명시적으로 막지 않는다(thread model none, 이식 전과 같음). Ruby Thread·Fiber 실행 범위 전체의 검증은 NOT RUN이다.
    • Prism은 모든 진입점에서 꺼져 있어 parse.y가 유일한 parser다.
    • shape 용량은 0x8000개로 제한된다. 다 쓰면 too-complex fallback으로 넘어가며, 이 경로는 native build에서만 확인했고 실제 게임에서는 NOT RUN이다.
    • rvp8.c·rwebp.c가 이식한 libvpx·libwebp의 원래 upstream version은 확정할 수 없어 현재 upstream 원문을 고지로 쓴다. codepen 예제를 참고한 particle 효과는 고지 대상이 아닌 것으로 기록했다.
    • 소수 DPR pointer 경계는 산술·정적 검사로만 확인했다. touch·pointer-lock·실기 기기는 NOT RUN이다.
    • 반복 메모리 측정은 Chrome heap 경로만 했다. Firefox·지연 콘텐츠 경로는 NOT RUN이다.

    host 적용 순서

    LINKS 적용 가이드 0.1.25 절을 따른다. 요약:

    1. @rgss23/arca 전체를 0.1.25 exact pin으로 올린다(runtime·SDK·Builder·contracts 함께). 0.1.23 이하에서 올리면 0.1.23·0.1.24 절도 함께 적용한다.
    2. runtime bytes가 바뀌었으므로 0.1.25 Builder로 generation을 다시 만든다. Builder digest·config digest를 새 pin과 대조한다.
    3. 실제 게임의 title·save/load·플레이와 mouse click 위치를 다시 확인한다. Ruby 오류 문자열을 분류에 쓰면 CRuby 4.0 backtrace 표기로 다시 대조한다.
    4. fresh 재시작과 stop() 뒤 재launch를 반복해 frame 메모리가 늘지 않는지 확인한 뒤 0.1.24 절의 instance 보존 경고(횟수 제한)를 해제한다.
    5. runtime을 재배포·서빙할 때는 runtime ZIP의 LICENSES/ 전체를 함께 둔다.

    NOT RUN

    • 사용자 acceptance(제공 VX/VX Ace의 acceptance 기준은 이전 CRuby 3.3 runtime의 2026-08-05 결과 그대로), LINKS opaque frame 적용, 실기 모바일·DPR.
    • 최종 runtime에서 runtime:smoke:bitmap-profile, restart VX Ace 변형, presentation-quality Firefox·opaque-origin 변형, dpr 기타 변형(opaque-origin·hidden-start·focus·기타 DPR/browser), browser scenario 일부(Edge VX play·cancel-menu·movement-left·audio-title, Edge VX Ace play, Firefox VX Ace play·movement-cancel·action-event, Firefox VX first-dialogue·background-resume·resize-input). font-selection은 운영자 입력이 없어 설계상 BLOCKED다.
    • puppeteer-core 25.16.0 갱신(pnpm minimumReleaseAge 때문에 2026-10-23T11:54Z까지 막힘), Linux 재현.

    배포 검증

    • tag: 0.1.25(annotated, tag object 7366087fff652c4d47aa6dd39eb18df84657bc42)
    • release source: 8c70f90351(Release: store npm package paths over 100 bytes in the ustar prefix, 버전 커밋 Release: prepare ARCA 0.1.25 5e3fbfe 바로 다음)
    • manifest SHA-256: 363342e99543a4b54055864ea6581377185f28700f8ce47324830c0431609214
    • Builder digest(pin): sha256:11d1d483fa333fae3e85aea535b219db433baf0325c1ea53df3304364cc0f85e · Builder config digest(pin builderConfigDigest): sha256:31e0ce5f42938563769f39579ae04c9da7b8c332d36109dd21bb3b3329f0614a
    • artifact SHA-256: builder 90dd41fcbb4787a61ea0074df11d690295833d0542adf2963ddf9b8ee6843529 · contracts b7fcebc2d9b5958ac5fede905b0c0d0d54b1f7199847efe5ffdfa6214cec1c14 · runtime 4d2657226772c973b8a73f6e67299c844aaa1d250c30f36d7fca3271e3c272d3
    • npm: rgss23-arca-0.1.25.tgz SHA-256 24ae784104f11ad9138590308ad127f69b0d6481e23e0f59fd0d7a86a24878ad, integrity sha512-6EptvRsVnBs4u1qzTjudAdqAI7rebi7K0t3N9Ur336Wl7uAc+0qytfkw8tzMTrThU62DxcliKyiYIkoMV4ei3w==(로컬 계산, registry 게시 NOT RUN)
    • runtime ZIP의 rgss23-core.js(97f21a30… 272,594 B)·rgss23-core.wasm(18848c93… 48,400,171 B)은 main a5e3af7 canonical runtime export와 byte 동일하고, export provenance가 runtime lock dc792e75…과 일치한다. redistribution-policy.json runtimeLockSha256은 dependencies.lock.json과 일치한다. named volume rgss23-mkxp-build의 frontend JS·WASM·core archive가 같은 hash라 runtime을 다시 build하지 않았다.
    • 게이트(source 8c70f90, 2026-10-23): install --frozen-lockfile, 단위 테스트 646개, 타입 검사, release 회귀 51개, browser 도구 회귀 137개, verify:locks, native 준비 recipe 검증, git diff --check PASS(work/release-0.1.25/logs/gates.log)
    • release:build(Builder gate·source-free consumer gate)·release:verify·release:npm:test·release:reproduce: PASS(2026-10-23, source 8c70f90, manifest sourceCommit = HEAD; release:reproduce는 clean detached worktree에서 end-to-end로 pin·세 artifact·npm이 동일, work/release-0.1.25/logs/pipeline.log). 첫 release:build(5e3fbfe)는 npm ustar 경로 한도로 실패해 위 수정 뒤 다시 실행했다. Windows·같은 Docker host만, Linux 재현 NOT RUN.

    asset 7개

    파일 크기(B) SHA-256
    release-manifest.json 2,155 363342e99543a4b54055864ea6581377185f28700f8ce47324830c0431609214
    release-manifest.sha256 88 68f95c0e836efc8d106ec7410628fe5abc7c56efff0e372dacb7bfd5ee12aa14
    release-pin.json 537 38ae03c8e8818ef3d7722bab98d6c27efd30883770094a03d25e4503f7cb8b34
    rgss23-arca-0.1.25.tgz 103,622,084 24ae784104f11ad9138590308ad127f69b0d6481e23e0f59fd0d7a86a24878ad
    rgss23-builder-0.1.25.oci.tar 63,196,672 90dd41fcbb4787a61ea0074df11d690295833d0542adf2963ddf9b8ee6843529
    rgss23-contracts-0.1.25.tgz 38,268 b7fcebc2d9b5958ac5fede905b0c0d0d54b1f7199847efe5ffdfa6214cec1c14
    rgss23-runtime-0.1.25.zip 50,112,038 4d2657226772c973b8a73f6e67299c844aaa1d250c30f36d7fca3271e3c272d3

    로컬 경로: dist/release/0.1.25/, dist/npm/0.1.25/rgss23-arca-0.1.25.tgz. artifacts.json·npm-package.json은 업로드하지 않는다.

    0.1.24 대비 전체 변경

    Downloads 9