merge origin/master into llm-recording-and-analysis

both sides independently fixed the same three bugs, so each one had to pick a
winner rather than keep both implementations.

extractor encoding: master's recordableValue in worker.go wins over ours in
marshal.go, since master's is pinned by extractor_encoding_test.go and ours had
no tests. our error semantics stay: encodeExtractorValue still returns an error
instead of nil, so an extractor cannot vanish from the trace silently.

apply errors: only the residual generic branch takes master's unconfirmed copy,
where the device may have committed the action before the call failed. the
finer branches that know nothing was dispatched keep lastAction = nil, and our
actionSkipReason taxonomy stays alongside master's held/skippedVerification.

selector matching: our matchAttr with matchSelectorKind wins over master's
match, since ours also handles idPrefix. matchSelector now calls it, which git
did not flag as a conflict and left calling a function our side had deleted.

the ltl doc comment takes master's correction: an unbounded eventually that
never fires IS violated at run end.
This commit is contained in:
pj committed 2026-08-16 18:10:55 +05:30
commit 6e85cac8b3
130 files changed
+12772 -1617

No files matched your search

+10 -2
View File
@@ -12,7 +12,8 @@
// descPrefix:<prefix> - starts-with on content-desc / accessibilityText
//
// Object selectors (multi-attribute AND, element-scoped or global):
// { attr: value, ... } - all key/value pairs must match; substring / boolean semantics
// { attr: value, ... } - all key/value pairs must match, each key resolved by
// the same rule its string form above uses
//
// Path queries (global scan only, string form):
// <sel> > <sel> > ... - each segment matched within subtree of previous match
@@ -445,7 +446,14 @@ func matchAttr(element *Element, attr, value string) bool {
return false
}
// matchSelector returns true when all filters in sel match the element (AND semantics).
// matchSelector returns true when all filters in sel match the element (AND
// semantics). Each filter goes through matchAttr, the same rule the string form
// resolves a "kind:value" segment by, so {id: "Submit"} and "id:Submit" can
// never resolve to different elements. Reaching the attribute map directly here
// made the object form skip the kind arms entirely: id, desc and descPrefix
// name no attribute any producer writes, so those keys matched NOTHING through
// an object selector while the string form matched, and every property over the
// missing element passed vacuously.
func matchSelector(element *Element, sel Selector) bool {
for _, f := range sel.Filters {
if !matchAttr(element, f.Attr, f.Value) {