Skip to content

Latest commit

 

History

History
312 lines (254 loc) · 15.3 KB

File metadata and controls

312 lines (254 loc) · 15.3 KB

Agent Guide

This file is the short operational map for coding agents and maintainers. Public user documentation lives in docs/; keep this file focused on how to work in the repository without scanning the whole tree or breaking Kanama's release shape.

Current Baseline

  • Kanama release line: 0.4.0 (tagged 2026-07-24; a pre-1.0 preview baseline).
  • Godot baseline: Godot 4.7 stable (re-pin only on stable releases; bumps follow docs/contributing/godot-upgrade.md).
  • Desktop runtime/build JDK: JDK 25+.
  • Mobile is Supported (4.7 stable), promoted from Experimental 2026-07-14 once the §7 mobile promotion bar went green. Android is device-validated across four models (Pixel 7, Moto g 5G 2023, Galaxy S10+, Pixel 3 XL): debug validated to Android 9, but release builds require Android 13+. iOS is device-validated on iPhone 12 and iPhone 15 Pro. Carried caveats: packaged mobile addons are runtime-only (compiling project scripts needs the Kanama checkout), the Android release path depends on the JitPack PanamaPort fork, and there is no mobile hot reload. See docs/reference/version-support.md for the full claims and validation dates.
  • Release artifacts use two shapes: desktop kits for new projects and store add-ons for existing projects. See docs/exporting/desktop.md.

Always confirm this section against README.md, docs/reference/version-support.md, and CHANGELOG.md before changing release wording.

First Stop for Implementation Work

If you're working on a specific roadmap task, read the matching file in /Users/lmuller/dev/kanama-tasks/ first. Each task is standalone and points to the smallest doc set and exact files to edit. The index is kanama-tasks/README.md. For ad-hoc work not covered by a task file, use "Read First By Task" below.

Read First By Task

Avoid broad context scans. Start with the smallest doc set that matches the task, then use targeted rg searches.

  • Project status, requirements, and user quick start: README.md, docs/getting-started/index.md, and docs/reference/version-support.md.
  • Contributing to runtime, wrappers, generator, Android, docs, or packaging: CONTRIBUTING.md and docs/contributing/index.md.
  • Bootstrap, FFI, script lifetime, ClassDB registration, script resources, or hot reload: docs/contributing/architecture.md.
  • Generated wrappers, generator policy, ABI helpers, KDoc sync, or wrapper coverage: docs/contributing/wrapper-maintenance.md.
  • Android runtime/export support: docs/exporting/android.md and docs/contributing/android-internals.md.
  • New Kanama game projects: docs/getting-started/source-checkout.md, docs/game-dev/scripts.md, docs/game-dev/godot-api.md, docs/game-dev/properties-resources.md, and docs/game-dev/signals.md.
  • Porting GDScript or working on gameplay demos: docs/game-dev/porting-gdscript.md, docs/contributing/demo-porting-rules.md, and the companion demo repo guide if present.

Repository Map

  • src/main/kotlin: Kanama runtime, bootstrap-facing Kotlin code, wrappers, value types, and editor/runtime support.
  • src/generated/godot: generated Panama bindings from Godot headers.
  • annotations: public KSP annotation definitions for game scripts.
  • processor: KSP processor that emits script registrars and metadata.
  • project-scripts: Gradle support for compiling consumer project scripts.
  • templates/starter and templates/starter_project: source-checkout onboarding templates.
  • templates/release-kit and templates/store-addon: packaged artifact templates.
  • example_project: local smoke-test Godot project.
  • scripts: CI, smoke, audit, packaging, and generator helper scripts.
  • docs: public MkDocs documentation.

Sibling Repositories

Repo Audience Contents
kanama/ (this repo) public Runtime, wrappers, generator, docs.
kanama-demos/ public 19 public demos: Bunnymark, Match3, 3D-Platformer, dodge, squash, character-controller, Racing, FPS, third-person, tps-demo-kanama, City-Builder, and others.
kanama-demos-private/ private Private demos such as deformable-snow-kanama and plowpatrol; not for tracked files.
kanama-maintainer-notes/ private DEV_LOG.md and TASKS.md with device IDs, signing teams, and exact local commands; never copy values from here into tracked files.

Generated vs Hand-Authored Files

