Skip to content

Drop the hexagon symbol-count tripwire from the payload gate - #902

Merged
a-ghorbani merged 1 commit into
mainfrom
feature/TASK-20260908-1600
Sep 8, 2026
Merged

Drop the hexagon symbol-count tripwire from the payload gate#902
a-ghorbani merged 1 commit into
mainfrom
feature/TASK-20260908-1600

Conversation

@pocketpal-dev-team

Copy link
Copy Markdown
Contributor

Summary

Removes expectedMatchCount — the hexagon .dynsym count — from the Android payload gate. mustExport carries the symbol rule alone.

Why

The count asserted no capability. Against the regression it exists for (#858, Android release builds shipping without the Hexagon backend), #859's own A/B experiment shows mustExport failing on its own:

no SDK SDK
lm_ggml_backend_hexagon_reg absent present
lm_ggml_backend_is_hexagon absent present
hexagon .dynsym matches 0 16
libraries / DSP assets 12/12, 4/4 12/12, 4/4

The broken APK satisfied every library and asset rule — that is why a symbol rule is needed at all — but mustExport alone rejects it. The count reported 0, which is the same fact stated twice. Every other layer is independent of it: the SDK tarball and consumed-subset digests, the build-mode assertion, the variant-allowlist log check, required libraries, and the DSP assets verified as EM_QDSP6 objects.

What the count detected was change, not brokenness, and its only remedy was re-declaring the number. So an upgrade that legitimately grew the symbol surface and one that gutted it were resolved the same way — by editing the number until CI was green. A gate whose documented remedy is to silence it does not merely cost a CI round-trip; it launders the change as reviewed.

It first came due on #901 (llama.rn 0.13.0-rc.10.13.0-rc.3), where the count moved 16 → 28 and nothing was wrong: ggml-hexagon.h is byte-identical across the two versions, and the growth is upstream adding multi-device allreduce, async tensor copy and events, and a host buffer type — hexagon-named identifiers in the vendored tree go 139 → 187.

16 was never an invariant. It was the measurement that proved #858, promoted to a permanent expectation on the strength of reading the same number twice. That promotion is the defect being removed.

What changes

  • scripts/android-payload-manifest.json — the rule and its countRule note are gone.
  • scripts/verify-android-payload.js — enforcement removed. The manifest validator is stricter, not looser: mustExport must now be present, a list, and non-empty, rather than being satisfiable by a count. The weakening shape the validator exists to refuse — emptying mustExport during a dependency bump — has one fewer way to pass.
  • The report keeps the llama.rn version stamp, moved onto the .dynsym entries line, so the evidence still names the build it read.

What is unchanged

Every layer that asserts a capability: SDK provisioning digests, rnllamaBuildFromSource, the allowlist reaching gradle, required libraries, DSP assets and their ELF machine, and both mustExport symbols being defined. The incident this gate was built for is still caught, by the rule that caught it.

Verification

npx jest scripts/ — 11 suites, 205 tests, all passing. ESLint and Prettier clean on the changed files.

The negative tests are the point, so they were kept honest rather than trimmed: a build with neither named symbol defined still fails, as an APK and as an AAB; a symbol present only as an undefined import still fails; and the manifest-weakening matrix still refuses a rule that asserts nothing, one whose mustExport was emptied, and one naming no library. The three cases that existed only to police expectedMatchCount's own shape are gone with it.

Not covered by CI: no built artifact was checked locally. The claim rests on #859's calibration of the reader against llvm-nm -D on two real artifacts, which this change does not touch.

Generated by PocketPal Dev Team

The count asserted no capability. Against the regression it was built
for -- Android release builds shipping without the Hexagon backend,
issue #858 -- mustExport fails on its own: PR #859's own A/B shows both
named symbols absent in the no-SDK run, with all 12 libraries and all 4
DSP assets present. Every other layer is independent of it: the SDK
tarball and consumed-subset digests, the build-mode assertion, the
variant-allowlist log check, required libraries, and the DSP assets
verified as EM_QDSP6 objects.

What it detected was change, not brokenness, and its only remedy was
re-declaring the number -- so an upgrade that legitimately grew the
symbol surface was indistinguishable from one that shrank it, and both
were resolved by editing the number. It first came due on the llama.rn
0.13.0-rc.3 bump, where the hexagon surface grew from 16 to 28 because
upstream added multi-device allreduce, async tensor copy and events.
Nothing was wrong.

mustExport now carries the rule alone, and the manifest validator
requires it to be present and non-empty rather than accepting a count
as an alternative demand, so the weakening shape the validator exists
to refuse has one fewer way to pass.

The report keeps the llama.rn version stamp, moved onto the .dynsym
entries line, so the evidence still says which build it read.
@a-ghorbani
a-ghorbani merged commit b527751 into main Sep 8, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant