Skip to content

feat: progressive skeleton traversal with ref-rooted drill-down#20

Open
lahfir wants to merge 9 commits intomainfrom
feat/progressive-skeleton-traversal
Open

feat: progressive skeleton traversal with ref-rooted drill-down#20
lahfir wants to merge 9 commits intomainfrom
feat/progressive-skeleton-traversal

Conversation

@lahfir
Copy link
Owner

@lahfir lahfir commented Mar 10, 2026

Summary

  • Add --skeleton flag: shallow 3-level overview with children_count on truncated containers, giving AI agents a high-level map before drilling into specific regions
  • Add --root @ref flag: start traversal from a previously-discovered ref instead of window root, with scoped invalidation (only that ref's subtree refs are replaced on re-drill)
  • Named or described containers at skeleton boundaries receive refs as drill-down targets (Electron compat: description from aria-label)
  • 78-96% token savings for dense Electron apps (Slack skeleton: ~3.6KB vs ~17.3KB full)

New files

  • crates/core/src/ref_alloc.rs — shared ref-allocation helpers extracted from snapshot.rs
  • crates/core/src/snapshot_ref.rs — drill-down logic with DrillDownConfig and scoped invalidation

Key changes

  • get_subtree() added to PlatformAdapter trait (default not_supported)
  • macOS count_children() uses raw CFArrayGetCount without materializing AXElement wrappers
  • RefMap write-side 1MB size check prevents runaway accumulation
  • STALE_REF error suggestion mentions --skeleton
  • Skill docs and phases.md updated

Test plan

  • 36 unit tests pass (32 core + 4 macOS), including 4 new skeleton-specific tests
  • Zero clippy warnings, fmt check passes
  • All files under 400 LOC
  • Release binary builds (1.1MB)
  • End-to-end smoke tested: skeleton + drill-down on Finder and Slack
  • --root @ref --surface menu returns INVALID_ARGS
  • Scoped invalidation verified: re-drill replaces only matching refs

Note

Medium Risk
Changes snapshot output shape and ref persistence behavior, and adds a new adapter API (get_subtree) that affects platform implementations. While mostly additive, it can impact downstream consumers expecting prior snapshot/refmap semantics.

Overview
Adds progressive skeleton traversal to snapshot: --skeleton clamps traversal depth (to 3) and emits children_count on truncated containers, while --root @ref re-snapshots a previously discovered container’s subtree and only replaces refs for that root (scoped invalidation).

This extends the core model and ref persistence to support drill-down (AccessibilityNode.children_count, RefEntry.root_ref, refmap pruning, and a 1MB write guard), factors shared ref-allocation into ref_alloc.rs, and introduces snapshot_ref::run_from_ref backed by a new PlatformAdapter::get_subtree method (implemented on macOS, including tree-builder updates to count children efficiently). CLI/batch parsing and documentation are updated accordingly, and stale-ref guidance now mentions snapshot --skeleton.

Written by Cursor Bugbot for commit 1ebc865. This will update automatically on new commits. Configure here.

lahfir added 6 commits March 10, 2026 01:59
Add children_count, root_ref, skeleton fields to core types. Add
get_subtree() to PlatformAdapter trait. Add remove_by_root_ref() and
write-side size check to RefMap. No behavioral changes yet.
…down

Add --skeleton and --root flags to snapshot command for token-efficient
accessibility tree exploration. Skeleton mode clamps depth to 3 levels
and annotates truncated containers with children_count, allowing AI
agents to discover regions before drilling into them. Named containers
at skeleton boundaries (via name or description) receive refs as
drill-down targets. The --root flag starts traversal from a previous
ref with scoped invalidation — only refs from that drill-down are
replaced on re-drill.

Key changes:
- New ref_alloc.rs: shared ref helpers (INTERACTIVE_ROLES, actions_for_role,
  ref_entry_from_node, is_collapsible) extracted from snapshot.rs
- New snapshot_ref.rs: drill-down logic with DrillDownConfig, scoped
  invalidation via root_ref tagging on RefEntry
- macOS count_children() uses raw CFArrayGetCount without materializing
  AXElement wrappers for performance at skeleton boundaries
- RefMap write-side size check prevents >1MB files
- Skeleton anchors consider both name and description for Electron compat
Add skeleton traversal as Phase 1 objective P1-O10. Document --skeleton
and --root flags, get_subtree() trait method, new core modules, and
platform-agnostic notes for Phase 2/3. Update risk mitigations and
performance optimizations table.
Update SKILL.md observe-act loop to skeleton-first approach. Add
progressive skeleton traversal as the primary workflow pattern. Update
anti-patterns, key principles, and command quick reference to lead
with --skeleton + --root. Full snapshot remains documented as fallback
for simple apps.
Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Bugbot Free Tier Details

You are on the Bugbot Free tier. On this plan, Bugbot will review limited PRs each billing cycle.

To receive Bugbot reviews on all of your PRs, visit the Cursor dashboard to activate Pro and start your 14-day free trial.

Autofix Details

Bugbot Autofix prepared fixes for both issues found in the latest run.

  • ✅ Fixed: count_children disagrees with copy_children child source
    • Shared the macOS child-attribute selection between counting and traversal so skeleton anchors use the same child source as drill-down.
  • ✅ Fixed: Missing children_count guard in drill-down interactive filter
    • Added the missing children_count guard in drill-down pruning and covered it with a regression test for truncated containers.

cursoragent and others added 2 commits March 10, 2026 09:44
Skeleton snapshots now load the existing refmap and remove only
skeleton-level refs (root_ref: None), preserving drill-down refs
accumulated via --root. Previously, build() always created a fresh
RefMap, breaking the skeleton → drill → act → skeleton(verify) workflow
by wiping all drill-down refs on the verify step.
Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Bugbot Autofix prepared a fix for the issue found in the latest run.

  • ✅ Fixed: Skeleton depth clamp silently truncates drill-down results
    • Snapshot drill-downs now keep the requested max depth even when --skeleton is also set, preventing silent subtree truncation.

args.max_depth.min(3)
} else {
args.max_depth
};
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Skeleton depth clamp silently truncates drill-down results

Medium Severity

When --skeleton and --root @ref are both passed, the effective_depth clamp to min(max_depth, 3) applies unconditionally before the root_ref branch is taken. The clamped opts.max_depth is then sent to get_subtree, which hardcodes skeleton: false. This means the drill-down tree gets silently capped at 3 levels deep with no children_count annotations to explain the truncation — children beyond depth 3 just vanish. The depth clamp should not apply when --root is set, or both flags together should be rejected as invalid.

Additional Locations (1)
Fix in Cursor Fix in Web

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.

2 participants