Agent skill

Traversing The Semantics Tree

by skydoves in skydoves/android-testing-skills

A skill your agent uses to navigate from one Compose semantics node to its relatives via onParent, onChildren, onChild, onChildAt, onSibling, onSiblings, onAncestors, plus the collection helpers…

Apache-2.0Auto-check passedMobile

Install Traversing The Semantics Tree

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

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

GitHub CLI
$ gh skill install skydoves/android-testing-skills traversing-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/finders/traversing-the-semantics-tree .claude/skills/traversing-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
traversing-the-semantics-tree
GitHub stars
333
Token cost
~3.3k tokens
SKILL.md length
901 words
Files
1
Skills in repo
50
Repo updated
First seen
Licence
Apache-2.0

At a glance

A skill your agent uses to navigate from one Compose semantics node to its relatives via onParent, onChildren, onChild, onChildAt, onSibling, onSiblings, onAncestors, plus the collection helpers…

  • Navigate from one Compose semantics node to its relatives via onParent
  • SKILL.md covers When to use this skill, When NOT to use this skill, Prerequisites and Workflow, plus 4 more sections
  • Instructions only: no scripts, shell commands, URLs or credentials in SKILL.md
  • Plus the collection helpers onFirst

What it does

Traversing The Semantics Tree is an agent skill from skydoves/android-testing-skills. Use this skill to navigate from one Compose semantics node to its relatives via onParent, onChildren, onChild, onChildAt, onSibling, onSiblings, onAncestors, plus the collection helpers onFirst, onLast, filter, filterToOne, and the [index] operator. Covers when to traverse vs when to add a stable testTag, the LazyColumn/LazyRow caveat (only currently composed children appear), the absence of a singular onAncestor, and the sticky useUnmergedTree flag across navigation. Use when the developer mentions onChildren…

Its SKILL.md is about 3.3k 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

  • Navigate from one Compose semantics node to its relatives via onParent
  • Plus the collection helpers onFirst
  • The [index] operator
  • The developer mentions onChildren

Example prompts

  • “/traversing-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

    No scripts in the folder and no shell commands in SKILL.md (its code samples are kotlin).

    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

Traversing The Semantics Tree loads about 3.3k tokens when it runs. Until then it costs about 217 tokens; SKILL.md has 901 words of instructions outside code blocks.

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

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). 901 words, ~3,256 tokens.

Download SKILL.mdSave it as .claude/skills/traversing-the-semantics-tree/SKILL.md (or your agent's skills folder).
name
traversing-the-semantics-tree
description
Use this skill to navigate from one Compose semantics node to its relatives via `onParent`, `onChildren`, `onChild`, `onChildAt`, `onSibling`, `onSiblings`, `onAncestors`, plus the collection helpers `onFirst`, `onLast`, `filter`, `filterToOne`, and the `[index]` operator. Covers when to traverse vs when to add a stable `testTag`, the LazyColumn/LazyRow caveat (only currently composed children appear), the absence of a singular `onAncestor`, and the sticky `useUnmergedTree` flag across navigation. Use when the developer mentions `onChildren`, `onChild`, `onParent`, `onSiblings`, `onAncestors`, `filterToOne`, `onFirst`, `onLast`, brittle child-index chains, or asks how to find the second child of a Row, the parent of a Text, or any sibling of a node. If the developer is dot-chaining navigation through a layout, use this skill.
license
Apache-2.0. See LICENSE for complete terms.
metadata.author
Jaewoong Eum (skydoves)
metadata.keywords
jetpack-compose, ui-testing, onChildren, onParent, onAncestors, filterToOne, onFirst, onLast, semantics-traversal, lazy-column-test

Traversing the Semantics Tree — When a Single Finder Won't Reach the Node

The single-finder shortcuts (onNodeWithTag, etc.) cover ~95% of test queries. The remainder need tree navigation: "the parent of this Text", "the third child of this Row", "any sibling that is enabled". This skill maps those navigation operators, calls out the LazyColumn snapshot caveat, and shows when traversal is the wrong tool.

When to use this skill

  • The target node has no stable testTag and one cannot be added to production (third-party composable, dynamically generated children).
  • The test verifies structural relationships ("the second child of the row is the icon").
  • The developer chains .onChildren()[i].onChildAt(j) and wants to know whether that is the right shape.
  • The developer mentions onChildren, onChild, onParent, onSibling, onSiblings, onAncestors, filterToOne, onFirst, onLast, or [index].
  • A LazyColumn test misses items because they are off-screen.

When NOT to use this skill

  • A Modifier.testTag(...) could be added to the target node — adding a tag is almost always cleaner than a traversal chain. See ../finding-nodes-by-tag-text-content/SKILL.md.
  • The relationship is "anywhere above" / "anywhere below" — prefer the matcher-based hasAnyAncestor / hasAnyDescendant (see ../composing-semantics-matchers/SKILL.md).
  • The query is about a LazyColumn item by key — use performScrollToKey instead (see ../../actions/clicking-and-scrolling/SKILL.md, ../../patterns/testing-lazy-lists/SKILL.md).

Prerequisites

  • A working ComposeTestRule / ComposeUiTest. See ../../setup/configuring-test-dependencies/SKILL.md.
  • Familiarity with the merged vs unmerged tree distinction. See ../finding-nodes-by-tag-text-content/SKILL.md.

Workflow

  • 1. Pick the navigator by relationship type. Each operator returns either a SemanticsNodeInteraction (single — fails on 0 or >1) or a SemanticsNodeInteractionCollection (plural — never fails on 0).

    From a single node, go to …APIReturnsFile:line
    parentonParent()singleSelectors.kt:36-42
    exactly one childonChild()singleSelectors.kt:71-77
    child at an indexonChildAt(index)singleSelectors.kt:85
    all currently-composed childrenonChildren()collectionSelectors.kt:53-59
    exactly one siblingonSibling()singleSelectors.kt:119-125
    all siblingsonSiblings()collectionSelectors.kt:101-107
    every ancestor up to rootonAncestors()collectionSelectors.kt:140-146
    From a collection, narrow to …APIReturnsFile:line
    firstonFirst() (= [0])singleSelectors.kt:156-158
    lastonLast()singleSelectors.kt:168-170
    nth[index]singleSemanticsNodeInteraction.kt:261-267
    filter to a sub-collectionfilter(matcher)collectionSelectors.kt:178-186
    filter to exactly onefilterToOne(matcher)singleSelectors.kt:198-206
  • 2. Do not look for onAncestor (singular) — it does not exist. The API surface ships only onAncestors() plural (Selectors.kt:140-146). It returns [parent, grandparent, …, root] in that order. To assert "the immediate parent satisfies X", use onParent().assert(matcherX) or the hasParent(matcherX) predicate.

  • 3. Remember the useUnmergedTree flag is sticky. Every navigator carries the same useUnmergedTree value as the source SemanticsNodeInteraction (SemanticsNodeInteraction.kt:42-49; each Selectors.kt constructor passes useUnmergedTree through unchanged). A finder created with onNodeWithTag(tag, useUnmergedTree = true) keeps the flag on across onChild/onParent/filter. Skydoves hot take #2: default merged, flip to unmerged only when targeting composition detail.

  • 4. onChildren() is a snapshot at invocation time. It returns nodes "currently present in the semantic tree" (Selectors.kt:46-52). For a LazyColumn or LazyRow only the on-screen items appear. To reach an off-screen item, scroll first with performScrollToIndex / performScrollToKey / performScrollToNode (see ../../actions/clicking-and-scrolling/SKILL.md).

  • 5. Re-finding by tag usually beats navigating. If the tree shape might change between Compose versions or under translation rotation, a stable tag on the target node is more durable than a [2].onChildAt(0) chain. Add the tag in production. Skydoves hot take #1.

  • 6. Use filterToOne(matcher) when the count assertion is implicit. filterToOne throws on 0 or >1 matches at fetch time, the same way onNode(matcher) does (Selectors.kt:198-206). It is the collection-narrowing analogue of onNode(matcher).

Patterns

Pattern: prefer a tag over a deep [index] chain
kotlin
// WRONG
@Test
fun second_avatar_isVisible() {
    rule.setContent { ProfileGrid(profiles = profiles) }
    rule.onNodeWithTag("ProfileGrid")
        .onChildren()[2]
        .onChildren()[0]
        .assertIsDisplayed()
}
// WRONG because: layout shuffling (a header inserted, an extra wrapper, a Spacer added)
// silently changes which node is being asserted. The test passes for the wrong reason.
kotlin
// RIGHT
// production:
@Composable
fun ProfileGrid(profiles: List<Profile>) {
    LazyColumn(modifier = Modifier.testTag(ProfileGridTag)) {
        itemsIndexed(profiles) { index, profile ->
            Row(modifier = Modifier.testTag("$AvatarTagPrefix$index")) {
                Avatar(profile, modifier = Modifier.testTag("$AvatarImageTagPrefix$index"))
            }
        }
    }
}

// test:
rule.onNodeWithTag("$AvatarImageTagPrefix${1}").assertIsDisplayed()
Show full SKILL.md (356 more words)Show less
Pattern: traversal when no tag is available
kotlin
@Test
fun row_third_child_isIcon() {
    rule.setContent {
        Row(modifier = Modifier.testTag("toolbar")) {
            Text("Title")
            Spacer(Modifier.weight(1f))
            Icon(Icons.Default.Share, contentDescription = "Share")
        }
    }

    rule.onNodeWithTag("toolbar")
        .onChildren()
        .assertCountEquals(3)

    rule.onNodeWithTag("toolbar")
        .onChildAt(2)
        .assertContentDescriptionEquals("Share")
}

onChildAt(index) is exactly onChildren()[index] (Selectors.kt:85). Both fail if the index is out of range or if the resolved node count is not exactly 1 at the leaf.

Pattern: filterToOne instead of [i]
kotlin
// WRONG
rule.onAllNodesWithTag(RowTag).onChildren().filter(hasClickAction())[0]
    .assertHasClickAction()
// WRONG because: indexing into a filtered collection silently passes when the filter returns
// many. The test asserts only "at least one is clickable", not "exactly one".
kotlin
// RIGHT
rule.onAllNodesWithTag(RowTag).onChildren()
    .filterToOne(hasClickAction())
    .assertHasClickAction()
Pattern: assert the parent role from a known child
kotlin
@Test
fun submitText_isInsideEnabledButton() {
    rule.setContent {
        Button(onClick = {}, modifier = Modifier.testTag("submit"), enabled = true) {
            Text("Submit", modifier = Modifier.testTag("submitLabel"))
        }
    }

    rule.onNodeWithTag("submitLabel", useUnmergedTree = true)
        .onParent()                       // sticky: stays unmerged
        .assertIsEnabled()
        .assertHasClickAction()
}

The useUnmergedTree = true set on the inner Text finder propagates to onParent() automatically — no need to repeat it.

Pattern: onAncestors() for the chain to root
kotlin
@Test
fun confirmButton_isInsideDialog() {
    rule.setContent { ConfirmDialog() }

    rule.onNodeWithTag(ConfirmButtonTag)
        .onAncestors()
        .filterToOne(isDialog())
        .assertExists()
}

PREFERRED: rule.onNode(hasTestTag(ConfirmButtonTag) and hasAnyAncestor(isDialog())).assertExists() — same intent in one matcher. See ../composing-semantics-matchers/SKILL.md.

Pattern: LazyColumn — only on-screen children appear
kotlin
// WRONG
rule.onNodeWithTag(ListTag).onChildren().assertCountEquals(1000)
// WRONG because: onChildren() returns a snapshot of currently composed children. A LazyColumn
// only composes the visible viewport plus a small prefetch buffer, so the count is window-sized,
// not data-set-sized.
kotlin
// RIGHT — assert the visible count, OR scroll first then assert by tag
rule.onNodeWithTag(ListTag).performScrollToIndex(999)
rule.onNodeWithTag("$ItemTagPrefix${999}").assertIsDisplayed()

See ../../patterns/testing-lazy-lists/SKILL.md for the full LazyColumn workflow.

Pattern: onSibling() to assert "the row's other half"
kotlin
@Test
fun checkbox_label_isPresent() {
    rule.setContent {
        Row {
            Checkbox(checked = true, onCheckedChange = {},
                modifier = Modifier.testTag("agreeBox"))
            Text("I agree", modifier = Modifier.testTag("agreeLabel"))
        }
    }

    rule.onNodeWithTag("agreeBox")
        .onSibling()
        .assertTextEquals("I agree")
}

onSibling() requires exactly one sibling (Selectors.kt:114-125). For multiple siblings use onSiblings() plus filterToOne(...).

Mandatory rules

  • MUST prefer adding a Modifier.testTag(...) to the target node over a multi-step traversal chain. Skydoves hot take #1.
  • MUST use filterToOne(matcher) over filter(matcher).onFirst() when the contract is "exactly one match"; the former throws on >1, the latter silently picks the first.
  • MUST NOT assume onChildren() returns the full data set for a LazyColumn / LazyRow — it returns the currently-composed snapshot only. Scroll first with performScrollToIndex / performScrollToKey.
  • MUST NOT look up a singular onAncestor — only onAncestors() plural exists. Use onParent() for the immediate parent or hasParent(matcher) / hasAnyAncestor(matcher) as predicates.
  • MUST remember the useUnmergedTree flag is sticky across onChild/onParent/filter. Skydoves hot take #2.
  • PREFERRED: for "anywhere above/below" relationships, replace traversal chains with hasAnyAncestor / hasAnyDescendant from ../composing-semantics-matchers/SKILL.md.

Verification

  • Every traversal chain has at most one navigation step, OR the deeper chain is justified by a comment naming the missing tag.
  • No onChildren()[i] chain spans a LazyColumn / LazyRow boundary without a preceding performScrollTo*.
  • No onAncestor (singular) usage — only onParent, onAncestors, or hasAnyAncestor.
  • filterToOne is used wherever the contract is "exactly one match"; filter().onFirst() is replaced.
  • useUnmergedTree = true on a chain root is intentional — there is a comment or matcher reason for it.
  • ./gradlew :app:connectedDebugAndroidTest or :app:testDebugUnitTest passes.

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/finders/traversing-the-semantics-tree of skydoves/android-testing-skills.

Open the folder on GitHubat commit 8665ed5

Compare with similar skills

Traversing 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.

Traversing The Semantics Tree compared with similar skills
SkillStarsUsed inTokensAuto-checkLicenceRepo updated
Traversing The Semantics Tree this skillskydoves/android-testing-skills333—~3.3kAutomated safety check: PassApache-2.0
React Native Best Practicesvercel-labs/openreview1.7k17 repos~1.1kAutomated safety check: PassMIT
Swiftui Protwostraws/SwiftUI-Agent-Skill5.2k2 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.2k 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 today
    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 Traversing The Semantics Tree

What does Traversing The Semantics Tree do?

A skill your agent uses to navigate from one Compose semantics node to its relatives via onParent, onChildren, onChild, onChildAt, onSibling, onSiblings, onAncestors, plus the collection helpers…. Traversing The Semantics Tree is an agent skill from skydoves/android-testing-skills. Use this skill to navigate from one Compose semantics node to its relatives via onParent, onChildren, onChild, onChildAt, onSibling, onSiblings, onAncestors, plus the collection helpers onFirst, onLast, filter, filterToOne, and the [index] operator.

When should I use Traversing The Semantics Tree?

Traversing The Semantics Tree fits situations like: navigate from one Compose semantics node to its relatives via onParent; plus the collection helpers onFirst; the [index] operator; the developer mentions onChildren.

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

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

How do I install Traversing The Semantics Tree in Codex?

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

Can I use Traversing 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 traversing-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/traversing-the-semantics-tree, .gemini/skills/traversing-the-semantics-tree, .github/skills/traversing-the-semantics-tree and .opencode/skills/traversing-the-semantics-tree in your project.

What does Traversing The Semantics Tree need to run?

SKILL.md names no scripts, command-line tools or credentials: Traversing The Semantics Tree is instructions for the agent only.

Does Traversing 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 Traversing 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 Traversing The Semantics Tree use?

Traversing 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 Traversing The Semantics Tree use?

About 3.3k tokens (SKILL.md is roughly 13k 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 Traversing The Semantics Tree?

Skills that share tags, products or a category with Traversing The Semantics Tree: React Native Best Practices (vercel-labs/openreview, 1.7k stars), Swiftui Pro (twostraws/SwiftUI-Agent-Skill, 5.2k 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 Traversing 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.