{"scope":"Prompt-to-artifact checklist for continuing docs/GOAL.md toward a playable web port.","objectiveRestated":"Continue the Hwanse Chwihojeon web-port goal in docs/GOAL.md until resource extraction, viewer/rendering, runtime loading, movement/input, normal progression, event/dialogue/battle systems, and completion gates are backed by concrete artifacts and verifiers.","sourceObjective":"docs/GOAL.md","sourceCompletionAudit":"out/completion_audit.json","achieved":false,"completionAuditAchieved":false,"routePromotionStatus":"blocked","routePromotionAllowed":false,"runtimeTraceCanCaptureNow":false,"runtimeTraceFailedGateIds":["qemu-i386-wine-trace-capture","gdbstub-breakpoint-watchpoint-stability","route-watchpoint-trace-proof"],"runtimeTraceBlockerCount":6,"runtimeTraceProbeCount":18,"savedataSlotScanStatus":"unknown","savedataSlotFoundCount":null,"savedataSlotCurrentSelectorCandidateCount":null,"audioTitleProvenance":{"source":"out/audio_archive_manifest.json + out/audio_title_provenance.json","archiveManifestStatus":"verified","archiveManifestConclusion":"PCMDATA.WLK is finalized as 54 verified mono PCM entries and MIDDATA.MLK as 20 verified SMF format-0 MIDI tracks.  No WLK id 25/45/53 special probe or audition rewrite is part of the restoration contract.","wlkVerifiedCount":54,"wlkArchiveCount":54,"mlkVerifiedCount":20,"mlkArchiveCount":20,"audioExeIdNumberingUnified":true,"wlkProbeRequired":false,"conclusion":"PCMDATA.WLK has no embedded effect-name storage, so WLK viewer labels stay as EXE id entries instead of inferred effect names; WLK browser cue names from web/game.html are provenance only, not titles. MIDDATA.MLK stores titles only where the contained MIDI has a track-name meta event.","wlkArchive":"PCMDATA.WLK","wlkTitleStorage":"absent","wlkEmbeddedTitleCount":0,"wlkBrowserCueLabelCount":0,"wlkExeIdLabelCount":54,"wlkFallbackLabelCount":54,"mlkArchive":"MIDDATA.MLK","mlkTitleStorage":"standard MIDI track-name meta events","mlkEmbeddedTitleCount":16,"mlkFallbackLabelCount":4,"mlkNoEmbeddedTitleIds":["02","06","11","17"]},"checks":[{"id":"inventory","requirement":"File inventory analysis for the original archives, executable, save folders, tools, and docs.","status":"complete","artifacts":[{"path":"docs/INVENTORY.md","exists":true},{"path":"docs/GOAL.md","exists":true}],"evidence":["Inventory document exists and GOAL.md records the original workspace structure."],"gaps":[],"verifiedBy":["tools/verify_web_assets.py"],"coverage":"Covers the explicit GOAL.md work-order item 1."},{"id":"binary-formats","requirement":"Binary/container format reconstruction for FLD/WLK/MLK/CNS and known runtime data surfaces.","status":"partial","artifacts":[{"path":"docs/FORMATS.md","exists":true},{"path":"docs/CNS_FORMAT.md","exists":true},{"path":"out/event_vm_semantics_gap.html","exists":true}],"evidence":["Container extraction and CNS decoding are documented.","Event VM handler/storage evidence is separated from execution proof."],"gaps":["Full event/object VM instruction length, operand layout, route dispatch, and story mutation proof remain open."],"verifiedBy":["tools/verify_web_assets.py"],"coverage":"Covers work-order item 2, but keeps unproven VM/runtime semantics outside completion."},{"id":"resource-extraction","requirement":"Resource extractors for original archives and browser-ready assets.","status":"complete","artifacts":[{"path":"tools/extract_wlk.py","exists":true},{"path":"tools/extract_mlk.py","exists":true},{"path":"tools/decode_cns.py","exists":true},{"path":"tools/build_web_assets.py","exists":true},{"path":"web/cns_rect_review.html","exists":true},{"path":"web/audio_review.html","exists":true},{"path":"web/monster_review.html","exists":true},{"path":"out/audio_archive_manifest.html","exists":true},{"path":"out/audio_title_provenance.html","exists":true}],"evidence":["174 field maps are present in the generated map index.","The verifier checks image, WAV sound, map, save selector, and DirectX import outputs.","The web runtime wires extracted WLK effects into movement, dialogue, runtime menu, map-transition, and candidate battle feedback, including runtime-menu-sound markers for menu open/move/close.","out/home_landing_browser_smoke.* records the detailed review surface links: assetSplitRows=None, mapGallery=None, monster=None, audio=None, cnsRect=None.","out/runtime_music_cue_browser_smoke.* records WebKit runtime MIDDATA.MLK cue markers plus browser MIDI synth playback: title=scene=title map=map1_01a cue=title src=../extract_mlk/00.mid index=0 archive=MIDDATA.MLK log=1 synth=scheduled notes=194 synthSrc=../extract_mlk/00.mid browserMusicCue=True browserMidiSynth=True originalMidiPlayback=False; map=scene=map map=map1_02b cue=map src=../extract_mlk/01.mid index=1 archive=MIDDATA.MLK log=1 synth=scheduled notes=330 synthSrc=../extract_mlk/01.mid browserMusicCue=True browserMidiSynth=True originalMidiPlayback=False; battle=scene=battle map=map1_02b cue=battle src=../extract_mlk/08.mid index=8 archive=MIDDATA.MLK log=2 synth=scheduled notes=380 synthSrc=../extract_mlk/08.mid browserMusicCue=True browserMidiSynth=True originalMidiPlayback=False; title/map/battle fetch the extracted .mid files and schedule WebAudio oscillator notes, while still avoiding claims about native DirectMusic playback or original music selection logic.","out/audio_archive_manifest.* verifies WLK 54/54 and MLK 20/20; active references use EXE internal ids directly.","out/audio_title_provenance.* records WLK title storage as absent (embedded=0, EXE id entries=54) and MLK titles as MIDI track-name metadata (embedded=16, EXE id entries=4)."],"gaps":[],"verifiedBy":["tools/verify_web_assets.py"],"coverage":"Covers work-order items 3 and 4 when verifier evidence is present."},{"id":"title-and-map-rendering","requirement":"First screen or at least one map rendered in the browser with reviewed map rendering.","status":"partial","artifacts":[{"path":"extract_fld/title.cns","exists":true},{"path":"out/title_start_browser_summary.json","exists":true},{"path":"out/home_landing_browser_smoke.json","exists":true},{"path":"out/maps_runtime_chunks/map1_02b.js","exists":true},{"path":"out/maps_runtime_chunks/map1_01a.js","exists":true},{"path":"web/index.html","exists":true},{"path":"web/map_review.html","exists":true}],"evidence":["0 maps have direct accepted render reviews.","0 tileset families can provide default render reviews.","Runtime render selection is covered for 0/174 maps by 0 direct accepted rows and 0 family-default rows, with 174 fallback rows.","Direct accepted render reviews cover every field map; old family-default/proxy render audits are no longer part of the active review surface.","The map review/gallery browser checks catch map preview scale/layout regressions."],"gaps":["Expected all field maps to be covered by direct accepted rows with no family-default or fallback render rows."],"verifiedBy":["tools/verify_web_assets.py","tools/verify_title_start_browser.py"],"coverage":"Covers work-order item 7."},{"id":"runtime-loading","requirement":"Runtime loading of maps, text, battle backgrounds, dialogue blocks, and save samples.","status":"complete","artifacts":[{"path":"out/maps_runtime.js","exists":true},{"path":"out/text_tables.js","exists":true},{"path":"out/battle_backgrounds.js","exists":true},{"path":"out/event_dialogue_blocks_runtime.js","exists":true},{"path":"tools/verify_runtime_http.py","exists":true}],"evidence":["The runtime uses out/maps_runtime.js plus per-map chunks instead of eagerly loading out/maps.js.","tools/verify_runtime_http.py smoke-checks /web/index.html, startup PNG/JS assets, key map chunks, and the save-selector trial URL through the local HTTP server.","out/runtime_music_cue_browser_smoke.* records WebKit runtime MIDDATA.MLK cue markers plus browser MIDI synth playback: title=scene=title map=map1_01a cue=title src=../extract_mlk/00.mid index=0 archive=MIDDATA.MLK log=1 synth=scheduled notes=194 synthSrc=../extract_mlk/00.mid browserMusicCue=True browserMidiSynth=True originalMidiPlayback=False; map=scene=map map=map1_02b cue=map src=../extract_mlk/01.mid index=1 archive=MIDDATA.MLK log=1 synth=scheduled notes=330 synthSrc=../extract_mlk/01.mid browserMusicCue=True browserMidiSynth=True originalMidiPlayback=False; battle=scene=battle map=map1_02b cue=battle src=../extract_mlk/08.mid index=8 archive=MIDDATA.MLK log=2 synth=scheduled notes=380 synthSrc=../extract_mlk/08.mid browserMusicCue=True browserMidiSynth=True originalMidiPlayback=False; title/map/battle fetch the extracted .mid files and schedule WebAudio oscillator notes, while still avoiding claims about native DirectMusic playback or original music selection logic."],"gaps":[],"verifiedBy":["tools/verify_web_assets.py","tools/verify_runtime_http.py --base http://127.0.0.1:8013"],"coverage":"Covers the browser runtime resource-loading surface and the fixed port 8013 smoke."},{"id":"movement-input","requirement":"Input, walking, collision, projection, mobile controls, party trail, and movement feedback.","status":"partial","artifacts":[{"path":"out/runtime_movement.html","exists":true},{"path":"out/movement_candidate_review_summary.json","exists":true},{"path":"tools/verify_candidate_movement_browser.py","exists":true},{"path":"tools/verify_mobile_runtime_controls.py","exists":true}],"evidence":["The runtime has tile-step walking, camera follow, keyboard input, wall-slide movement, a touch virtual joystick, and mobile transition-review controls.","Sprite frame extraction and web verification cover the current character frame data.","out/runtime_timing.* identifies the original GetTickCount main-loop tick: frameIntervalMs=48, frameCap=3, nominalFps=20.833, updateTarget=0x00411476, webDefaultTileStepMs=48, exactActorConsumerStillInferred=False.","out/runtime_movement.* identifies the original per-frame movement/input consumer and tile projection: updateFunction=0x00411476, perFrameCalls=0x00422d74,0x00435c04,0x00432ff0, actionPacker=0x0042f6d2, continuousInputMask=0x0059e310, pressedEdgeMask=0x0059e312, cameraTileGlobals=0x004576dc/0x004576de, objectTileFields=+0xe8/+0xea, tileSizePixels=16, tileGridAndCameraConsumerGrounded=True, originalDrawPrepProjectionGrounded=True, partyActorDrawProjectionGrounded=True, partyActorAnimationStateValuesGrounded=True, originalSpriteFrameSelectorGrounded=True, originalFrameSelectorToSourceRectGrounded=True, originalSpriteFrameScriptTimerGrounded=True, actorAnimationStateDirectFrameMappingEliminated=True, actorAnimationStateToFrameSelectorPartyDescriptorsGrounded=True, objectDescriptorTableClassified=True, objectDescriptorTableRows=12, objectDescriptorPartyStateTableRows=[0, 1, 2], nonPartyDescriptorAnimationStateTablesAbsent=True, nonPartyDescriptorFieldMapRefsAbsent=True, descriptorActivationOrderPathGrounded=True, descriptorActivationOrderCountRuntimeVa=0x004576e8, descriptorActivationOrderBytesVa=0x004576e9, descriptorActivationSlotBaseVa=0x00457750, descriptorActivationLoadRebuildGrounded=True, descriptorActivationOpcode20SelfMutationEliminated=True, descriptorActivationCurrentSelectorActiveOrderStillUnproven=True, actorAnimationStateToFrameSelectorStillUnresolved=True, actorAnimationStateToFrameSelectorRemainingGap=the 12-row descriptor table is classified: rows 0..2 ground +0x68 -> +0x64 state-table frame scripts for Ataho/Rinshan/Smashu, while rows 3..11 are battle/effect/object resource descriptors without +0x68 state-table selectors or field-map refs; save-load active-order materialization is grounded, but current selector 2:0 active order and scripted behavior for non-player descriptors remain inferred, spriteFrameSelector=+0x28 via 0x00416ce2 surfaceTable=0x0055abd8, spriteFrameSelectorCounts=0/0, spriteFrameScriptTimer=0x00432ff0->0x00402321, spriteFrameOpcode=0x21@0x004405bc->0x004044bd, spriteFrameTimerField=+0x62, spriteFrameScriptPointer=+0x64, partyFrameDescriptorTable=0x00442d95, objectDescriptorTableClassification=the 12-row descriptor table has +0x68 animation state-table selectors only in rows 0..2 for the party walking descriptors; rows 3..11 are battle/effect/object resource descriptors with no +0x68 state-table selector and no field-map CNS refs, descriptorActivationOrder=save load calls 0x00432323 after count/order and slot blocks are read and before selector pointer selection; later add/remove active descriptor mutations are general-table opcode 0x62/0x63 paths, while the current selector 2:0 active order remains unproven because public samples do not cover it, descriptorActivationLoadRebuild=0x0042349c->0x00432323, partyFramePointerOpcode=0x20@0x004405b8->0x0040448a, partyFrameStateTableOpcode=0x18@0x00440598->0x0040408f, partyFrameStateCommand=18 a0 64 68 <state-table-va>, partyFrameResources=cara_at1.cns,cara_rs1.cns,cara_sm1.cns, partyFrameSurfaces=cara_at1.cns:0x11,cara_rs1.cns:0x14,cara_sm1.cns:0x17, webDefaultTileStepMs=48, webFrameQuantizedTileStepGrounded=True, webSourceRectSelectorMappingGrounded=True, webSourceRectSelectorCadenceGrounded=True, webSourceRectPhaseStillInferred=False, webWalkFrames=5, webIdleFrame=0, webFirstWalkFrame=1, webFrameSize=48x64, webWallSlideFallbackPresent=True, webDefaultCollisionMode=originalLayer1Flags, webCollisionFallbackStillInferred=True, webDefaultCollisionModeUsesOriginalLayer1Flags=True, exactActorWalkInterpolationStillInferred=True, webPositionInterpolationStillVisualApproximation=True, originalActorPositionModel=tile-field-snap-plus-fixed-point-draw-projection, originalActorSubTileInterpolationEvidenceFound=False, originalActorSubTileInterpolationEvidenceScope=identified actor controller tile mutations +0xe8/+0xea and draw projection writes +0x1c/+0x20, browserInterpolationClassification=browser-visual-overlay-not-original-proof, browserInterpolationOverlayDetected=True, browserInterpolationFixedPointPreserved=True, browserInterpolationOffsetField=interpolationOffset, browserInterpolationOverlayNotPromotingOriginal=True, exactActorSpriteFramePhaseStillInferred=False, actorController=0x0043022d, collisionHelper=0x004319f8, actorInputMaskRefs=24, actorTileFieldWrites=2/2, actorTrailRing=0x00574540/0x00574550/0x00574552/0x00574554, actorTrailSlots=7, actorTrailInitialCursors=0,4,1, partyActorProjection=party actor draw coordinates are recomputed from tile/trail entries; +0x68 stores direction/animation state; party frame scripts provide the selector phase outside the actor controller, collisionFlagTable=0x0058d7d0/0x0058d7ce, collisionFlagReads=4, directionBlockBits={'down': '0x02', 'up': '0x01', 'left': '0x04', 'right': '0x08'}, directionCaseCount=4, objectOverlapResponse=actor-overlap collision response after tile-flag checks, objectOverlapClearsDirectionLatch=True, mapCollisionLoader=0x0042449c, cnsLayer0Grid=0x00595af0, cnsLayer1CollisionGrid=0x0058d7d0, inputDrivenTileMutationGrounded=True, originalCollisionFlagTestsGrounded=True, originalCollisionFlagTableNotMappedToCnsTiles=False, originalCollisionFlagGridMappedToCnsLayer1=True, webOriginalLayer1FlagCollisionModePresent=False, webActorOverlapCollisionModePresent=True, webActorOverlapCollisionDefaultEnabled=False, webActorOverlapCollisionModeQuery=actorCollision=1 enables review-only party blocking, webActorOverlapCollisionStillOptIn=True, webPartyTrailHistoryPresent=True, webPartyTrailRingSlotCount=7, webPartyTrailRingMatchesOriginalSlotCount=True, webPartyTrailActorSlotCount=3, webPartyTrailInitialCursors=0,4,1, webPartyTrailCursorModelPresent=True, webPartyTrailInitialCursorsMatchOriginal=True, webPartyTrailRecordsAcceptedMoveTarget=True, webPartyTrailCallTimingMatchesOriginalTileMutation=True, webPartyTrailStillApproximate=False, webDefaultCollisionModeStillFallback=False.","out/non_party_descriptor_asset_links.* links non-party descriptor rows 3..11 to decoded/runtime assets without behavior promotion: promotionStatus=asset-linked-behavior-gap-remains, descriptorTable=0x00442d95, nonPartyRows=9, linkedCns=10, decoded/listed/resourceRefs=None/10/0, directBattlePartySprites=0, battleSpriteCandidates=5, fieldObjectOverlayCandidates=1, sceneLinkedZAssets=7, descriptorZAssetsWithSceneAnchors=1/5, descriptorZAssetsWithoutSceneAnchor=['zsa_saru', 'zsl_yoi', 'zsa_mi', 'zsa_kika'], fieldObjectOverlaySceneAnchorCount=1, detailReviewOnly=5, rowsWithoutFrameInitializer=[3], noAnimationStateTables=True, noFieldMapRefs=True, fieldObjectOverlaySceneAnchorLimitedToZsaIwa=True, descriptorZAssetsWithoutSceneAnchorsStayBattleOnly=True, currentSelectorActiveOrderProven=False, originalNonPartyDescriptorScriptBehaviorProven=False, originalFieldObjectRuntimeBound=False, originalBattleEntryProven=False.","tools/verify_mobile_runtime_controls.py guards the mobile joystick, A button, touch-action suppression, non-control document touch starts, direction mapping, and pointer-event wiring in source and HTTP responses.","The web movement loop uses tile-target stepping and wall-slide fallback: vertical input falls back to left/right, horizontal input falls back to up/down, and wallSlidePreference flips when the preferred side is blocked so held input can snake through narrow two-tile passages."],"gaps":["Original frame timing, input masks, 16px tile/camera projection, draw-prep/projected-coordinate consumption, input-driven actor tile mutation, party actor draw projection from tile/trail entries, +0x28 sprite frame selector/source-rect/offset consumer, opcode 0x21 script/timer frame selector path, party actor +0x68-to-+0x64 frame-script descriptor tables for cara_at1/cara_rs1/cara_sm1, descriptor table rows 0..2 vs 3..11 classification, save-load active descriptor materialization through 0x00432323, web source-rect selector mapping, exact 8-command selector cadence, browser 48ms sub-tile interpolation, original collision flag tests, directional latch fallback cases, actor-overlap collision response, CNS layer1 collision flag grid, default web original layer1 collision mode, original companion tile/direction trail ring with initial cursors 0,4,1 and post-mutation leader-tile write timing, default web party actor-overlap collision, and 7-slot web party-trail cursor history are now identified. The original actor position model is tile-field snap plus fixed-point draw projection, while browser interpolation is classified as a visual overlay and not original proof; original actor interpolation timing remains unproven, and current selector 2:0 active order plus scripted behavior for non-player descriptors are still inferred."],"verifiedBy":["tools/verify_web_assets.py","tools/verify_candidate_movement_browser.py"],"coverage":"Covers work-order item 8 for browser movement, but non-player/original selector behavior remains partial."},{"id":"normal-progression","requirement":"Confirmed normal-route progression, map transitions, strict hotspots, and route promotion.","status":"partial","artifacts":[{"path":"out/route_promotion_gate.html","exists":true},{"path":"out/route_blocker_evidence_matrix.html","exists":true},{"path":"out/runtime_trace_execution_probe.json","exists":true},{"path":"out/strict_source_hotspot_external_review_packet.html","exists":true},{"path":"out/savedata_slot_scan.html","exists":false}],"evidence":["Confirmed transition points: 2.","Confirmed map edges: 1.","Reachable from title start: 2 maps (map1_02b, map1_01a).","out/title_start_browser_summary.* records a compact WebKit title-screen start: titleChecksum=4101021888, mapChecksum=913600737, started=map1_02b@11,12, submenuCancel=shop-buy->main->False, mobileMenuCancel=shop-buy->main->False, mobileBCancel=shop-buy->main->False, returnTitle=title menu=['start', 'playStart', 'startEncounters', 'loadSavedat', 'scanSavedat', 'sampleSavedat', 'routeAssist', 'routeGoal', 'home'] visible=['도구 선택 -', '전체 지도', '전투 탐색 켜기', '전투 선택', '전투', '임시 저장', '임시 불러오기 -', '제목'] pointerHit=26, titleSavedatRequest=title-menu parser=True, titleSavedatScan=found0/loadedFalse search=?savedatScan=1, titlePublicSavedat=flack3r-savedat2->map2_02d@5,14 selector=1:0 sampleSavedatRouteMissing=3 sampleSavedatRouteExternalInput=real-selector-2-0-save sampleSavedatMarkerMissing=3, savedTitlePublicSavedat=1:0@5,14 quickRouteMissing=3, continueTitlePublicSavedat=1:0->map2_02d@5,14 continueRouteMissing=3 label=이어하기 map2_02d 5,14 세이브 1:0, queryPlay=map2_02d->map1_02b@11,12 source=query-play-start fieldEncounter=True/0 autoSaved=True->map1_02b, playStart=map2_02d->map1_02b@11,12 fieldEncounter=True/0 autoSaved=True->map1_02b autoRoute=map2_02d, continuePlayStart=map2_02d->map1_02b@11,12 label=이어하기 map1_02b 11,12 전투 후보 map2_02d fieldEncounter=True/0, playStartNext=map2_02d->map1_01a@11,11 autoSaved=True fieldEncounter=True/0, playStartFocus=map2_02d->map1_01a@18,0 autoSaved=True fieldEncounter=True/0, playStartTarget=map2_02d->map2_02d@47,47 autoSaved=True routeTargetBlockReasons=5 routeTargetTrialOnly=trial-only: no map1_01a->map2_02d transition-review row +4 routeTargetFeedback=route-progress-feedback:후보 완료 1/1 map2_02d · trial-only: no map1_01a->map2_02d transition-review row +4 fieldEncounter=True/0, continuePlayStartTarget=map2_02d->map2_02d@47,47 label=이어하기 map2_02d 47,47 전투 후보 map2_02d fieldEncounter=True/0, playStartTargetEncounter=map2_02d@48,47 targetEncounterInput=True/{'x': 47, 'y': 47}->{'x': 48, 'y': 47} fieldEncounterVictoryCount=1 continuePlayStartTargetEncounter=map2_02d@48,47 label=이어하기 map2_02d 48,47 전투 후보 map2_02d 완료 map2_02d, playStartConfirmedInput=ArrowUp/map1_02b@11,12->map1_01a@11,11 autoSource=map-transition transitionFeedback=map-transition-feedback:맵 이동 map1_02b -> map1_01a confirmedTransitionSound=transition:../extract_wlk/07.wav:True transitionFeedbackRender=True, playStartCandidateInput=ArrowUp/map1_01a@18,0->map2_02d@47,47 autoSource=route-candidate transitionFeedback=route-candidate-feedback:후보 이동 map1_01a -> map2_02d · trial-only: no map1_01a->map2_02d transition-review row +4 candidateTransitionSound=transition:../extract_wlk/07.wav:True transitionFeedbackRender=True, continuePlayStartInput=map2_02d->map2_02d@47,47 label=이어하기 map2_02d 47,47 전투 후보 map2_02d, continue=map1_02b@11,12 label=이어하기 map1_02b 11,12, itemUseObjective=후보 도구 완료 map1_02b/완료 알림 확인 itemFeedback=inventory-item-use:herb:약초 HP +12 itemFeedbackRender=True itemFeedbackSound=item itemFeedbackSoundSrc=../extract_wlk/11.wav itemFeedbackSoundPlayed=True itemNoticeFeedback=inventory-item-completion-notice-feedback:도구 사용 완료 1/1 itemNoticeFeedbackRender=True itemNoticeSound=menuConfirm:../extract_wlk/04.wav:True itemFailFeedback=inventory-item-failed-feedback:herb:약초 대상 없음 itemFailFeedbackRender=True statusFailFeedback=inventory-item-failed-feedback:item_2:해독초 회복 대상 없음 statusFailFeedbackRender=True itemSoundItemCount=1 action=item-use-completion-notice active=item-use-complete:map1_02b notice=item-use-complete:map1_02b completion=약초:24->36, itemUseContinue=hp36/36/herb0/progress1 quickSaveFeedback=title-start-smoke-quick-save-feedback:임시 저장 map1_02b 11,12 quickSaveFeedbackRender=True quickSaveFeedbackSound=menuConfirm:../extract_wlk/04.wav:True quickLoadFeedback=title-continue-feedback:임시 불러오기 map1_02b 11,12 quickLoadFeedbackRender=True quickLoadFeedbackSound=menuConfirm:../extract_wlk/04.wav:True objective=후보 도구 완료 map1_02b/완료 알림 확인 action=item-use-completion-notice active=item-use-complete:map1_02b notice=item-use-complete:map1_02b completion=약초:1->0, encounterStart=map1_02b@11,12 fieldEncounter=True/0 modeAutoSource=field-encounter-enable modeFeedback=field-encounter-enable-feedback:전투 탐색 켬 0/6 modeAutoFeedback=field-encounter-enable-feedback:전투 탐색 켬 0/6 modeFeedbackRender=True stepAutoSource=field-encounter-step stepInput=True/{'x': 11, 'y': 12}->{'x': 12, 'y': 12} encounterFeedback=field-encounter-step-feedback:전투 탐색 1/6 encounterFeedbackRender=True encounterFeedbackSound=step:../extract_wlk/00.wav:True savedFieldEncounter=True/1, continueEncounter=map1_02b@12,12 label=이어하기 map1_02b 12,12 전투 fieldEncounter=True/1, disableAutoSource=field-encounter-disable disableFeedback=field-encounter-disable-feedback:전투 탐색 끔 1/6 disableAutoFeedback=field-encounter-disable-feedback:전투 탐색 끔 1/6 disableFeedbackRender=True continueDisabledEncounter=map1_02b@12,12 label=이어하기 map1_02b 12,12 fieldEncounter=False/0, routeAssistMenu=map1_02b@11,12, titleRouteGoal=map2_18d->map1_02b@11,12, continueRouteGoal=map2_18d->map1_02b@11,12 label=이어하기 map1_02b 11,12 후보 map2_18d, routeAssist=map1_02b@11,12 goal=map2_02d.","out/title_menu_sound_browser_smoke.* records WebKit title menu WLK sound markers: keyboard=scene=map map=map1_02b titleMenuSound=title-menu-sound:move,move,select titleMenuSoundSrcs=../extract_wlk/03.wav,../extract_wlk/03.wav,../extract_wlk/04.wav selectedKeys=startEncounters,playStart,playStart inputSources=keyboard,keyboard,keyboard menuMoveSoundCount=2 menuConfirmSoundCount=1 browserTitleMenuSound=True; pointer=scene=map map=map1_02b titleMenuSound=title-menu-sound:select titleMenuSoundSrcs=../extract_wlk/04.wav selectedKeys=playStart inputSources=canvas-pointer menuMoveSoundCount=None menuConfirmSoundCount=1 browserTitleMenuSound=True; mobile=scene=map map=map1_02b titleMenuSound=title-menu-sound:select titleMenuSoundSrcs=../extract_wlk/04.wav selectedKeys=playStart inputSources=mobile-action menuMoveSoundCount=None menuConfirmSoundCount=1 browserTitleMenuSound=True; ArrowDown/ArrowUp produce menuMove, while keyboard Enter, canvas pointer, and mobile A on the default playStart row produce menuConfirm before map start.","Map-exit trial mode reaches 13 maps using geometry-only exits, kept separate from confirmed gameplay.","The WebKit mobile browser smoke verifies the confirmed A-button transition from map1_02b to map1_01a and confirms the URL follows the current map/startTile after the transition.","The web runtime has a routeAssist trial mode that combines selector-only links and map-exit candidates, and the WebKit mobile browser smoke verifies a touch joystick transition from map1_01a to the map2_02d candidate route with routeAssist preserved in the URL, then activates the next candidate frontier and continues from map2_02d to map2_18d."],"gaps":["1 confirmed-route blocker(s) remain.","The current route stops at map1_01a because no strict source coordinate has been found for map1_01a -> map2_02d.","routeAssist is a trial/debug route continuation, not confirmed normal-route evidence.","The current VM has Wine tooling installed, but qemu-i386/WineDbg control probes fail even for notepad, so the Hwanse2.exe runtime trace cannot be captured here yet. A non-debugger /proc memory snapshot can read Hwanse2.exe globals, but the latest idle sample is not route-path proof. Embedded execution-probe binfmt view: registered=True, enabled=True; summary view enabled=True. The qemu-i386 binfmt state is consistent with the child/debugger trace availability check. Direct qemu-i386 can run both the Wine loader smoke (wine-9.0 (Ubuntu 9.0~repack-4build3)) and CLI child exec smoke (Microsoft Windows 10.0.19043); the remaining blocker is the WineDbg/runtime trace path. A bounded X/input-buffer probe confirms Wine DirectInput can see held synthetic keydowns, but ordinary X key events did not move the idle selected pointer. Direct cached-key-buffer pokes moved the selected pointer only to non-route selector 50:0, not current selector 2:0. This remains input-path diagnostic evidence, not route proof. A bounded direct key-buffer sequence probe covered 28 sequence(s) after 14.0s startup wait and reached route selector 2:0=False; it remains non-promoting diagnostic evidence. A supplemental input-path prelude key-buffer probe covered 3 sequence(s), observed selectors=8:0,50:0, and reached route selector 2:0=False; it is diagnostic-only. A non-debugger selected-pointer poll covered 1550 sample(s) across 8 direct key-buffer sequence(s), observed selectors=8:0, and reached route selector 2:0=False; it still does not replace a watchpoint trace. A supplemental input-path selected-pointer poll covered 491 sample(s) across 3 direct key-buffer sequence(s), observed selectors=8:0,50:0, and reached route selector 2:0=False; it is also diagnostic-only. A supplemental long selected-pointer poll covered 1928 sample(s) across 5 custom key-buffer sequence(s), observed selectors=8:0,50:0, and reached route selector 2:0=False; it is also diagnostic-only. A supplemental late-start selected-pointer poll waited 45.0s, covered 1122 sample(s) across 1 key-buffer sequence(s), observed selectors=8:0,50:0, and reached route selector 2:0=False; it is also diagnostic-only. A supplemental route watch-value poll covered 1123 sample(s) across 1 key-buffer sequence(s), observed selectors=8:0,50:0, watchValues=opcode24CurrentObjectIndex=0x00x1123; opcode24Mode1Source=0x00x1123; opcode24RuntimeFlag=0x00x783,0x01x340, and reached route selector 2:0=False; it is also diagnostic-only. A supplemental standalone synthetic selector 2:0 poll staged a save-shaped file under SaveData\\savedat1.dat, ran the input-path prelude with case aliases, and covered 374 sample(s) across 2 load-menu candidate sequence(s), inputQuality=pid=2/2 base=2/2 keyWrites=2/2 pressedSeq=2/2 pressedEvents=18 offsets=28,208, stagedSelectors=2:0, observed selectors=8:0, observedStaged=False, and reached route selector 2:0=False; this keeps standalone synthetic selector 2:0 non-promoting. A supplemental patched public-base selector 2:0 diagnostic poll staged a public save with only selector/position bytes patched under SaveData\\savedat1.dat, ran the input-path prelude with case aliases, and covered 375 sample(s) across 2 load-menu candidate sequence(s), inputQuality=pid=2/2 base=2/2 keyWrites=2/2 pressedSeq=2/2 pressedEvents=138 offsets=28,208, stagedSelectors=2:0, observed selectors=2:0,10:0, observedStaged=True, and reached route selector 2:0=True; this proves the load path can select a constructed 2:0 save-shaped diagnostic, but it is not a captured gameplay save. A supplemental SaveData file-I/O trace probe (winedebug) placed a public save under SaveData/savedat1.dat but inputTraceUsable=False (pidRuns=0/1, keyWriteRuns=0/1); absent savedat lines remain backend-feasibility evidence, not load-menu proof. A supplemental SaveData file-I/O trace probe (strace) placed a public save under SaveData/savedat1.dat but inputTraceUsable=False (pidRuns=0/1, keyWriteRuns=0/1); absent savedat lines remain backend-feasibility evidence, not load-menu proof. A supplemental attached SaveData file-I/O trace probe (strace-attach) placed a public save under SaveData/savedat1.dat and had inputTraceUsable=True (pidRuns=1/1, keyWriteRuns=1/1, matchedFileIoLines=0, savedat1Access=False); this rules out savedat file access only for the bounded attached load-menu candidate window, not the full load-menu path. A broader attached SaveData file-I/O trace probe (strace-attach) placed a public save under SaveData/savedat1.dat and had inputTraceUsable=True (pidRuns=8/8, keyWriteRuns=8/8, matchedFileIoLines=0, savedat1Access=False); this rules out savedat file access across the covered bounded load-menu candidate windows, not the full load-menu path. A case-alias attached SaveData file-I/O trace probe (strace-attach) placed a public save under SaveData/savedat1.dat with temporary resource archive aliases and had inputTraceUsable=True (pidRuns=8/8, keyWriteRuns=8/8, matchedFileIoLines=0, savedat1Access=False); aliases reduce archive lookup noise but still do not prove full load-menu reachability."],"verifiedBy":["tools/verify_web_assets.py","tools/verify_runtime_http.py --base http://127.0.0.1:8013"],"coverage":"Covers the next-goal route transition requirement only as trial/debug routeAssist after map1_01a."},{"id":"event-dialogue-battle","requirement":"Dialogue, event, save-point, object, party, item, shop, equipment, and battle gameplay systems.","status":"partial","artifacts":[{"path":"out/event_vm_semantics_gap.html","exists":true},{"path":"web/battle_analysis.html","exists":true},{"path":"web/battle_formula_calculator.html","exists":true},{"path":"out/dialogue_candidate_review_summary.json","exists":true},{"path":"web/battle_simulator.html","exists":true}],"evidence":["Scene events, save selectors, branch state candidates, and object assets are extracted into review reports.","CP949 Korean text candidates are indexed with executable VA and pointer references for the next text-handler pass.","Decompressed CNS payload text scan found no plain CP949 dialogue candidates after excluding image pixels and tilemap bodies.","Scene event condition/payload windows have no direct Korean text or text-table pointers in the current extraction.","Known Korean text routine callers use instruction-aligned direct-call scanning and classify as system UI, item/menu, or battle-action contexts; no story-dialogue caller class is identified yet.","Event/object VM handler scan finds opcode 0x0b calling text routine candidate 0x0041b579 without local static Korean text refs; other matched handlers reference item/action text tables.","Event text source flow identifies opcode 0x0d as a context+0x28 producer and finds story-like CP949 text near many 0x0d/0x0b streams, but current route-linked 0x0c hits are pointer-byte overlaps.","Event dialogue block indexing groups opcode 0x0d source commands with nearby CP949 text, 0x0b render commands, and 0x35 literal text payload markers, but this is still storage evidence rather than event-driven playback."],"gaps":["Dialogue playback is still candidate/debug playback rather than confirmed event VM execution.","Save-point candidate review is still candidate/debug playback rather than original inn/statue/recovery NPC save-point event execution.","Linked event object previews are still candidate/prototype surfaces rather than original object/NPC descriptor execution.","out/event_vm_semantics_gap.* keeps instruction-length decoding, operand layout, route-linked event VM execution, full browser event VM implementation, and story dialogue route proof outside the completed surface; browser opcode trace replay is partial and non-promoting.","out/event_vm_semantics_gap.* promotion gate keeps browserReplayPromotesOriginalEventVm=False until route-linked dispatch, full instruction/operand decode, scene dialogue binding, original story flag mutation, full browser VM execution, and event-driven battle entry are proven.","out/event_battle_gap_audit.* keeps route-linked event VM execution, event-driven battle entry, original enemy data/formulas/rewards, equipment offsets, status flag offsets, and story flag mutation outside the completed surface.","EXE battle resource descriptors are grounded resource-load rows, not enemy rows, formations, encounter dispatch, reward tables, combat formulas, or battle-entry event execution.","Battle enemy sprite candidate visuals are EXE-proximity visual evidence, not enemy row/stat/formation/reward/formula proof."],"verifiedBy":["tools/verify_web_assets.py","tools/verify_candidate_dialogue_progress_browser.py","tools/verify_candidate_battle_browser.py"],"coverage":"Covers work-order item 8 for browser prototype gameplay; original event VM and battle systems remain partial."},{"id":"completion-gate","requirement":"Playable web-version completion gate and final objective status.","status":"partial","artifacts":[{"path":"out/completion_audit.html","exists":true},{"path":"out/completion_candidate_review_summary.json","exists":true},{"path":"out/completion_candidate_review_summary.html","exists":true}],"evidence":["The browser prototype is useful for map walking, resource review, transition review, and route investigation.","out/completion_candidate_review_summary.* now verifies a browser-local playable route completion gate: after routeAssist continuation to map2_14j, walking into the prototype field encounter, winning, restoring from title, and opening the completion notice, the runtime records routeCompleteProgress=route-complete, routeCompleteFlagKeys=story:route-complete:map2_14j:route-complete:map2_14j, opens playableGate=prototype-playable-route-gate with playableGateStatus=open/playableGateOpen=True/playableGateLabel=웹 후보 루트 완료 map2_14j, shows routeGateFeedback=route-completion-feedback:후보 완료 map2_14j with routeGateFeedbackRender=True plus routeGateSound=victory/routeGateSoundSrc=../extract_wlk/12.wav/routeGateSoundPlayed=True, auto-saves that gate, then restores it through title `이어하기 map2_14j ... 완료 map2_14j` with savedRouteCompleteCount=1, savedRouteCompleteFlagKeys=story:route-complete:map2_14j:route-complete:map2_14j, completionRouteCompleteCount=1, completionItem=후보 완료 map2_14j, and runtimeRouteCompletionGateTitleRestore=True. The same restored menu command reopens the completion notice with duplicateProgress=True, savedRouteCompleteCount=1, and runtimeRestoredRouteCompletionMenuNotice=True. A completed save now also exposes the title menu item titleItem=후보 완료 map2_14j; selecting it loads the save and opens route-complete:map2_14j with title-route-completion-gate, payloadRouteCompleteCount=1, payloadRouteCompleteFlagKeys=story:route-complete:map2_14j:route-complete:map2_14j, playableGateOpen=True, duplicateProgress=True, and runtimeTitleRouteCompletionGate=True. The same completed save exposes titleItem=후보 클리어 저장 map2_14j before the first clear; selecting it loads the save, opens route-clear:map2_14j with title-route-clear-gate, records routeClearProgress=route-clear, auto-saves routeClearAutoSource=route-clear with payloadRouteClearCount=1 and payloadRouteClearFlagKeys=story:route-clear:map2_14j:route-clear:map2_14j,story:route-complete:map2_14j:route-complete:map2_14j, shows routeGateFeedback=route-clear-feedback:후보 클리어 map2_14j with routeGateFeedbackRender=True plus routeGateSound=victory/routeGateSoundSrc=../extract_wlk/12.wav/routeGateSoundPlayed=True, updates the post-save title payload to titlePayloadLabel=후보 클리어 map2_14j/titlePayloadClearCount=1, shows the title label suffix 클리어 map2_14j, and leaves runtimeTitleRouteClearGate=True. The clear summary now reports 진행 기록 9 and 결과 소지금 80 / 보상 약초 2, and the progress review also reports 클리어 목표 map2_14j, 클리어 진행 기록 9, and 클리어 결과 소지금 80 / 보상 약초 2. The smoke then restores that clear save through title `이어하기 ... 클리어 map2_14j` with runtimeRouteClearGateTitleRestore=True, savedRouteClearFlagKeys=story:route-clear:map2_14j:route-clear:map2_14j,story:route-complete:map2_14j:route-complete:map2_14j, titleRouteCompletionLabelHidden=True, titleRouteCompletionHidden=True, titleRouteClearVisible=True, completionItem=-, clearLabelCount=1, routeNextText=클리어, HUD objective `목표 후보 루트 클리어 map2_14j` / `다음 클리어 요약 확인`, verifies the objective action and route button both reopen route-clear:map2_14j with runtimeRouteClearObjectiveAndButton=True and duplicateProgress=True, and reopens the runtime menu clear summary with duplicateProgress=True while keeping savedRouteClearCount=1 and runtimeRestoredRouteClearMenuSummary=True, all while keeping originalFullGameCompletionImplemented=False.","out/completion_candidate_review_summary.* now verifies the same playable completion gate on the longer map5_40f routeAssist chain: completionNotice=activeId=route-complete:map5_40f completionRouteCompleteCount=1 playableGate=prototype-playable-route-gate playableGateOpen=True playableGateLabel=웹 후보 루트 완료 map5_40f routeGateFeedback=route-completion-feedback:후보 완료 map5_40f routeGateFeedbackRender=True routeGateSound=victory routeGateSoundSrc=../extract_wlk/12.wav routeGateSoundPlayed=True, routeClearGate=activeId=route-clear:map5_40f routeClearCount=1 completionRouteClearCount=1 payloadRouteClearCount=1 autoSource=route-clear routeGateFeedback=route-clear-feedback:후보 클리어 map5_40f routeGateFeedbackRender=True routeGateSound=victory routeGateSoundSrc=../extract_wlk/12.wav routeGateSoundPlayed=True lines=후보 클리어 요약 map5_40f / 후보 진행 12 / 필드 전투 승리 1 / 완료 기록 1, routeClearTitleContinue=titleContinue=True label=이어하기 map5_40f 66,39 전투 후보 map5_40f 클리어 map5_40f clickOk=True map=map5_40f@66,39 routeCompleteCount=1 routeClearCount=1 savedRouteCompleteCount=1 savedRouteClearCount=1 routeNextText=클리어 playableGateOpen=True, while keeping originalFullGameCompletionImplemented=False and originalRoutePromotionImplemented=False."],"gaps":["Confirmed normal route coverage is far below full-game progression.","The playable completion gate is browser-local routeAssist completion, not the original full-game ending, normal-route promotion, or story flag completion.","Major original game systems beyond map walking, reviewed map transitions, and candidate/prototype gameplay surfaces are not implemented."],"verifiedBy":["tools/verify_web_assets.py"],"coverage":"Browser-local routeAssist completion exists, but original normal-route/full-game completion is not achieved."}],"blockingRequirements":[{"gateId":"strictSourceHotspot","required":"strict map1_01a source coordinate or hotspot linked to map2_02d","missingEvidence":"strict source coordinate","sourceArtifact":"out/route_promotion_gate.json"},{"gateId":"tileHotspotConfirmation","required":"confirmed normal gameplay tile trigger, not routeAssist/debug trial","missingEvidence":"tile hotspot confirmation","sourceArtifact":"out/route_promotion_gate.json"},{"gateId":"realSelector20Savedata","required":"non-synthetic captured savedat with selector 2:0 / selected pointer 0x00540714","missingEvidence":"real selector 2:0 captured savedata","sourceArtifact":"out/route_promotion_gate.json"},{"gateId":"selectedRootExecution","required":"runtime/static proof that selected pointer 0x0059de30 executes root 0x00540714","missingEvidence":"selected-root execution ref","sourceArtifact":"out/route_promotion_gate.json"},{"gateId":"runtimeTraceOrEquivalent","required":"watchpoint/runtime trace or equivalent selected-root proof","missingEvidence":"runtime trace/equivalent selected-root proof","sourceArtifact":"out/route_promotion_gate.json"}],"hardMissingEvidence":["strict source coordinate","tile hotspot confirmation","selected-root execution ref","real selector 2:0 captured savedata","runtime trace/equivalent selected-root proof"],"nextRequiredInputs":["Captured gameplay savedat with selector 2:0 and selected pointer 0x00540714 covering map1_01a -> map2_02d.","Stable selected-root runtime trace or equivalent proof for selected pointer 0x0059de30 executing root 0x00540714.","Strict map1_01a source coordinate or tile-hotspot proof linked to map2_02d."],"notAchievedReason":"completion_audit.json is achieved=false and the route hard gates still require strict hotspot, real selector 2:0 savedata, selected-root execution, and runtime trace/equivalent proof."}