Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
145 changes: 140 additions & 5 deletions .agents/skills/android-runtime-parity/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,20 +1,50 @@
---
name: android-runtime-parity
description: Maintain Android Fusion object selection and creation helpers, checking the current Windows implementation for changes on every Android extension or runtime update.
description: Maintain Android Fusion runtime parity by checking Windows selection/creation helpers, using native application storage and file access, and synchronizing shared media dependencies.
---

# Android runtime helper parity

For transient execution-environment or transport interruptions during this work,
follow [execution environment recovery](../execution-environment-recovery/SKILL.md):
announce recovery, use bounded increasing-delay retries, and verify write results
before resuming. Do not stop after the first transient disconnection.

Use this skill whenever updating Android extensions, Android exporter/runtime
integration, or the shared object selection/creation helpers. Also use it when
changing their Windows counterparts so Android does not silently fall behind.

## Implementation boundary

Keep Android runtime access in Java. These helpers manipulate Java objects,
selection chains and event/action state directly; do not introduce JNI just to
reuse the Windows helper classes. Pure computational plugin algorithms can have
a separate native core when justified by that plugin's requirements.
Treat **C++ as the first-class implementation** for existing C++ extensions.
Reuse their algorithms, playback state, queues, caches, synchronization and public
abstractions across platforms. Use conditional compilation to isolate necessary
platform dependencies; avoid a separate Android business-logic implementation.
JNI is a boundary to that shared C++ implementation, not a reason to replace it.

Keep actual Fusion Android runtime access in Java. Selection chains, event/action
state, `CRunApp` storage/files and renderer calls manipulate Java runtime objects;
do not import Windows pointers/layouts or add JNI solely to imitate those APIs.
Use Java/Android APIs where required by the platform, with thin JNI marshalling
around the reused C++ core. A platform-specific adaptor is appropriate; a duplicate
portable engine is not.

The goal is one C++ core that compiles and is reused across platforms, including
Windows. Conditional compilation should isolate unavoidable platform APIs, not
hide an independently rewritten mobile engine. Keep portable algorithms, state,
resource ownership and fixes in that common implementation.

When a Windows dependency blocks reuse, report the concrete header/API, affected
call chain and missing portable contract before choosing a replacement strategy.
Bring the blocker to the user and pursue a shared-core refactor or an adaptor
through its existing interface. Do not silently work around it with a new Java
or C++ implementation of the same logic. Existing authorization for a specific
adaptor/refactor remains valid; this rule does not add a confirmation step to
routine work within that agreed design.
Scope compatibility branches to the platform, ABI and API range that actually
needs them. Keep other platforms on their existing code path, and verify that
with targeted compilation or preprocessing where practical. Report unresolved
platform dependencies instead of substituting another portable-core implementation.

Windows defines the shared helper capabilities and filtering behavior. Preserve
Android-specific runtime representations and creation entry points. Port the
Expand Down Expand Up @@ -82,3 +112,108 @@ pointers, layouts or call tables.
comparison instead of claiming HEAD includes them. In the completion report,
briefly state whether Windows helpers changed, what was synchronized, any
retained platform differences and the validation performed.

## Application-owned extension state

For every port, inspect Windows `CreateRunObject`, `DestroyRunObject`, `StartApp`
and `EndApp`, including `GetExtUserData`/`SetExtUserData` ownership. A Fusion
**global object flag does not keep the Java extension instance or its JNI handle
alive**: Android `killRunLoop` saves global values and then destroys frame objects.
`saveGlobalObjectsData`/`loadGlobalObjectsData` restore values, not private extension
fields or native resources.

If Windows retains shared state until EndApp, make the Android state application
owned, keyed by the actual `CRunApp`, independently of instance reference counts.
Destroying the last frame instance must only detach it. Preserve playback, channel
settings, caches, source leases and periodic maintenance through frames with no
instances. Release on application exit, reset on Restart Application, and isolate
sub-applications while allowing intentionally shared platform devices. Keep user
pause separate from application suspension; handle backgrounding and exit even
when no extension instance remains.

