The dataset viewer is not available for this split.
Error code: StreamingRowsError
Exception: CastError
Message: Couldn't cast
schema_version: string
model_key: string
condition: string
expected_entries: int64
result_count: int64
success_count: int64
success_rate: double
missing: list<item: null>
child 0, item: null
unexpected: list<item: null>
child 0, item: null
duplicates: list<item: null>
child 0, item: null
issues: list<item: null>
child 0, item: null
valid: bool
valid_reason: string
entry_count: int64
rows: list<item: struct<source_model: string, task_name: string, success: bool, score: double, exception: (... 92 chars omitted)
child 0, item: struct<source_model: string, task_name: string, success: bool, score: double, exception: null, recov (... 80 chars omitted)
child 0, source_model: string
child 1, task_name: string
child 2, success: bool
child 3, score: double
child 4, exception: null
child 5, recovery_max_steps: int64
child 6, budget_reference_replayed_actions: int64
child 7, episode: string
source_model_key: null
note: string
to
{'schema_version': Value('string'), 'model_key': Value('string'), 'condition': Value('string'), 'expected_entries': Value('int64'), 'result_count': Value('int64'), 'success_count': Value('int64'), 'success_rate': Value('float64'), 'missing': List(Value('null')), 'unexpected': List(Value('null')), 'duplicates': List(Value('null')), 'issues': List(Value('null')), 'valid': Value('bool'), 'valid_reason': Value('string'), 'rows': List({'source_model': Value('string'), 'task_name': Value('string'), 'success': Value('bool'), 'score': Value('float64'), 'exception': Value('null'), 'recovery_max_steps': Value('int64'), 'budget_reference_replayed_actions': Value('int64'), 'episode': Value('string')}), 'source_model_key': Value('string'), 'note': Value('string')}
because column names don't match
Traceback: Traceback (most recent call last):
File "/src/services/worker/src/worker/utils.py", line 149, in get_rows_or_raise
return get_rows(
dataset=dataset,
...<4 lines>...
column_names=column_names,
)
File "/src/libs/libcommon/src/libcommon/utils.py", line 272, in decorator
return func(*args, **kwargs)
File "/src/services/worker/src/worker/utils.py", line 129, in get_rows
rows_plus_one = list(itertools.islice(safe_iter(ds, dataset=dataset), rows_max_number + 1))
File "/src/services/worker/src/worker/utils.py", line 544, in safe_iter
yield from ds.decode(False) if ds.features else ds
File "/usr/local/lib/python3.14/site-packages/datasets/iterable_dataset.py", line 3052, in __iter__
for key, example in ex_iterable:
^^^^^^^^^^^
File "/usr/local/lib/python3.14/site-packages/datasets/iterable_dataset.py", line 2559, in __iter__
for key, pa_table in self._iter_arrow():
~~~~~~~~~~~~~~~~^^
File "/usr/local/lib/python3.14/site-packages/datasets/iterable_dataset.py", line 2584, in _iter_arrow
for key, pa_table in self.ex_iterable._iter_arrow():
~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^
File "/usr/local/lib/python3.14/site-packages/datasets/iterable_dataset.py", line 558, in _iter_arrow
for key, pa_table in iterator:
^^^^^^^^
File "/usr/local/lib/python3.14/site-packages/datasets/iterable_dataset.py", line 440, in _iter_arrow
for key, pa_table in self.generate_tables_fn(**gen_kwags):
~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^
File "/usr/local/lib/python3.14/site-packages/datasets/packaged_modules/json/json.py", line 361, in _generate_tables
self._cast_table(pa_table, json_field_paths=json_field_paths),
~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.14/site-packages/datasets/packaged_modules/json/json.py", line 132, in _cast_table
pa_table = table_cast(pa_table, self.info.features.arrow_schema)
File "/usr/local/lib/python3.14/site-packages/datasets/table.py", line 2381, in table_cast
return cast_table_to_schema(table, schema)
File "/usr/local/lib/python3.14/site-packages/datasets/table.py", line 2309, in cast_table_to_schema
raise CastError(
...<3 lines>...
)
datasets.table.CastError: Couldn't cast
schema_version: string
model_key: string
condition: string
expected_entries: int64
result_count: int64
success_count: int64
success_rate: double
missing: list<item: null>
child 0, item: null
unexpected: list<item: null>
child 0, item: null
duplicates: list<item: null>
child 0, item: null
issues: list<item: null>
child 0, item: null
valid: bool
valid_reason: string
entry_count: int64
rows: list<item: struct<source_model: string, task_name: string, success: bool, score: double, exception: (... 92 chars omitted)
child 0, item: struct<source_model: string, task_name: string, success: bool, score: double, exception: null, recov (... 80 chars omitted)
child 0, source_model: string
child 1, task_name: string
child 2, success: bool
child 3, score: double
child 4, exception: null
child 5, recovery_max_steps: int64
child 6, budget_reference_replayed_actions: int64
child 7, episode: string
source_model_key: null
note: string
to
{'schema_version': Value('string'), 'model_key': Value('string'), 'condition': Value('string'), 'expected_entries': Value('int64'), 'result_count': Value('int64'), 'success_count': Value('int64'), 'success_rate': Value('float64'), 'missing': List(Value('null')), 'unexpected': List(Value('null')), 'duplicates': List(Value('null')), 'issues': List(Value('null')), 'valid': Value('bool'), 'valid_reason': Value('string'), 'rows': List({'source_model': Value('string'), 'task_name': Value('string'), 'success': Value('bool'), 'score': Value('float64'), 'exception': Value('null'), 'recovery_max_steps': Value('int64'), 'budget_reference_replayed_actions': Value('int64'), 'episode': Value('string')}), 'source_model_key': Value('string'), 'note': Value('string')}
because column names don't matchNeed help to make the dataset viewer work? Make sure to review how to configure the dataset viewer, and open a discussion for direct support.
- Layout
- Episode payload parquet
- Condition names β paper notation
- Scoring: two rules, and why
- Denominators
scripts/score_tables.py- KNOWN ISSUES
- 1. LLM-verified
t: 21 cells, 20 contract issues each, on the same 5 episodes - 2.
llm_verified/replay_312/t/gemma_31b_improved_think_on: 142/312 rollout errors - 3.
t_plus_132B cells: run before thereplay_utils.py:747fix - 4. Older cells carry outcomes only
- 5.
MarkorTranscribeVideofails structurally in the positive arm - 6. Five LLM-verified subset cells: header and rows disagree
- 7.
t_plus_1exists as two different sets β checkentry_set - 8. Reproducibility caveats that are not defects in any single cell
- 1. LLM-verified
- Related repositories
- Citation
AndroidFlux β Evaluation Output
Scored results for every number reported in the AndroidFlux paper, in one place.
This repository holds results only: success counts, per-episode outcomes, and the provenance needed to tell one run from another. It does not hold the benchmark inputs (source trajectories, VM snapshots, screenshots) β those live in the input repositories listed under Related repositories.
Every table in the paper can be regenerated from this repository alone:
python3 scripts/reproduce.py --check # recount every cell and compare with its header
python3 scripts/score_tables.py --root . --all # regenerate the paper tables
How to run them is described in scripts/README.md.
If a number printed by that script disagrees with the paper, the script is right and the paper has a typo. Known disagreements are listed in KNOWN ISSUES.
Layout
androidworld/<run>/ sr_summary.json per_task.json MANIFEST.json
human_verified/
replay_65/<condition>/<model>/ validation.json per_episode.json MANIFEST.json
episodes/<task>/
snapshot_15/<condition>/<model>/ "
llm_verified/
replay_312/<condition>/<model>/ "
t_plus_1/ entry-set definition of the LLM-verified t+1 arms (no cells)
supplementary/
t_plus_1_65/t_plus_1/<model>/ 65-entry t+1 cells (not used in the tables; see KNOWN ISSUES 7)
llm_verified_subsets/t/<model>_lv177 177- and 254-episode subset cells (see KNOWN ISSUES 6)
replicate_runs/ an independent replicate of one cell, for run-to-run variation
_inputs/manifests/ the run manifests the cells were produced from
scripts/score_tables.py
scripts/scratch_80_tasks.txt
README.md
<condition> is one of:
clean t_minus_5 t_minus_3 t_minus_1 t t_plus_1_pos t_plus_1_neg
Not every (axis, subset, condition, model) combination exists. score_tables.py
reports missing cells rather than silently skipping them.
Per-cell files
| File | Contents |
|---|---|
validation.json |
The scored cell: success_count, result_count, per-episode rows, contract-check issues, valid. |
per_episode.json |
One row per episode: shard, episode, source_model, task_name, is_successful, success, exception. |
MANIFEST.json |
Provenance: which run this is, what it was run against, what is wrong with it. See MANIFEST_SCHEMA.md. |
episodes-<i>-of-<n>.parquet |
Per-episode artifacts, one row per episode, payload in a files column. See Episode payload parquet. Cells whose run left outcomes only carry no such file β see KNOWN ISSUES #4. |
Two notes on the layout:
- Shards are an execution detail and are flattened away. The producing harness writes
shards/shard_0N/<task>/; here that isepisodes/<task>/. The original shard assignment is preserved invalidation.json:shard_countsand inper_episode.json:shard. app_data_cache/is excluded. It is emulator scratch state, not a result.
Episode payload parquet
Screenshots and step records ship as parquet, not as loose files: a cell holds thousands of PNGs, and uploading them one by one runs into the hosting API's request limits.
<axis>/<subset>/<condition>/<model>/episodes-00000-of-00001.parquet
One row per episode. Identity columns (cell_id, axis, subset, condition,
model_key, task_name, entry_id, source_model), the verdict as scored
(is_successful, score), and the artifacts in
files list<struct<name: string, kind: string, size_bytes: int64, sha256: string, bytes: binary>>
kind: screenshot_png Β· step_json Β· jsonl Β· action_audit_png Β· action_audit_json
Β· replay_debug_png Β· replay_debug_json Β· episode_json
name is the path the file had inside its episode directory, so the original tree can be
restored exactly. sha256 is taken over the bytes AS PUBLISHED β absolute paths and machine
names are scrubbed out of the text payloads first β so verification passes on the files as
they stand here.
What the format guarantees
- Every file is under 5GB, the hosting limit. A cell that would exceed it is split, and
the parts are named
-00000-of-00003,-00001-of-00003, β¦ The reader reads a cell as the union of its parts. - Every row group is at most 240MB. This is what makes partial reads cheap: a reader pulling one episode loads that group, not the file. It is the row group size, not the file size, that bounds the memory a reader needs. The largest single part here is 3.3GB.
app_data_cache/is excluded (emulator scratch state), and so are preserved partial runs β directories whose name begins with a dot (.stopped_*,.enginedead_*_partial). Only the leading character decides, so a cell likeqwen3vl_8b.dup1is unaffected.
entry_id is the episode directory name, verbatim. Its shape follows the campaign that
produced the cell, and it is not normalised β normalising it would break the correspondence
with the source tree:
| Subset | entry_id |
|---|---|
snapshot_15 |
<source_model>__<task>__t<NNNN> |
replay_65 |
<source_model>__<task> |
t_plus_1_pos / t_plus_1_neg |
<source_model>__<task>__tplus1_positive / __tplus1_negative |
The key is (cell_id, entry_id); it only has to be unique within a cell. Polarity is read
from the path, never from the row. In a t_plus_1 cell the stored condition is
t_plus_1 for both arms, and the two arms share the same (source_model, task_name) pairs β
only cell_id (and, for these campaigns, the entry_id suffix) separates them.
Reading it
python3 scripts/read_episodes.py <cell>/episodes-00000-of-00001.parquet # what is in here
python3 scripts/read_episodes.py <cell>/episodes-00000-of-00001.parquet --extract <entry_id> --out DIR
python3 scripts/read_episodes.py <cell>/episodes-00000-of-00001.parquet --verify # recompute every sha256
Condition names β paper notation
The paper uses compact math notation; the repository uses filesystem-safe names.
| Repository | Paper | Meaning |
|---|---|---|
clean |
Scratch | Task run from step 0. No replay, no primed history. |
t_minus_5 |
$t{-}5_{\text{rep}}$ | Replay through $t_{\mathrm{err}}-5$, then the policy acts. |
t_minus_3 |
$t{-}3_{\text{rep}}$ | Replay through $t_{\mathrm{err}}-3$. |
t_minus_1 |
$t{-}1_{\text{rep}}$ | Replay through $t_{\mathrm{err}}-1$. |
t (in replay_65, replay_312) |
$t_{\text{rep}}$ | Replay through the first-error step. |
t (in snapshot_15) |
$t_{\text{snap}}$ | VM snapshot restored to the first-error step. |
t_plus_1_pos |
$t{+}1^{+}$ | Inject the first action of a recovery rollout that eventually succeeded. |
t_plus_1_neg |
$t{+}1^{-}$ | Inject the first action of a recovery rollout that eventually failed. |
Superscripts describe the outcome of the rollout that supplied the action, not a claim that the single action is sufficient.
Three paper columns are derived and have no directory of their own:
| Paper | Definition |
|---|---|
| $t_{\text{all}}$ | $(\texttt{replay_65/t} + \texttt{snapshot_15/t}) / 80$ |
| $t{+}1_{\text{all}}$ | $(\texttt{t_plus_1_pos} + \texttt{t_plus_1_neg}) / 104$ |
Scratch (80) in tab:aw-vs-recovery |
The same AndroidWorld 116-task run restricted to the 80 recovery tasks in scripts/scratch_80_tasks.txt |
The 80 recovery tasks are exactly replay_65 (65) βͺ snapshot_15 (15); the two sets are
disjoint and their union is a subset of the AndroidWorld 116.
Source-name mapping, for anyone cross-referencing the older input repositories:
| Repository | Original condition name |
|---|---|
clean |
clean / snapshot_clean |
t_minus_1 |
pre_error_t_minus_1 / snapshot_pre_error_t_minus_1 |
t_minus_3, t_minus_5 |
pre_error_t_minus_3, pre_error_t_minus_5 |
t |
error_t / snapshot_error_t |
t_plus_1_pos, t_plus_1_neg |
t_plus_1_positive, t_plus_1_negative |
Scoring: two rules, and why
The two result formats in this repository are scored differently. This is deliberate, and the difference is visible only where a partial score of 0.5 exists.
| Format | Field read | Success means | 0.5 counts as |
|---|---|---|---|
validation.json (human-verified, LLM-verified) |
rows[].score |
score > 0 |
success |
sr_summary.json (AndroidWorld) |
["total"]["successes"] |
score >= 1 |
failure |
Why they differ: the two numbers are produced by two different harnesses, and each paper
table reports the figure its own harness reported. AndroidWorld's sr_summary.json counts
a task correct only on full completion, which is the convention that benchmark's published
numbers use. The recovery harness records partial credit and treats any progress as a
recovery success, which is the quantity the recovery tables are about. Rescoring one to the
other's rule would make our numbers incomparable with published AndroidWorld results.
score_tables.py applies the rule that matches the file it is reading, and every
MANIFEST.json records both success (its own rule) and success_alt_rule (the other
rule) so the gap is never hidden. Worked examples, all from cells in this repository:
human_verified/replay_65/t/qwen3vl_2b scores {0.0:57, 0.5:1, 1.0:7}
score>0 -> 8/65 = 12.3 <- paper
score>=1 -> 7/65 = 10.8
human_verified/replay_65/t_plus_1_pos/ma3_32b scores {0.0:29, 0.5:1, 1.0:22}
score>0 -> 23/52 = 44.2 <- paper
score>=1 -> 22/52 = 42.3
androidworld/ma3_7b_4env_gen1 score points total 78.5
score>=1 -> 78/116 = 67.2 <- paper
score>0 -> 79/116 = 68.1
Two further traps that cost us real time:
- Read
d["total"]["successes"], not the top-level key and notscore_points.score_pointsis the sum of partial scores (78.5 above), not a count. - Recompute from
rows, never trust the header. Five LLM-verified subset cells carry aresult_countandsuccess_ratefrom before a denominator correction whilerowsis already corrected β three different numbers in one file. See KNOWN ISSUES #6.score_tables.pyrecomputes fromrowsand asserts agreement with the header, failing loudly on mismatch.
Denominators
Denominators are fixed constants, not derived from how many result rows happen to be present. A cell with a missing episode is a defect to be reported, not a smaller denominator.
| Axis | Subset | Conditions | n |
|---|---|---|---|
| AndroidWorld | aw_116 |
β | 116 |
| AndroidWorld | aw_116 restricted |
Scratch (80) | 80 |
| Human-verified | replay_65 |
clean, t_minus_5, t_minus_3, t_minus_1, t |
65 |
| Human-verified | replay_65 |
t_plus_1_pos, t_plus_1_neg |
52 |
| Human-verified | snapshot_15 |
clean, t_minus_1, t |
15 |
| LLM-verified | replay_312 |
clean, t |
312 |
| β | derived | $t_{\text{all}}$ | 80 |
| β | derived | $t{+}1_{\text{all}}$ | 104 |
AndroidWorld is 116 including the 6 exception episodes. They are failures, not exclusions.
$t{-}3$ and $t{-}5$ also have denominator 65. When the source trajectory is too short to admit the requested offset, the task is retained and that condition is mapped to a clean start β it is not dropped. This keeps all conditions paired over the same 65 cases, at the cost of mixing true offset starts with clean starts:
| Condition | Offset | Feasible | Clipped | Feasible share |
|---|---|---|---|---|
t_minus_1 |
1 | 65 | 0 | 100.0% |
t_minus_3 |
3 | 39 | 26 | 60.0% |
t_minus_5 |
5 | 21 | 44 | 32.3% |
The feasible-only figures are carried per cell in validation.json:t_minus_n and in
MANIFEST.json:score.t_minus_n. The paper reports the 65-case aggregate; treat the
feasible-restricted score as a secondary diagnostic, and never compare a 65-aggregate
against a feasible-only figure.
scripts/score_tables.py
# every paper table
python scripts/score_tables.py --all
# one table
python scripts/score_tables.py --table tab:hvlvcomparison
python scripts/score_tables.py --table tab:aw-vs-recovery
python scripts/score_tables.py --table tab:instreason
# one cell, with the full derivation printed
python scripts/score_tables.py --cell human_verified/replay_65/t_plus_1_pos/ma3_32b --explain
# machine-readable
python scripts/score_tables.py --all --format json > tables.json
python scripts/score_tables.py --all --format latex
# fail the build if any cell disagrees with its MANIFEST, or any manifest is incomplete
python scripts/score_tables.py --verify
--explain prints the numerator, denominator, scoring rule, score histogram, and the
MANIFEST.json fields that identify the run:
human_verified/replay_65/t_plus_1_pos/ma3_32b
rule score > 0 (validation.json:rows[].score)
scores 0.0 x29 0.5 x1 1.0 x22
success 23 / 52 = 44.23 % -> tab:hvlvcomparison t+1^+
alt rule 22 / 52 = 42.31 % (score >= 1)
valid false issues: memory_prefix 14, start_state_contract 14, +4
accepted true
input rev Gyubeum/AndroidFlux_tplus1_inputs @ 0360552
known issues coord_mode_from_source_traj (affects score)
memory_prefix_short_by_one (affects score)
--verify is the gate to run before publishing: it recomputes every cell from rows,
checks it against the header fields and against MANIFEST.json, checks that denominators
match the table above, and checks that every cell has a manifest. It exits non-zero on any
disagreement.
Identifying a cell
Do not identify a cell by model_key, and do not identify it by its score. Both fail:
model_key is not unique. Across the 321 published cells, five keys are shared by more
than one model directory. In four of the five, the variant suffix is silently dropped:
model_key |
directories that share it |
|---|---|
gemma_31b_improved |
gemma_31b_improved, gemma_31b_improved_think_on |
gemini_3_0_flash_preview_qwen_prompt |
gemini_3_0_flash_preview_qwen_prompt, ..._think_on |
gemma_4_31b_it_0716_improved_m3a |
gemma_4_31b_it_0716_improved_m3a, gemma_4_31b_it_improved_m3a_think_off |
mobile_run_gf30 |
gemini_3_0_flash_preview_qwen_prompt, ..._think_off |
guiowl15_8b_think |
guiowl15_8b_think, yemo_guiowl15_8b_think |
So gemma_31b_improved_think_on (148/312) and gemma_31b_improved (139/312) are two
different cells reporting the same model_key β and thinking-enabled and thinking-disabled
rows of tab:instreason are exactly the pair that key cannot distinguish.
dir_name is not reliable either. Five directories carry different model_key values
in different cells, so the mapping is not even a function in the other direction:
| directory | model_key values observed |
|---|---|
gemini_3_0_flash_preview_qwen_prompt |
gemini_3_0_flash_preview_qwen_prompt, mobile_run_gf30 |
gemini_3_0_flash_preview_qwen_prompt_think_off |
gemini_3_0_flash_preview_qwen_prompt_think_off, mobile_run_gf30 |
gemma_4_31b_it_improved_m3a_think_off |
gemma_4_31b_it_0716_improved_m3a, gemma_4_31b_it_improved_m3a_think_off |
mai_ui_8b |
mai_ui_8b, mai_ui_8b_history |
mai_ui_2b |
mai_ui_2b, mai_ui_2b_history |
Matching on the score value is worse still β it crosses model boundaries. Searching for a
15/52 cell finds t_plus_1_pos/qwen3vl_2b when the target was t_plus_1_neg/qwen3vl_4b.
Fix the (model, condition) pair first, then read the value; never the reverse.
The identifier is therefore the path, MANIFEST.json:identity.cell_id. dir_name and
model_key are both recorded, separately, precisely because they disagree with each other.
KNOWN ISSUES
These are defects we have confirmed and chosen to publish rather than quietly fix. Each is
recorded per cell in MANIFEST.json:status.known_issues[] with an affects_score flag, so
a consumer can filter programmatically.
1. LLM-verified t: 21 cells, 20 contract issues each, on the same 5 episodes
All 21 full-set LLM-verified t cells were run before the 2026-09-07 metadata
regeneration. Each reports valid: false with an identical signature:
executed_prefix 5 + memory_prefix 5 + skipped_replay 5 + history_or_budget_contract 5 = 20
That is four checks failing on the same five episodes β the "20" is an issue count, not a cell count or an episode count. The five failing episodes are the same five in all 21 cells, with zero variation in the key set:
gemini_flash_4env | FilesMoveFile
gemini_flash_4env_gen2 | ExpenseAddMultipleFromGallery
qwen3vl_2b_4env | Do_I_have_any_events_October_28...
qwen3vl_30b_a3b_4env | What_incomplete_tasks...
qwen3vl_8b_think_4env_gen1 | RecipeDeleteSingleWithRecipeWithNoise
Because the failing episodes are identical across models, this is an input-side defect:
row-to-row comparison within the table still holds, absolute values do not. Some cells carry
additional per-model issues on top of the shared 20 (agent_rollout_error,
shard_result_sizes); those are model- or run-specific and are listed per cell in
MANIFEST.json:status.issues_by_type.
Do not read valid: false here as "a minor 5-episode problem." The underlying defect is
larger than what validation.json catches: replay_metadata's memory.steps is empty for
103 of 312 entries (33.0%), concentrated in four source trajectories
(gemma_31b_m3a_gemma31b_improved_0716_4env 49, gemma_31b_m3a_gemma_31b_m3a_4env 42,
ui_tars_7b_sft gen3/gen4 6 each). The contract check catches five of them. The two numbers
come from different checks and are not comparable.
The cause for the Gemma sources is confirmed: the metadata's image_name values
(step_000.png) do not match the files on disk (screenshot_N.png), so every image
reference resolves to nothing and memory.steps is built empty. The original trajectories
are intact β this is a metadata build failure, not missing data, and it is regenerable
without GPU. The ui_tars sources have a different, still-unidentified cause: their images
are all present.
Checking this yourself: read len(m["memory"]["steps"]). len(m["memory"]) is the number
of keys (8) and will look fine.
2. llm_verified/replay_312/t/gemma_31b_improved_think_on: 142/312 rollout errors
45.5% of this cell's episodes ended in agent_rollout_error. Its reported success rate
of 47.44% is a measurement failure, not a performance figure.
This cell is marked canonical: false in its manifest. It is published because the paper's
tab:instreason LLM-verified Reasoning column (47.4) rests on it, and suppressing the cell
would hide that.
3. t_plus_1 32B cells: run before the replay_utils.py:747 fix
Six cells β t_plus_1_pos and t_plus_1_neg for gui_owl_32b, ma3_32b, and
qwen3vl_32b β were produced on a tree that predates the replay_utils.py:747 fix
(extract_action_payload was missing its AndroidWorld-native action_type branch, so t+1
candidate actions could not be extracted). All six report issues: 32:
negative arm memory_prefix 16 + start_state_contract 16
positive arm memory_prefix 14 + start_state_contract 14 (+ 4 unrelated)
The failing episode keys are identical across all three models within an arm (16/16 and
14/14 intersection), which places the cause on the input side, not the model. The mechanism
is a memory prefix one step short of expected; 11 of the 13 start-state contract checks
still pass, with only history_prefix_exact and history_last_is_live_target failing.
Root cause, recorded in the cells' own notes: the input revision
(AndroidFlux_tplus1_inputs @ 03605523705a59dce1e1b3b0ba5e30881cfa5e5f) predates the
candidate_coord_mode split, so coord_mode was assigned from the source trajectory rather
than from the recovery action. The evidence is that the positive and negative arms have an
identical coord_mode distribution (pixel 14, normalized_1000 38) when they should differ.
This applies uniformly to every model evaluated on that input.
gemma_31b_improved/t_plus_1_neg used the same input revision but had its 16 zero-length
episodes re-run after applying commit ff747bd; the first-pass output is preserved in that
cell rather than deleted. Whether those 16 episodes are the same 16 is unverified.
4. Older cells carry outcomes only
Cells produced before the artifact-retention change have no intermediate step records and
no screenshots β episodes/<task>/ holds the episode outcome and nothing else.
MANIFEST.json:status.artifacts reports per cell which of action_audit, agent_rollout,
and replay_debug are present. Analyses that need step-level evidence must filter on those
flags; there is no way to recover the missing artifacts from these runs.
5. MarkorTranscribeVideo fails structurally in the positive arm
actuation.py has no input_text branch, so the action dispatches as
long_press(None, None). This reproduces across five models, occurs only in the
t_plus_1_pos arm, and is unrelated to agent capability. The episode is retained and the
denominator stays 52 β it is a harness limitation counted as a failure.
6. Five LLM-verified subset cells: header and rows disagree
gemini_flash_lv177, gemini_flash_lv254, guiowl15_8b_lv177, ma3_7b_lv177, and
ui_tars_1_5_7b_lv177 were published between a denominator correction and a header fix.
Their rows are correct; their result_count and success_rate header fields are from the
pre-correction 312-entry state, and the success_rate field does not even equal
success_count / result_count within the same file. Three different numbers are readable
from one file:
| Cell | Header | success_rate field |
Recomputed from rows |
|---|---|---|---|
guiowl15_8b_lv177 |
81/312 | 31.41% | 45.76% (81/177) |
gemini_flash_lv177 |
90/312 | 49.36% | 50.85% (90/177) |
gemini_flash_lv254 |
131/312 | 49.36% | 51.57% (131/254) |
ma3_7b_lv177 |
85/312 | 45.83% | 48.02% (85/177) |
ui_tars_1_5_7b_lv177 |
43/312 | 19.23% | 24.29% (43/177) |
score_tables.py uses the rows recomputation and --verify fails on these until the
headers are corrected.
7. t_plus_1 exists as two different sets β check entry_set
There is a t_plus_1 condition with 65 entries, produced on a different machine, that is
distinct from the 52-task t_plus_1_pos / t_plus_1_neg arms. The paper tables use the
52-task arms. Seven models (gui_owl_7b, ma3_7b, mai_ui_8b, qwen3vl_2b,
qwen3vl_4b, qwen3vl_8b, ui_tars_7b_sft) have both, so a lookup by model and condition
name alone will pick the wrong one. Split on MANIFEST.json:identity.entry_set, or on
validation.json:expected_entries (65 vs 52).
8. Reproducibility caveats that are not defects in any single cell
- Rollout failures are counted in the denominator. An episode where the model emitted
unparseable output is a failure, not an exclusion. This is intentional β excluding them
would reward models that fail loudly β but it means a cell's score mixes "the agent tried
and failed" with "the measurement failed."
issues_by_typein each manifest lets you separate the two. - Rollout failures re-run selectively would bias results. Where the cause is the model's
own output (truncated tool-call JSON, hallucinated element indices), re-rolling until a
cell passes advantages that cell. Those episodes are accepted as failures and the reason
is recorded in
accepted_rationale. Only harness and infrastructure failures were re-run. mobilerun_gemini_flash_3.0does not honour the step budget. MobileRun runs its own agent loop at a flat 160 framework steps and ignores per-taskrecovery_max_steps(inference/android_world_agents.py:95). 23/65 episodes exceeded their task budget and 11 of those succeeded. Applying the standard rule gives 31/65 rather than the recorded 42/65. These cells carrystep_budget_policy: mobilerun_flat_160; do not difference them against harness-enforced rows. For comparison, across the other 26replay_65/tcells there were 568 budget-exhausted episodes and 0 successes.- Serving reachability was observed differently from different hosts at the same time, and flipped within 13 minutes on one host. Reachability claims in the manifests are qualified by host and time for that reason.
Related repositories
| Repository | Contents |
|---|---|
Gyubeum/AndroidFlux_Failure_Recovery_Eval_Agent |
Human-verified and LLM-verified inputs, VM snapshots, and the $t{+}1$ candidate-action inputs (human_verified_set/t_plus_1, llm_verified_set/t_plus_1) |
Cells whose MANIFEST.json names Gyubeum/AndroidFlux_tplus1_inputs as their input were
run from an earlier packaging of the human-verified $t{+}1$ inputs; that content is now
human_verified_set/t_plus_1 of the input repository.
9. Seven cells carry header fields corrected during migration
Five LLM-verified subset cells (supplementary/llm_verified_subsets/t/*) were published with
result_count and expected_entries of 312 and a success_rate copied from the parent
312-entry run; they now read 177 / 254 and the rate recomputed from their own rows. One
human-verified cell (replay_65/t_minus_1/guiowl15_8b_think) had a success_rate field that
did not equal success_count / result_count (both are 0). No success_count changed. The
original values are kept in each cell's MANIFEST.json:corrections.
Citation
See the paper. If you use a single cell rather than a table, cite the cell by its
MANIFEST.json:identity.cell_id and quote its known_issues β several cells in this
repository are published because they are defective and a paper number rests on them.
- Downloads last month
- 151