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
| Category | What it records | Stage 2 direction |
|---|---|---|
widget_constant | WidgetInfoExtended declarations and references | Local WidgetID aliases are resolved to authoritative or raw terminals; live widgets remain a separate gate |
raw_widget_lookup | Two-argument getWidget(group, child) calls | Use a canonical constant or typed target |
packed_widget_id | Packed (group << 16) | child expressions | Use a canonical constant or typed target |
semantic_first_match | Widgets.search(...).first() selection | Resolve zero, one, and multiple matches explicitly |
legacy_widget_action | Legacy void widget-action queues | Preserve compatibility facades; migrate modern consumers around them |
direct_widget_packet | Direct widget packet calls | Route modern callers through InteractionApi.actions.* |
deprecated_api | Deprecated declarations and WidgetInfo imports | Isolate compatibility code under a legacy boundary |
deprecated_use | Compiler-resolved uses of deprecated declarations | Move modern consumers to supported contracts |
ambiguous_action_match | First-match ActionResolver.findActionIndex(...) calls | Require a unique match or explicit op index |
optimistic_result | InteractionResult.success(...) calls | Distinguish dispatch from observed confirmation |
action_helper, query_helper, widget_helper | Shared helper declarations | Preserve ownership while migration proceeds |
Boundaries And Dispositions
modernentries are current production paths. Raw, optimistic, or deprecated uses remain visible asfindingentries until a later stage migrates them.legacyentries are physically under alegacypackage. Location alone does not suppress a finding.exceptionentries 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.dispositionrecords source status:compliant,finding, orallowed.liveEvidenceStatusis 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.
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.