File Generated by Notes
src/main/kotlin/binding/runtime/ObjectCalls.kt scripts/generate_api_wrapper.py Large generated call surface with many confined-arena blocks; edit the generator, not this file.
Generated wrappers under src/main/kotlin/ scripts/generate_api_wrapper.py Public wrappers must be regeneratable unless explicitly marked as hand-authored policy classes.
src/generated/godot/* scripts/refresh_godot_api.sh from Godot headers Generated Panama bindings.
docs/internals/reference/ios-backend-handwritten.md scripts/ios_handwritten_report.py Generated report; do not hand-edit.

Hand-authored policy classes are the exception and are explicitly marked. Prefer generated wrappers and focused policy fixes over ad hoc hand wrappers.

Repository Rules

  • Public package namespace is net.multigesture.kanama.
  • Do not reintroduce local workstation paths, private maintainer notes, raw task logs, or obsolete project names into tracked files.
  • Keep running task lists, dev logs, bundles, raw logs, and private handoff notes outside the public repo.
  • Keep Gradle coordinates, docs snippets, badges, changelog headings, package workflow variables, and companion demo defaults synchronized with the active release pass.
  • Some invariants are pinned in more than one file; changing one and not the others is silent drift. Changing the example script's exported-property set means updating the count in both scripts/runtime_smoke.sh (properties size = N) and scripts/local_ci.sh (propertyCount = N). Adding a field to the serialized script model (ScriptModelJson) means bumping SCRIPT_MODEL_SCHEMA_VERSION. Before finishing, grep the repo for the old value of any pinned constant you touched.
  • Claims drift the same way constants do, and nothing notices. When you close a task that removes a limitation, grep for the comments citing it and delete the ones that are now false. Task 30 gave iOS full wrapper breadth and nobody grepped for "missing iOS wrapper types"; that line steered decisions for six more weeks. Where the limitation is machine-checkable, write it as a KANAMA-BLOCKED marker instead — scripts/audit_stale_blockers.py fails the build when the blocker is lifted. See "Documented Limitations" in CONTRIBUTING.md.
  • Do not widen ABI-sensitive types to make wrappers generate. Add exact helper shapes and audits first.
  • Prefer generated wrappers and focused policy fixes over ad hoc hand wrappers.
  • Do not claim platform support unless the matching smoke path has passed.
  • Do not treat package zips as proof of exported-game support; desktop kits validate editor/runtime onboarding.
  • In GDExtension callbacks, never pre-empt work the engine performs after the callback returns — path assignment, ResourceCache registration, ownership transfer. Precedent: issue #106, where the .kt loader pre-set the script path inside _load, the engine's registering set_path() early-returned, and every load minted a distinct Script ("cannot assign X to X").
  • When adding an engine-facing surface (Script/ScriptLanguage virtual, property metadata field, loader behavior), pair the Kanama fixture with the equivalent GDScript construct in a smoke parity probe (the _kanama_classdb_script_class_smoke / _kanama_typed_global_class_smoke pattern), and never leave a virtual returning a constant without a comment arguing why the constant is correct.

Looks Wrong But Isn't

Item Why it's correct Do not change
REFCOUNTED_REFERENCE_HASH == REFCOUNTED_UNREFERENCE_HASH (both 2240911060) in ScriptBridge.kt reference() and unreference() share the bool() signature, so their method hashes match by construction. Verified legitimate against extension_api.json (architecture-review verdict; the review is archived in the internal task repo).
compatibility_minimum = "4.7" in .gdextension descriptors classdb_register_extension_class6 is a Godot 4.7 API; 4.3-4.6 loads would fail at class registration. Keep the 4.7 invariant from architecture review F1.
Single-class iOS wrapper regeneration can drop methods returning other wrapper types The generator emits returns only for classes in the active iOS wrapper set. Regenerate with the full emitted-class union (the iOS "Generator Gotcha" note; recorded in the internal task repo).
PanamaPort textual remap from java.lang.foreign.* to com.v7878.foreign.* It is a pragmatic Android ART compatibility path guarded by pre/post audits. Do not clean it up without replacing the audit mitigation; see the architecture review Android section.

Common Workflows

New Kanama Game Project

Use the source-checkout path for current main or unreleased changes:

./gradlew createStarterProject \
  -PkanamaStarterProjectDir=/absolute/path/to/kanama-starter
./gradlew installAddonJar \
  -PkanamaProjectDir=/absolute/path/to/kanama-starter \
  -PkanamaProjectScriptsDir=/absolute/path/to/kanama-starter

For an existing project, use installStarterTemplate instead of copying a full starter project over it:

./gradlew installStarterTemplate \
  -PkanamaStarterProjectDir=/absolute/path/to/godot_project
./gradlew installAddonJar \
  -PkanamaProjectDir=/absolute/path/to/godot_project \
  -PkanamaProjectScriptsDir=/absolute/path/to/godot_project

Kotlin scripts belong in kotlin-src/, attach with @ScriptClass, and are compiled through the Godot Build Scripts button or buildScripts.

Package Artifacts

Build the host desktop kit and local store add-on:

./gradlew packageDistributions

Validate package install flows without a sibling Kanama checkout:

scripts/package_install_smoke.sh \
  --desktop-kit \
  build/distributions/kanama-desktop-kit-v<version>-<platform>.zip \
  /absolute/path/to/godot-4.7-stable
scripts/package_install_smoke.sh \
  --store-addon \
  build/distributions/kanama-store-addon-v<version>.zip \
  /absolute/path/to/godot-4.7-stable

Desktop kits are complete starter projects for one platform. Store add-ons are install-safe addons/ zips for existing projects.

Port Existing GDScript

Follow docs/game-dev/porting-gdscript.md and docs/contributing/demo-porting-rules.md. Preserve scene names, exported property names, signal names, node paths, upstream gameplay semantics, and license/attribution notes. Prefer fixing missing Kanama wrapper/runtime support in this repo over hiding framework gaps in demo code.

Wrapper Work

Use scripts/generate_api_wrapper.py, scripts/check_wrapper_generator.py, wrapper audits, and the coverage pages under docs/contributing/. Public wrappers must be regeneratable unless they are explicitly hand-authored policy classes.

Regenerate name constants after API refreshes:

python3 scripts/generate_name_constants.py

Refresh wrapper reports before release-facing wrapper changes:

python3 scripts/api_wrapper_generator_report.py --markdown docs/contributing/wrapper-generator-report.md
python3 scripts/api_wrapper_coverage.py --markdown docs/contributing/api-coverage.md

Godot Upgrade

Follow docs/contributing/godot-upgrade.md (the authoritative runbook). Kanama re-pins only on stable Godot releases. Edit kanamaGodotVersion in gradle.properties (plus the CI GODOT_VERSION dash form), then run the mechanical pipeline:

scripts/upgrade_godot.sh /absolute/path/to/new_godot_binary

It refreshes API/header inputs and Panama bindings, re-adopts all generated wrappers, re-syncs KDoc, refreshes reports, and re-runs the gates — then stops at the human-judgment boundary (device gates, support claims, release wording). Run local CI and demo smoke checks before changing support claims.

Every stable bump: scan for newly deprecated GDExtension functions and keep the backends converged. 4.7 is the baseline — we do not carry deprecated GDExtension callbacks once a newer variant solves the same problem. Diff gdextension/gdextension_interface.h for @deprecated entries, migrate every backend to the newest variant, and confirm the desktop/Android JVM backend (src/main) and the iOS Kotlin/Native shim (ios/bootstrap/kanama_ios_shim.c) bind the same construct/register/script-instance entry points — they pick these independently and have drifted silently before (construct_object2 vs construct_object3, issue #91 / task 61). Enforced by scripts/check_gdextension_modernization.py (in local_ci.sh and the upgrade gates); steps are in the upgrade runbook.

Android Work

Android is Supported (4.7 stable), but never widen a support claim past what the matching APK/emulator or device smoke path has actually validated — the release floor (Android 13+) and the runtime-only addon caveat both still hold. Use the Gradle AAR workflow documented in docs/exporting/android.md; do not infer Android support from desktop package success.

Validation

Run the narrowest useful check while iterating, then use the broader gate before publishing or rewriting history:

python3 scripts/validate_godot_api.py --api extension_api.json
python3 scripts/check_wrapper_generator.py
mkdocs build --strict
./gradlew ktfmtFormat            # format hand-written Kotlin (ktfmtCheck gates CI)
./gradlew jar
./scripts/local_ci.sh /absolute/path/to/godot-4.7-stable

Hand-written Kotlin is formatted with ktfmt (googleStyle, 2-space) via the root build.gradle.kts; local_ci.sh runs ktfmtCheck as a stage. The generated **/net/multigesture/kanama/api/** wrappers and *.gradle.kts scripts are excluded — see the Formatting section in CONTRIBUTING.md.

local_ci.sh is the definition of done for processor, runtime, and script-model changes: it runs the static registrar/count/generated-code checks that the in-editor runtime smoke does not exercise, so a passing runtime smoke alone is not sufficient. It also compiles the generated code for every exported property shape, so run it after widening any accepted @ScriptProperty type.

CI coverage is build-time + desktop runtime only. The PR gate compiles every platform (desktop, the Android runtime AAR, the iOS device xcframework) and runs the desktop/Linux runtime smokes — which also exercise the runtime Android shares via PanamaPort. It does not run on-device: the Android R8-minified runtime gate (the task-42 PanamaPort-under-R8 class) and the iOS device runtime are deliberate local device gates. A green PR means it builds everywhere and the desktop runtime behaves — not that it is device-safe on mobile.

Before a release tag, prefer an isolated clone gate:

./scripts/fresh_clone_smoke.sh /absolute/path/to/godot-4.7-stable

The Web analog is scripts/web_fresh_checkout_smoke.sh: it exports from a clean clone in an isolated workspace, proves no build-machine path reaches a served file and that the demo source tree is untouched, then drives the artifact in a browser with the harness from that clone. See docs/exporting/web.md.

Use demo-repo smoke tasks when a change affects real gameplay ports, wrappers used by demos, package onboarding, or Android exports.