diff --git a/.gitignore b/.gitignore index 1140ee1c..adae45d5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,9 @@ .DS_Store tmp/ build/ +!runtime/build/ +runtime/build/* +!runtime/build/.gdignore dist/ node_modules/ runtime/.godot/ diff --git a/docs/goals/PHASE9_0_BOOT_TITLE_GOAL.md b/docs/goals/PHASE9_0_BOOT_TITLE_GOAL.md index 8b3aacce..29d843f8 100644 --- a/docs/goals/PHASE9_0_BOOT_TITLE_GOAL.md +++ b/docs/goals/PHASE9_0_BOOT_TITLE_GOAL.md @@ -76,6 +76,11 @@ acceptance, fun, listening and willingness to continue remain open, as do physical device, packaging, store and release gates. No later page is authorized by this candidate. +The Owner subsequently authorized one private remote H5 preview of this exact +candidate. That authorization is limited by +`docs/runtime/PHASE9_0_WEB_PREVIEW.md`; it does not reopen product scope or +grant device, store, publishing or release acceptance. + ## Legal Terminal `Aetherbound Guild P9.0 boot and Title candidate ready` diff --git a/docs/reviews/PHASE9_GODOT_VERTICAL_SLICE_LEDGER.md b/docs/reviews/PHASE9_GODOT_VERTICAL_SLICE_LEDGER.md index 98fc9d74..6be012db 100644 --- a/docs/reviews/PHASE9_GODOT_VERTICAL_SLICE_LEDGER.md +++ b/docs/reviews/PHASE9_GODOT_VERTICAL_SLICE_LEDGER.md @@ -9,3 +9,4 @@ separate. | 2026-08-12 | fixed source `e7eb408`; `p9-1-e7eb408-attempt01` | `P9_1_RUNTIME_CANDIDATE_READY` | Fresh isolated Git archive passes domain `cases=40`, parsed ScreenTouch `touch=25 fallback=0 direct=0`, layout `fixtures=18`, six-state normal rendering at exact `1280x720`, `844x390` and `390x844`, and existing design/economy/loadout/provenance regressions. The 28-row ignored evidence inventory SHA-256 is `7ff16f1a231e6c76dba0ec0545ea112f0afcc87f611b2ceb7ea953bbe7d22384`; strict diagnostics are clean and no owned Godot process remains. `844x390` and `390x844` captures are domain-fixture render evidence with no touch claim. Automated/runtime and agent visual gates pass; Owner understanding, choice, fun, visual/listening, device and release remain open. P9.2 stays frozen. | | 2026-08-12 | Owner sequencing clarification after P9.1 candidate | `P9_1_PRESERVED_P9_0_ACTIVE` | Owner rejected the broad delivery rhythm, not the preserved source, and required real UI/code to proceed page by page from launch. P9.1 is not Owner-accepted. P9.0 now owns only Boot verification and the no-run Title; Start New Game may hand off to the existing Contract page. P9.1 review and P9.2 remain frozen. | | 2026-08-12 | fixed source `f881920`; `p9-0-f881920-attempt01` | `P9_0_BOOT_TITLE_CANDIDATE_READY` | Fresh isolated Git archive passes the ordered nine-resource Boot and visible verified terminal, no-run Title, parsed ScreenTouch `touch=5 fallback=0 direct=0`, initial keyboard/controller focus, layout `fixtures=6`, six normal-render Boot/Title frames at exact `1280x720`, `844x390` and `390x844`, affected P9.1 regressions and current design/economy/loadout/provenance gates. Strict diagnostics are zero. The 27-row evidence content inventory verifies 27/27 with SHA-256 `3c34f1742357d0cc57e8c4c59124133b8fb92de0a2dec01a90b2bbcf4ffb8318`. Automated/runtime and agent visual gates pass; Owner understanding, visual acceptance, fun, listening, device and release remain open. Later pages remain frozen. | +| 2026-08-12 | Owner Web preview request | `P9_0_WEB_PREVIEW_AUTHORIZED` | Owner requested a dedicated remote H5 page for phone review. Only a non-threaded Godot Web export, browser verification and private Artifact Gallery deployment of fixed P9.0 content are authorized. Product behavior, later pages, native packages, stores and release remain frozen. | diff --git a/docs/runtime/PHASE9_0_BOOT_TITLE_CONTRACT.md b/docs/runtime/PHASE9_0_BOOT_TITLE_CONTRACT.md index 895370ca..f2721934 100644 --- a/docs/runtime/PHASE9_0_BOOT_TITLE_CONTRACT.md +++ b/docs/runtime/PHASE9_0_BOOT_TITLE_CONTRACT.md @@ -6,11 +6,11 @@ boot_verifying -> title_no_run -> new_game_contract ``` -Boot verifies the required local scene, domain, world, actor and equipment -resources in stable order. A failed check retains the failed path and offers a -real Retry action. A successful check presents a real `9 / 9 resources ready` -terminal before entering Title. Boot itself spends nothing and creates no save -or run. +Boot verifies the required local scene, domain, world, actor, equipment and +embedded UI font resources in stable order. A failed check retains the failed +path and offers a real Retry action. A successful check presents a real +`10 / 10 resources ready` terminal before entering Title. Boot itself spends +nothing and creates no save or run. Title exposes: diff --git a/docs/runtime/PHASE9_0_WEB_PREVIEW.md b/docs/runtime/PHASE9_0_WEB_PREVIEW.md new file mode 100644 index 00000000..148c58b7 --- /dev/null +++ b/docs/runtime/PHASE9_0_WEB_PREVIEW.md @@ -0,0 +1,47 @@ +# Phase 9.0 Web Preview Delivery + +## Owner Authorization + +On 2026-08-12 the Owner requested a dedicated remote H5 page for phone review +of the accepted P9.0 runtime candidate. This authorizes only a Godot Web export, +local browser verification and private Artifact Gallery deployment. + +## Content Boundary + +- Product behavior authority remains P9.0 source + `f881920b1be29933169950429d4e55d8cac825d8`; the Web-compatible successor may + add only the embedded font, stream-playback selection, export configuration + and capture-harness cleanup required by this delivery. +- The Web package may expose the preserved Contract page only through the + already accepted `Start New Game` handoff. +- It may not change Boot, Title, Contract, Recruit, Market, battle, economy, + save, art, audio or content authority. +- Tests, evidence, build outputs, source documents and repository history must + not be included in the exported PCK. + +## Delivery Contract + +- Preset: `Web Preview` in `runtime/export_presets.cfg`. +- Template: Godot 4.6.1 non-threaded Web release, so the private remote page + does not require cross-origin isolation headers. +- Canvas: browser-window responsive, canvas focused on startup, experimental + mobile virtual keyboard enabled. +- Portrait phones show an explicit rotate-to-landscape overlay instead of a + tiny letterboxed game canvas; rotating to landscape reveals the game. +- Local output: ignored `runtime/build/web-preview//`. +- Remote output: the smallest self-contained export directory, published as a + dedicated Artifact Gallery HTML item. + +Verify export success, expected HTML/JS/WASM/PCK files, exact ZIP data-package +contents with zero `tests/`, `evidence/` or `build/` entries, local HTTP loading, +real browser canvas dimensions, portrait rotation guidance, one canvas click +and the `Start New Game` handoff. Verify the Gallery health and signed share URL +after publication. + +The browser build embeds `runtime/assets/fonts/NotoSansSC.ttf` and its +`runtime/assets/fonts/OFL.txt` license so Simplified Chinese does not depend on +a host font. `PrototypeSFX` must use Godot stream playback; a Web backend may +not select sample playback for `AudioStreamGenerator`. + +This is a browser preview, not physical-device, packaging, store, publishing or +release acceptance. Owner review of the remote page remains a human gate. diff --git a/runtime/assets/fonts/NotoSansSC.ttf b/runtime/assets/fonts/NotoSansSC.ttf new file mode 100644 index 00000000..4c4da763 Binary files /dev/null and b/runtime/assets/fonts/NotoSansSC.ttf differ diff --git a/runtime/assets/fonts/NotoSansSC.ttf.import b/runtime/assets/fonts/NotoSansSC.ttf.import new file mode 100644 index 00000000..36f6b516 --- /dev/null +++ b/runtime/assets/fonts/NotoSansSC.ttf.import @@ -0,0 +1,36 @@ +[remap] + +importer="font_data_dynamic" +type="FontFile" +uid="uid://drqjmcfs0e77b" +path="res://.godot/imported/NotoSansSC.ttf-53e7d2951cb723449d16fcecb813db38.fontdata" + +[deps] + +source_file="res://assets/fonts/NotoSansSC.ttf" +dest_files=["res://.godot/imported/NotoSansSC.ttf-53e7d2951cb723449d16fcecb813db38.fontdata"] + +[params] + +Rendering=null +antialiasing=1 +generate_mipmaps=false +disable_embedded_bitmaps=true +multichannel_signed_distance_field=false +msdf_pixel_range=8 +msdf_size=48 +allow_system_fallback=true +force_autohinter=false +modulate_color_glyphs=false +hinting=1 +subpixel_positioning=4 +keep_rounding_remainders=true +oversampling=0.0 +Fallbacks=null +fallbacks=[] +Compress=null +compress=true +preload=[] +language_support={} +script_support={} +opentype_features={} diff --git a/runtime/assets/fonts/OFL.txt b/runtime/assets/fonts/OFL.txt new file mode 100644 index 00000000..31ea83be --- /dev/null +++ b/runtime/assets/fonts/OFL.txt @@ -0,0 +1,92 @@ +Copyright 2014, 2015 Adobe Systems Incorporated (http://www.adobe.com/). + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +https://openfontlicense.org + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply to +any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/runtime/assets/fonts/README.md b/runtime/assets/fonts/README.md new file mode 100644 index 00000000..ab7031c6 --- /dev/null +++ b/runtime/assets/fonts/README.md @@ -0,0 +1,14 @@ +# Runtime Font + +`NotoSansSC.ttf` is the unmodified Noto Sans SC regular font used by the Godot +runtime so Simplified Chinese rendering does not depend on a host system font. + +- Font SHA-256: `a0af66489baf1f306732ff16da58d86c342e0b3b4b3674079ee2a9b0aee23258`. +- License file: `OFL.txt`. +- License SHA-256: `e2b6ccef8ef613a8fad6571020ad77c9143273a752001b42293a089da46940d0`. +- License: SIL Open Font License 1.1. +- Copyright notice: Copyright 2014, 2015 Adobe Systems Incorporated, as + recorded in `OFL.txt`. + +The font is bundled unchanged. Do not rename, modify or replace it without +updating this record, its license and the Boot resource contract. diff --git a/runtime/build/.gdignore b/runtime/build/.gdignore new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/runtime/build/.gdignore @@ -0,0 +1 @@ + diff --git a/runtime/export_presets.cfg b/runtime/export_presets.cfg new file mode 100644 index 00000000..ddb5a553 --- /dev/null +++ b/runtime/export_presets.cfg @@ -0,0 +1,35 @@ +[preset.0] + +name="Web Preview" +platform="Web" +runnable=true +advanced_options=false +dedicated_server=false +custom_features="" +export_filter="all_resources" +include_filter="assets/fonts/OFL.txt" +exclude_filter="tests/*,evidence/*,build/*" +export_path="build/web-preview/index.html" +patches=PackedStringArray() +encryption_include_filters="" +encryption_exclude_filters="" +seed=0 +encrypt_pck=false +encrypt_directory=false +script_export_mode=2 + +[preset.0.options] + +custom_template/debug="" +custom_template/release="" +variant/extensions_support=false +variant/thread_support=false +vram_texture_compression/for_desktop=true +vram_texture_compression/for_mobile=false +html/export_icon=true +html/custom_html_shell="" +html/head_include="" +html/canvas_resize_policy=2 +html/focus_canvas_on_start=true +html/experimental_virtual_keyboard=true +progressive_web_app/enabled=false diff --git a/runtime/main.gd b/runtime/main.gd index 08d036d4..96a5424c 100644 --- a/runtime/main.gd +++ b/runtime/main.gd @@ -1,6 +1,7 @@ extends Control const RunState = preload("res://domain/run_state.gd") +const UI_FONT := preload("res://assets/fonts/NotoSansSC.ttf") enum AppPage { BOOT, @@ -25,6 +26,7 @@ const BOOT_REQUIRED_PATHS: Array[String] = [ "res://assets/actors/wayrunner.png", "res://assets/actors/kindhand.png", "res://assets/equipment/eq_012_open_line_cord.png", + "res://assets/fonts/NotoSansSC.ttf", "res://default_bus_layout.tres", ] @@ -68,6 +70,9 @@ var sfx_playback: AudioStreamGeneratorPlayback func _ready() -> void: + var runtime_theme := Theme.new() + runtime_theme.default_font = UI_FONT + theme = runtime_theme build_shell() render() if auto_boot: @@ -152,6 +157,7 @@ func build_shell() -> void: audio_player = AudioStreamPlayer.new() audio_player.name = "PrototypeSFX" audio_player.bus = &"SFX" + audio_player.playback_type = AudioServer.PLAYBACK_TYPE_STREAM audio_player.stream = generator add_child(audio_player) audio_player.play() diff --git a/runtime/tests/p9_0_boot_title_test.gd b/runtime/tests/p9_0_boot_title_test.gd index 0189e377..6331f032 100644 --- a/runtime/tests/p9_0_boot_title_test.gd +++ b/runtime/tests/p9_0_boot_title_test.gd @@ -30,6 +30,7 @@ func run_test() -> void: game.boot_paths_override.clear() await game.run_boot_verification() expect(game.boot_verified, "required local content verified") + expect(game.theme != null and game.theme.default_font != null, "runtime uses an embedded UI font") expect(game.boot_success_presented, "successful Boot presented a verified terminal before Title") expect(game.page == game.AppPage.TITLE, "verified Boot reaches Title") expect(find_control(game, "TitleScreen") != null, "Title screen is visible") @@ -49,7 +50,7 @@ func run_test() -> void: game.queue_free() await drain_cleanup() if failures.is_empty(): - print("ABG_P9_0_BOOT_TITLE_OK checks=22 required=9 failure_retry=1 verified_terminal=1 no_run=1 focus=start contract_handoff=1 mutation=0") + print("ABG_P9_0_BOOT_TITLE_OK checks=23 required=10 embedded_font=1 failure_retry=1 verified_terminal=1 no_run=1 focus=start contract_handoff=1 mutation=0") quit(0) else: for failure in failures: diff --git a/runtime/tests/p9_0_capture_test.gd b/runtime/tests/p9_0_capture_test.gd index d7410211..a7afe939 100644 --- a/runtime/tests/p9_0_capture_test.gd +++ b/runtime/tests/p9_0_capture_test.gd @@ -56,8 +56,10 @@ func capture_page(index: int, page_name: String, locale: String, text_scale: flo var error := image.save_png(path) if error != OK: failures.append("capture %s failed with %s" % [page_name, error]) - game.queue_free() viewport.queue_free() + await process_frame + await RenderingServer.frame_post_draw + await create_timer(0.5).timeout await drain_cleanup() diff --git a/runtime/tests/p9_0_capture_test.gd.uid b/runtime/tests/p9_0_capture_test.gd.uid new file mode 100644 index 00000000..7f0ccb0b --- /dev/null +++ b/runtime/tests/p9_0_capture_test.gd.uid @@ -0,0 +1 @@ +uid://dmedb6b4h0xan diff --git a/runtime/tests/p9_0_layout_test.gd.uid b/runtime/tests/p9_0_layout_test.gd.uid new file mode 100644 index 00000000..39414085 --- /dev/null +++ b/runtime/tests/p9_0_layout_test.gd.uid @@ -0,0 +1 @@ +uid://dgjk0wktghf4p