Agent skill

Printing The Semantics Tree

by skydoves in skydoves/android-testing-skills

A skill your agent uses to diagnose "no node matched", "found N nodes", and "useUnmergedTree" failures by dumping the actual semantics tree with printToLog(tag, maxDepth) and printToString(maxDepth).

Apache-2.0Auto-check passedMobile

Install Printing The Semantics Tree

skills CLI
$ npx skills add skydoves/android-testing-skills --skill printing-the-semantics-tree -a claude-code

Project install by default; add -g for ~/.claude/skills/.

GitHub CLI
$ gh skill install skydoves/android-testing-skills printing-the-semantics-tree --agent claude-code

Project scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).

Manual copy
$ git clone --depth 1 https://github.com/skydoves/android-testing-skills.git skills-src && mkdir -p .claude/skills && cp -r skills-src/compose/debug/printing-the-semantics-tree .claude/skills/printing-the-semantics-tree && rm -rf skills-src

Use ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.

Claude Code skills documentation · loads skills from .claude/skills/

Facts

Skill name
printing-the-semantics-tree
GitHub stars
333
Token cost
~3.1k tokens
SKILL.md length
897 words
Files
1
Skills in repo
50
Repo updated
First seen
Licence
Apache-2.0

At a glance

A skill your agent uses to diagnose "no node matched", "found N nodes", and "useUnmergedTree" failures by dumping the actual semantics tree with printToLog(tag, maxDepth) and printToString(maxDepth).

  • Diagnose no node matched
  • SKILL.md covers When to use this skill, When NOT to use this skill, Prerequisites and Workflow, plus 4 more sections
  • Calls node and adb
  • UseUnmergedTree failures by dumping the actual semantics tree with printToLog(tag

What it does

Printing The Semantics Tree is an agent skill from skydoves/android-testing-skills. Use this skill to diagnose "no node matched", "found N nodes", and "useUnmergedTree" failures by dumping the actual semantics tree with printToLog(tag, maxDepth) and printToString(maxDepth). Covers the output grammar (Node

Its SKILL.md is about 3.1k tokens, which your agent loads only when the skill is triggered. It is a single SKILL.md file with no bundled scripts.

It sits in Mobile. The repository describes itself as: ⚡️ A set of skills for Android testing: Compose UI, AndroidX Test, JVM unit tests, and ADB. The licence is Apache-2.0.

When your agent uses it

  • Diagnose no node matched
  • UseUnmergedTree failures by dumping the actual semantics tree with printToLog(tag
  • MaxDepth) and printToString(maxDepth)

Example prompts

  • “no node matched”
  • “found N nodes”
  • “useUnmergedTree”
  • “/printing-the-semantics-tree”

What it can do on your machine

Read from SKILL.md and the folder at commit 8665ed5. It shows what the files ask for, not the result of running them.

  • Tool permissions

    Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.

    From allowed-tools in the SKILL.md frontmatter.

  • Runs code

    Shell commands in SKILL.md call:

    • node
    • adb

    From the folder's file list and the shell code blocks in SKILL.md.

  • Network

    Links to these hosts (documentation or services it may open):

    • developer.android.com

    From URLs in SKILL.md, links to its own repository left out.

  • Credentials

    Names no API keys, tokens, secrets or passwords.

    From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.

Context cost

Printing The Semantics Tree loads about 3.1k tokens when it runs. Until then it costs about 64 tokens; SKILL.md has 897 words of instructions outside code blocks.

Always · name and description, kept in context so the agent knows when to use it
~64
When it runs · the whole SKILL.md, loaded when a task matches
~3.1k

Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.

Safety

Auto-check passed

The automated check found no risky patterns in SKILL.md.

Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.

SKILL.md

The full file from skydoves/android-testing-skills at commit 8665ed5, republished under its Apache-2.0 licence (© skydoves). 897 words, ~3,068 tokens.

Download SKILL.mdSave it as .claude/skills/printing-the-semantics-tree/SKILL.md (or your agent's skills folder).
name
printing-the-semantics-tree
description
Use this skill to diagnose "no node matched", "found N nodes", and "useUnmergedTree" failures by dumping the actual semantics tree with `printToLog(tag, maxDepth)` and `printToString(maxDepth)`. Covers the output grammar (`Node
license
Apache-2.0. See LICENSE for complete terms.
metadata.author
Jaewoong Eum (skydoves)
metadata.keywords
jetpack-compose, ui-testing, printToLog, printToString, semantics-tree, useUnmergedTree, fetchSemanticsNode, debug-finder, tag-not-found, multiple-nodes-matched

Printing the Semantics Tree — When a Finder Fails, Dump the Tree

onNodeWithTag failures are structural — the tag is wrong, the matcher is on the merged tree but the tag is on a leaf, or the node is genuinely not composed yet. None of those are timing problems, so Thread.sleep does nothing. This skill teaches the agent to diagnose finder failures by dumping the semantics tree with printToLog / printToString and reading the framework's built-in "found in the unmerged tree" hint.

When to use this skill

  • The test fails with "Failed: assertExists. Reason: Expected exactly '1' node but could not find any node that satisfies: …".
  • The test fails with "Reason: Expected exactly '1' node but found 'N' nodes that satisfy: …".
  • The error message ends with "…were found in the unmerged tree. If you really wanted to match against merged tree, use useUnmergedTree = true." and the developer is confused.
  • The developer is writing a custom matcher and needs direct access to a SemanticsNode.
  • A test was fixed by adding Thread.sleep — the underlying problem is a wrong tag, not a timing issue. Replace with this skill's pattern. See skydoves directive #7.

When NOT to use this skill

  • The symptom is timing (animation has not finished, an idling resource is busy). Use ../../synchronization/synchronizing-with-idle/SKILL.md or ../../synchronization/testing-animations-deterministically/SKILL.md.
  • The finder is wrong because the test searches by text instead of by tag. Fix with ../../finders/finding-nodes-by-tag-text-content/SKILL.md (skydoves directive #1).
  • The diagnosis involves accessibility violations rather than node lookup. Use ../enabling-accessibility-checks/SKILL.md.

Prerequisites

  • androidx.compose.ui:ui-test on androidTestImplementation or testImplementation (the API lives in commonMain so it is available in both source sets).
  • An active ComposeTestRule or ComposeUiTest (no extra setup beyond the standard skeleton).

Workflow

  • 1. When a finder fails, dump the merged tree first. Add the line right after rule.setContent { … } (or right before the failing finder).
kotlin
rule.onRoot().printToLog("DEBUG")

The signature is fun SemanticsNodeInteraction.printToLog(tag: String, maxDepth: Int = Int.MAX_VALUE) — full subtree by default for a single-node receiver. Cited at compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/Output.kt:62-69. View the output via adb logcat -s DEBUG.

  • 2. If the merged dump does not show the expected node, dump the unmerged tree.
kotlin
rule.onRoot(useUnmergedTree = true).printToLog("DEBUG")

Inner descendants of merging composables (e.g. an Icon inside a Button) only appear in the unmerged tree.

  • 3. Read the output grammar. The format is fixed by Output.kt:135-296:
Node #<id> at (l=<L>, t=<T>, r=<R>, b=<B>)px, Tag: '<tag>'
  <Sorted ConfigKey> = '<value>'
  ...
  [<FlagKey1>, <FlagKey2>]            // keys whose value is Unit
  Actions = [<ActionKey1>, ...]        // AccessibilityActions and Function values
  MergeDescendants = 'true'            // when isMergingSemanticsOfDescendants
  ClearAndSetSemantics = 'true'        // when isClearingSemantics
   |-Node #<childId> at (...)px        // children indented with " |-"
  • Top line: Node # + id + bounds + (optionally) Tag: '<testTag>'.

  • Config entries are alphabetically sorted by key name.

  • AccessibilityAction and Function<*> values are summarized as Actions = [keyName, …].

  • Unit values (e.g. Disabled, IsTraversalGroup) are summarized as [Disabled, IsTraversalGroup, …].

  • MergeDescendants = 'true' and ClearAndSetSemantics = 'true' flags appear at the bottom of a node's block.

  • 4. Read the framework's built-in hint. When onNode(...) finds zero matches in the merged tree but the same matcher would have matched in the unmerged tree, the error message embeds a "These nodes were found in the unmerged tree" block. Cited at compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/SemanticsNodeInteraction.kt:184-194. When the developer sees that, the fix is to flip useUnmergedTree = true on the failing finder. See ../../finders/finding-nodes-by-tag-text-content/SKILL.md for the merged/unmerged decision.

  • 5. For custom matchers, use fetchSemanticsNode / fetchSemanticsNodes directly. These return the underlying SemanticsNode(s) for direct introspection.

kotlin
val node = rule.onNodeWithTag("title").fetchSemanticsNode()
assertThat(node.config[SemanticsProperties.Text]).isNotEmpty()

val all = rule.onAllNodesWithTag("row").fetchSemanticsNodes(
    atLeastOneRootRequired = true,
    errorMessageOnFail = "expected at least one row",
)

Single-node signature: fun fetchSemanticsNode(errorMessageOnFail: String? = null): SemanticsNode. Collection signature: fun fetchSemanticsNodes(atLeastOneRootRequired: Boolean = true, errorMessageOnFail: String? = null): List<SemanticsNode>.

  • 6. For collection finders, default maxDepth is 0 (no children). Override when the developer wants the full subtree per match.
kotlin
rule.onAllNodesWithTag("row").printToLog("DEBUG", maxDepth = Int.MAX_VALUE)

Cited at Output.kt:83-116.

  • 7. Capture the dump as a String for assertion-based debugging. Useful in CI logs.
kotlin
val tree = rule.onRoot(useUnmergedTree = true).printToString()
println(tree) // or attach to a test report
Show full SKILL.md (347 more words)Show less

Patterns

Pattern: Thread.sleep to "wait for the node" instead of dumping the tree
kotlin
// WRONG
@Test
fun appears() {
    rule.setContent { MyScreen() }
    Thread.sleep(2000)                                    // hide the symptom
    rule.onNodeWithTag("submit").assertIsDisplayed()      // still fails
}
// WRONG because: Thread.sleep desyncs from MainTestClock and does not advance composition.
// If the node is missing, more wall time will not produce it. The cause is structural.
// Skydoves directive #7: Thread.sleep is a smell.
kotlin
// RIGHT
@Test
fun appears() {
    rule.setContent { MyScreen() }
    rule.onRoot(useUnmergedTree = true).printToLog("DEBUG")   // see what's actually present
    rule.onNodeWithTag("submit").assertIsDisplayed()
}
Pattern: "found in the unmerged tree" hint
kotlin
// WRONG — the test ignores the framework hint
@Test
fun iconExists() {
    rule.setContent { Button(onClick = {}) { Icon(Icons.Default.Add, modifier = Modifier.testTag("add-icon")) } }
    rule.onNodeWithTag("add-icon").assertExists()
    // AssertionError: ... 0 matches.
    // These nodes were found in the unmerged tree:
    //   Node #4 ... Tag: 'add-icon'
    // If you really wanted to match against merged tree, use useUnmergedTree = true.
}
// WRONG because: a Button merges descendants. The Icon's tag is collapsed into the Button
// node in the merged tree. The error message names the fix in plain English.
kotlin
// RIGHT
@Test
fun iconExists() {
    rule.setContent { Button(onClick = {}) { Icon(Icons.Default.Add, modifier = Modifier.testTag("add-icon")) } }
    rule.onNodeWithTag("add-icon", useUnmergedTree = true).assertExists()
}
Pattern: ambiguous "found 3 nodes" — disambiguate via dump
kotlin
// WRONG
rule.onNodeWithTag("row").assertIsDisplayed()
// AssertionError: Reason: Expected exactly '1' node but found '3' nodes that satisfy:
// (TestTag = 'row')
kotlin
// RIGHT — dump first, then narrow the matcher
rule.onAllNodesWithTag("row").printToLog("DEBUG", maxDepth = Int.MAX_VALUE)
// Read the dump to find a stable disambiguator (e.g. parent tag, contentDescription),
// then narrow:
rule.onAllNodesWithTag("row")
    .filterToOne(hasAnyAncestor(hasTestTag("section-completed")))
    .assertIsDisplayed()
Pattern: fetchSemanticsNode for custom assertions
kotlin
// RIGHT
@Test
fun rowConfigContainsCustomKey() {
    rule.setContent { /* … */ }
    val node = rule.onNodeWithTag("row").fetchSemanticsNode("missing 'row'")
    val custom: String? = node.config.getOrNull(MyCustomKey)
    assertThat(custom).isEqualTo("expected")
}

Mandatory rules

  • MUST dump the semantics tree with rule.onRoot().printToLog("DEBUG") (or the unmerged variant) the moment a finder fails. The default maxDepth for a single-node receiver is Int.MAX_VALUE, so the full subtree is printed without further configuration. Cited at Output.kt:43, 65.
  • MUST read the framework hint "…were found in the unmerged tree. If you really wanted to match against merged tree, use useUnmergedTree = true." as a directive — flip useUnmergedTree = true on the failing finder. Cited at SemanticsNodeInteraction.kt:184-194.
  • MUST NOT add Thread.sleep(…) to "give the tree time to load". A finder failure is structural; more wall time does not produce the node. Skydoves directive #7. The single legitimate use is screenshot/RenderThread waits.
  • MUST override maxDepth to a non-zero value when calling printToLog on a SemanticsNodeInteractionCollection — the collection default is 0 (no children). Cited at Output.kt:85, 112.
  • PREFERRED: start the diagnosis with the merged tree, then escalate to useUnmergedTree = true only if the expected node is collapsed. Skydoves directive #2: keep useUnmergedTree = false by default.
  • PREFERRED: for custom matchers, prefer fetchSemanticsNode(errorMessageOnFail = "…") so the failure message identifies the test, not just the assertion line.

Verification

  • When a finder fails, the test (or the agent's interactive debugging) emits at least one printToLog("DEBUG") line for the relevant subtree.
  • No Thread.sleep was added to fix a finder problem.
  • If the dump shows the node only under useUnmergedTree = true, the finder has been updated to pass that flag.
  • If multiple matches were found, the finder has been narrowed (via filterToOne, hasAnyAncestor, a parent testTag, etc.) — not by adding sleep or assert-attempts.
  • Custom matchers use fetchSemanticsNode(errorMessageOnFail = "…") with a meaningful diagnostic message.
  • In CI, the test logs include the dump output (developer collects logcat for the test process).

References

© skydoves, Apache-2.0. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file

Files

Just SKILL.md in compose/debug/printing-the-semantics-tree of skydoves/android-testing-skills.

Open the folder on GitHubat commit 8665ed5

Compare with similar skills

Printing The Semantics Tree next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.

Printing The Semantics Tree compared with similar skills
SkillStarsUsed inTokensAuto-checkLicenceRepo updated
Printing The Semantics Tree this skillskydoves/android-testing-skills333—~3.1kAutomated safety check: PassApache-2.0
React Native Best Practicesvercel-labs/openreview1.7k17 repos~1.1kAutomated safety check: PassMIT
Swiftui Protwostraws/SwiftUI-Agent-Skill5.1k2 repos~1.5kAutomated safety check: PassMIT
Kortix Brandkortix-ai/suna20k—~4kAutomated safety check: PassCustom licence
Ip As LogoKartikLabhshetwar/better-shot2.4k1 repos~4.3kAutomated safety check: PassMIT
Compose Multiplatform Patternsmonta-app/ocpp-emulator1805 repos~2kAutomated safety check: PassApache-2.0

Similar skills

  • React Native Best Practices

    vercel-labs/openreview

    Official

    A prioritized rule set for React Native and Expo apps covering list performance, animation, navigation, UI patterns, state, rendering, monorepos and configuration.

    1.7k GitHub starsUsed in 17 repos~1.1k tokens
    MobileAuto-check passed
  • Swiftui Pro

    twostraws/SwiftUI-Agent-Skill

    Comprehensively reviews SwiftUI code for best practices on modern APIs, maintainability, and performance.

    5.1k GitHub starsUsed in 2 repos~1.5k tokens
    MobileAuto-check passed
  • Kortix Brand

    kortix-ai/suna

    Load FIRST for anything that carries the Kortix look or voice: product or mobile UI, copy of any kind, decks, social, images, email, CLI output, anything with the logo, and reviews of these.

    20k GitHub stars~4k tokensUpdated yesterday
    MobileAuto-check passed
  • Ip As Logo

    KartikLabhshetwar/better-shot

    Generate extremely simple, cute, personified square character images with rounded heavy forms, two purposeful character colors, one solid background color, and a dominant lower-corner composition.

    2.4k GitHub starsUsed in 1 repo~4.3k tokens
    MobileAuto-check passed
  • Compose Multiplatform Patterns

    monta-app/ocpp-emulator

    Compose Multiplatform and Jetpack Compose patterns for KMP projects — state management, navigation, theming, performance, and platform-specific UI.

    180 GitHub starsUsed in 5 repos~2k tokens
    MobileAuto-check passed
  • Aso Appstore Screenshots

    adamlyttleapps/claude-skill-aso-appstore-screenshots

    Generate high-converting App Store screenshots by analyzing your app's codebase, discovering core benefits, and creating ASO-optimized screenshot images using Nano Banana Pro.

    1.8k GitHub starsUsed in 1 repo~9.6k tokens
    MobileAuto-check passed

More from skydoves/android-testing-skills

All 50 skills in this repo
  • Asserting Bounds And Dimensions

    skydoves/android-testing-skills

    A skill your agent uses to verify Compose layout measurements from a UI test using assertWidthIsEqualTo, assertHeightIsEqualTo, assertWidthIsAtLeast, assertHeightIsAtLeast…

    333 GitHub stars~3.3k tokensUpdated 4 mo ago
    Auto-check passed
  • Asserting Node State And Text

    skydoves/android-testing-skills

    A skill your agent uses to verify a Compose semantics node's properties from a UI test using assertExists, assertDoesNotExist, assertIsDisplayed, assertIsNotDisplayed, assertIsDeactivated…

    333 GitHub stars~3.6k tokensUpdated 4 mo ago
    Auto-check passed
  • Capturing Preview Screenshots In CI

    skydoves/android-testing-skills

    A skill your agent uses to render every Jetpack Compose @Preview as a screenshot on a real Android device or emulator and publish a browsable HTML catalog from CI.

    333 GitHub stars~4.5k tokensUpdated 4 mo ago
    Auto-check: notes
  • Capturing Screenshots And Screenrecord

    skydoves/android-testing-skills

    A skill your agent uses to capture visual artefacts from a device for test failures, golden image generation, QA repro, and demo videos.

    333 GitHub stars~3.7k tokensUpdated 4 mo ago
    Auto-check passed
  • Choosing Test Rule Vs Runtest

    skydoves/android-testing-skills

    A skill your agent uses to pick the correct Compose UI test entry point.

    333 GitHub stars~4.5k tokensUpdated 4 mo ago
    Auto-check passed
  • Choosing What To Test

    skydoves/android-testing-skills

    A skill your agent uses to pick which behaviors to cover in an Android test suite using Google's five-category state vocabulary plus the explicit "what NOT to test" list from…

    333 GitHub stars~4.6k tokensUpdated 4 mo ago
    Auto-check passed

Categories

Questions about Printing The Semantics Tree

What does Printing The Semantics Tree do?

A skill your agent uses to diagnose "no node matched", "found N nodes", and "useUnmergedTree" failures by dumping the actual semantics tree with printToLog(tag, maxDepth) and printToString(maxDepth). Printing The Semantics Tree is an agent skill from skydoves/android-testing-skills. Use this skill to diagnose "no node matched", "found N nodes", and "useUnmergedTree" failures by dumping the actual semantics tree with printToLog(tag, maxDepth) and printToString(maxDepth).

When should I use Printing The Semantics Tree?

Printing The Semantics Tree fits situations like: diagnose no node matched; useUnmergedTree failures by dumping the actual semantics tree with printToLog(tag; maxDepth) and printToString(maxDepth).

How do I install Printing The Semantics Tree in Claude Code?

Run `npx skills add skydoves/android-testing-skills --skill printing-the-semantics-tree -a claude-code`. Or copy the skill folder (compose/debug/printing-the-semantics-tree in skydoves/android-testing-skills) into .claude/skills/printing-the-semantics-tree in your project. Claude Code loads it when a task matches its description.

How do I install Printing The Semantics Tree in Codex?

Run `npx skills add skydoves/android-testing-skills --skill printing-the-semantics-tree -a codex`. Or copy the skill folder (compose/debug/printing-the-semantics-tree in skydoves/android-testing-skills) into .agents/skills/printing-the-semantics-tree in your project. Codex loads it when a task matches its description.

Can I use Printing The Semantics Tree in Cursor, Gemini CLI or GitHub Copilot?

Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add skydoves/android-testing-skills --skill printing-the-semantics-tree -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/printing-the-semantics-tree, .gemini/skills/printing-the-semantics-tree, .github/skills/printing-the-semantics-tree and .opencode/skills/printing-the-semantics-tree in your project.

What does Printing The Semantics Tree need to run?

Going by SKILL.md and its folder, Printing The Semantics Tree needs the command-line tools its instructions call (node and adb).

Does Printing The Semantics Tree access the network?

SKILL.md names 1 domain. As links in the text: developer.android.com. This is read from the text; nothing was executed.

Is Printing The Semantics Tree safe to install?

Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. Review the folder before installing.

What licence does Printing The Semantics Tree use?

Printing The Semantics Tree is published under the Apache-2.0 licence (declared in SKILL.md). It allows redistribution, so the full SKILL.md is shown on this page.

How many tokens does Printing The Semantics Tree use?

About 3.1k tokens (SKILL.md is roughly 12k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.

What are the alternatives to Printing The Semantics Tree?

Skills that share tags, products or a category with Printing The Semantics Tree: React Native Best Practices (vercel-labs/openreview, 1.7k stars), Swiftui Pro (twostraws/SwiftUI-Agent-Skill, 5.1k stars), Kortix Brand (kortix-ai/suna, 20k stars) and Ip As Logo (KartikLabhshetwar/better-shot, 2.4k stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.

Who maintains Printing The Semantics Tree?

skydoves (a GitHub user) maintains it in skydoves/android-testing-skills, which has 333 GitHub stars. The repository holds 50 skills in this directory. The repository was last updated on May 25, 2026.

Source: skydoves/android-testing-skills on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.