Use Fusion's **native extension global storage** first: subclass
`Extensions.CExtStorage`, acquire via `CRun.getStorage(id)`, publish via
`addStorage(data, id)`, and remove via `delStorage(id)`. These are already scoped
to `rhApp.extensionStorage`; do not build a parallel static application registry
or manufacture global objects/storage markers. Reuse the extension identifier
and reject ID/type collisions. Consult the supplied runtime and SDK documentation
before inventing a global-state mechanism.

The supplied `CExtStorage` has no cleanup/update callbacks and is not cleared by
`CRunApp.killGlobalData()`. Add only the lifecycle adaptation actually needed for
native resource release and maintenance; do not replace exporter runtime classes.
Observe existing fields read-only when callbacks are unavailable, with documented
semantics and tests for transitions consumed between observation ticks. SDL Mixer
observes `CRunApp.playerNames` identity: `initGlobal()` replaces that array on each
Restart Application, including subapps sharing values, while ordinary frame changes
and Restart Frame preserve it. Also observe `CRunApp.file == null` after
`endApplication()`: subapp destruction can call it without leaving SL_QUIT set.
Do not write `adGO`, global values or player data
to create an artificial lifecycle token.

Add regressions for last-instance destruction and recreation, a frame without the
object, frame restart versus application restart, exit without a surviving
instance, parent/sub-application isolation and cleanup, source lifetime, and pause
restoration. Distinguish fixture coverage from a full Fusion Runtime APK test.

## Shared dependency version selection

Check Windows dependency deployment recipes before choosing Android versions.
Use the same authoritative version/revision selection, with platform-specific
SDK paths, toolchains, features and archive hashes. In particular Android FFmpeg
must read `Extensions/_3rdLib/FFmpeg-build/FFmpegVersion.cmake`, used by Windows;
do not repeat a release constant in the Android builder. Validate cached SDK
version, corresponding-source hash and requested ABIs before using/publishing
it. Version updates must invalidate stale SDKs rather than silently reuse them.

For media ports, prefer `CRunApp.openHFile()` for Fusion embedded files and
OBB/platform resolution. Keep ContentResolver URI grants and Android scoped-storage
semantics intact; do not invent filesystem paths for document URIs or assume a
Windows path exists on-device. Share source materialization/AES code, retain files
until native readers close, and scope caches through native application storage.

When two extensions share an audio backend, ship one shared native runtime and
matching Java/vendor bindings. Do not replace a process-wide music callback when
the other extension needs Mixer X music streams. FFmpeg's PCM chunk/effect channels
can coexist with SDL_Mixer's music streams, following Windows SMI. Verify device
ownership, independent pause/closure, seek queue invalidation, audio-clock progress
and DSP tail drain together; ensure the overlapping files in both export ZIPs match.

## Counterexample: a working Android rewrite is not a shared-core port

In the initial FFMpeg Android implementation, the existing
`Extensions/_3rdLib/FFMpeg.h`, `FFMpegAdapter` factory and Windows `CopyAdapter`
architecture were left outside the Android build. A new
`Extensions/FFMpeg/Android/cpp/Player` implemented its own demux/decode scheduling,
queues, seek, draining and clocks. Java/JNI/rendering wrappers and extensive
playback tests made that backend functional, but the portable logic existed in
parallel and could diverge when the Windows implementation changed. Matching ACE
IDs, FFmpeg versions and observable test cases did not satisfy the requested
architecture. This was an implementation mistake, not a necessary Android limit.

Do not repeat that approach or describe it as completed architectural parity.
Windows CNG/D3D/Fusion dependencies justify isolating those dependencies; they do
not justify rewriting the surrounding portable playback core. Moving the duplicate
engine to a directory named "shared" also does not fix this if Windows continues
executing the old implementation.

Before implementation and before declaring a port complete:

1. Inventory the existing C++ core, adaptor interfaces/factories, policies and
dependencies. Explain which original modules Android will actually compile and
call, and which specific platform APIs require replacement.
2. Reuse/refactor the existing core. Add platform adaptors through its established
interfaces and factories; guard platform headers/implementations with macros.
Preserve Windows call compatibility. Share portable fixes and regression tests.
3. Review the real call chains and build source lists for both platforms. Check
for duplicate decode loops, state machines, seek/EOF handling, clocks and
channel/cache policies hidden behind different filenames or languages.
4. Validate shared C++ behavior as well as platform integration. Passing Android
unit tests or an independent APK does not establish reuse or Windows parity.
Report any independently reimplemented behavior explicitly, with a concrete
platform reason; do not claim a stronger result than the code demonstrates.

For FFMpeg, the intended direction is a common C++ playback core using the existing
`FFMpegAdapter`/`CopyAdapter` design, with MediaCodec/GLES platform implementations
and native Fusion Java bindings. Encrypter remains deferred until its agreed
refactoring/resumption point; this counterexample does not authorize its port.
59 changes: 59 additions & 0 deletions .agents/skills/execution-environment-recovery/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
---
name: execution-environment-recovery
description: Recover from transient execution-environment, network or server disconnections with bounded increasing-delay retries and verified resumption.
---

# Execution environment recovery

Applies to managed execution environments on any host. This is independent of
Android and iOS exporter workflows; it does not change their platform procedures.

## Bounded backoff

When an execution tool disconnects, its transport fails, or an environment
unexpectedly becomes unavailable during authorized work:

1. Briefly tell the user that execution was interrupted and recovery is being
attempted. Keep the task active; a first transient failure is not the final
failure report.
2. After the initial failure, allow up to **three retries** with increasing
delays. Approximately **10, 50 and 100 seconds** is the default example, not
a rigid timing requirement. Adapt to explicit recovery status or retry hints;
small jitter is appropriate to avoid synchronized retries.
3. Use orchestration-side timers when executor-side waiting is unavailable.
Keep each blocking wait at most 60 seconds: split a longer delay, such as
100 seconds, into two 50-second waits. Continue concise progress updates at
least every 60 seconds. Useful independent work can occupy the delay; do
not add another full delay if that work already consumed it.
4. Probe availability with a harmless read before resuming. Use the dedicated
environment-start wait tool only when an environment is actually marked
`starting` and its tool contract permits it; it is not a general wait for an
unavailable environment. Connector availability alone does not establish
executor recovery.
5. On recovery, inspect the workspace, branch/HEAD, pending changes and relevant
outputs. Resume from the interrupted step, preserving prior authorization,
task scope and validated results.

## Verify effects before retrying writes

A lost response does not establish that an operation failed. Before retrying a
commit, push, upload, deployment or external write, check its actual result.
Read the remote ref or object/status as appropriate; resume only the missing
step. Use compare-and-swap or an exact force-with-lease when updating a branch.
Do not blindly replay non-idempotent operations or duplicate already completed
work. Never describe a local result as published until the remote is verified.

## Stop conditions and reporting

After all three retries fail, stop dependent execution and report the remaining
blocker. State what completed, what was verified, what remains unpublished or
unfinished, and the relevant local/remote revision when available. Do not claim
success from a prepared result alone. Mark remote work as pending only after
recovery attempts are exhausted, unless an immediate status correction is needed
to prevent a misleading publication claim.

Backoff is for plausibly transient infrastructure failures. Do not retry clear
permission/policy rejections, invalid requests, deterministic build failures or
an explicitly destroyed environment as though they were network collisions.
Honor an explicit user stop or retry limit. Do not request renewed permission
for work already authorized merely because its environment disconnected.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,12 @@ Extensions/Android_Extensions.lnk

# Generated FFmpeg SDK and dependency downloads
/Extensions/_3rdLib/FFmpeg-sdk/
/Extensions/_3rdLib/FFmpeg-sdk-Android/
/Extensions/_3rdLib/FFmpeg-build/out/

# Generated OpenSSL SDK, vcpkg checkout and dependency caches
/Extensions/_3rdLib/Encrypt-sdk/
/Extensions/_3rdLib/Encrypt-build/out/

# Platform crypto SDK uses the Windows version pin with Android binaries.
/Extensions/_3rdLib/Encrypt-sdk-Android/
Loading
Loading