Skip to main content

Widget And Action Correctness Contract

Stage 1 established the inventory and enforcement boundary. Stage 2 uses the same audit to migrate findings without weakening compatibility contracts. Run the inventory with:

.\gradlew.bat auditStageOneCorrectness --console plain

The task parses and semantically analyzes src/main/java with the JDK compiler tree API and writes build/reports/correctness/stage-1-audit.json. The report records its source HEAD revision, audited-source/config dirty state, audited-source/config content digest, category and disposition totals, and a source-located entry for each audited declaration or use. Unrelated worktree changes do not affect auditedSourceDirty.

Categories

CategoryWhat it recordsStage 2 direction
widget_constantWidgetInfoExtended declarations and referencesLocal WidgetID aliases are resolved to authoritative or raw terminals; live widgets remain a separate gate
raw_widget_lookupTwo-argument getWidget(group, child) callsUse a canonical constant or typed target
packed_widget_idPacked (group << 16) | child expressionsUse a canonical constant or typed target
semantic_first_matchWidgets.search(...).first() selectionResolve zero, one, and multiple matches explicitly
legacy_widget_actionLegacy void widget-action queuesPreserve compatibility facades; migrate modern consumers around them
direct_widget_packetDirect widget packet callsRoute modern callers through InteractionApi.actions.*
deprecated_apiDeprecated declarations and WidgetInfo importsIsolate compatibility code under a legacy boundary
deprecated_useCompiler-resolved uses of deprecated declarationsMove modern consumers to supported contracts
ambiguous_action_matchFirst-match ActionResolver.findActionIndex(...) callsRequire a unique match or explicit op index
optimistic_resultInteractionResult.success(...) callsDistinguish dispatch from observed confirmation
action_helper, query_helper, widget_helperShared helper declarationsPreserve ownership while migration proceeds

Boundaries And Dispositions

  • modern entries are current production paths. Raw, optimistic, or deprecated uses remain visible as finding entries until a later stage migrates them.
  • legacy entries are physically under a legacy package. Location alone does not suppress a finding.
  • exception entries must match a committed allowlist item by category and path plus an exact stable finding ID, symbol, or narrow source locator. Whole-file and wildcard suppression is rejected.
  • disposition records source status: compliant, finding, or allowed. liveEvidenceStatus is independent and identifies canonical widget constants that still require controlled RuneLite evidence on the target revision.

Legacy compatibility facades are not migrated in place. Their packet contract is an unconditional MousePackets.queueClickPacket() immediately followed by the widget packet call. Modern result-aware actions preserve the same ordering through WidgetActions: check ActionPacer, queue the click through MousePackets.tryQueueClickPacket(), issue the widget packet, verify its PacketSendResult, then record the action. A paced or failed click queues no widget packet. A failed widget packet returns PACKET_NOT_QUEUED, does not record pacing, and is never reported as DISPATCHED.

Client-owned widget operations that depend on local listeners require the native RuneLite menu-action path. Side-tab actions therefore call Client.menuAction(...) through WidgetActions.interactViaMenuAction(...) so the client applies the tab change before it queues the corresponding widget packet. Bank closing invokes RuneLite's native WIDGET_CLOSE menu action because the dynamic close control is client-local and its direct widget packet does not close the interface. Server-owned widget operations continue to use direct packet dispatch.

Caution

A successful audit task proves that the inventory was generated, the allowlist is structurally valid, and no finding was added outside the reviewed structural fingerprint baseline. It does not prove that existing findings are migrated or that revision-sensitive widgets work in the live client.

Allowlist Contract

src/test/resources/audits/stage-one-correctness-allowlist.json is reserved for proven exceptions. Each entry includes an ID, category, exact path, an exact stable finding ID, symbol, or narrow locator, plus reason, authority, reviewed revision, and removal condition. Every entry must match exactly one compiler-AST finding.

The same resource stores the reviewed finding count and digest for each category. Stable finding IDs use structural owner, category, subcategory, normalized evidence, and deterministic occurrence rather than source line. Blank-line movement therefore does not change the reviewed identity.

Stage 2 Migration Progress

Result-aware packet helpers use ActionResolver.findUniqueActionIndex(...). NPC, object, player, ground-item, and modern widget actions fail closed when zero or multiple slots match. ActionResolver.hasAction(...) retains any-match query semantics. The legacy void WidgetPackets.queueWidgetAction(...) method intentionally retains first-match resolution so unchanged compatibility callers still issue their widget packet after the queued click.

The modern semantic-first-match, two-argument widget lookup, ad hoc packed-ID, direct widget-packet, and optimistic-result slices are resolved or explicitly classified. Remaining compatibility first-match sites and intentional low-level or direct-packet boundaries use exact allowlist entries. The protected BankInteraction, BankInventoryInteraction, and InventoryInteraction facades remain source- equivalent to their compatibility baseline; regression tests lock all 27 immediate mouse-then-widget pairs and prohibit routing them through WidgetActions.

Modern bank, trade, break-handler, utility-event, world, deposit-box, combat, prayer, production, Grand Exchange, and walker widget operations use the paced result-aware boundary. Walker and Break Handler state machines retry PACED and transient results instead of advancing or treating them as terminal.

InteractionResult distinguishes accepted dispatch from observed state: DISPATCHED means the packet or action was accepted for dispatch, while CONFIRMED means the requested state or postcondition was observed. Legacy SUCCESS remains supported for compatibility. accepted() includes all three statuses; succeeded() is limited to legacy success or observed confirmation, so a dispatched action cannot by itself complete a workflow step.

The current aggregate report contains 1,260 entries: 428 compliant entries, 78 exact allowed exceptions, and 754 intentional residual findings. Those residuals consist only of 727 revision-sensitive widget_constant findings and the 27 protected legacy-facade packet calls. Another 43 compliant widget constants also await live evidence, for 770 live-pending entries in total. The report contains zero unreviewed optimistic_result findings; six exact completion checks are allowed only where DISPATCHED is handled before the check or accounting deliberately requires completion. These residual gates do not represent an incomplete modern migration, and an automated audit pass does not award LIVE_VERIFIED for revision-sensitive widgets.