Repository navigation
Tae conversion gates - #440
Merged
Merged
Conversation
HARNESS-GOTCHAS is at 57 entries and CONVERSION-LESSONS ~40. Nobody recalls that mid-debug,
so the same failures get re-derived on device at 1-2 cycles each with the answer already
written down. This reads what the loop already produced and names the likely cause, citing
the gotcha id to go read.
Read-only — it never touches the tree or the device, so it can be run on every failure
without risk. It also encodes the two triage rules people get wrong: scan the FIRST
attempt (later attempts die on leftover state, somewhere later and more confusingly), and
treat status.json as authoritative for pass/fail while the trace explains why.
Eleven rules, all keyed to patterns from real runs: T0 absence assertion that can never
come true (selector matching another surface), T1 arrival check satisfied behind an
overlay, T2 clickAndSync reporting a landed click as failed, T3 mozEnterText's
non-asserting locate, T4 ambiguous Compose match reported as not-found, T5 system overlay
masking as absence, T6 long press behaving as a tap, T7 stale PageStateTracker routing
through a destructive edge, T8 merged-vs-unmerged text miss, T9 arrival timeout, T10 crash
rather than assertion failure.
Hardened against 8 labelled failures from the search batch, each with a cause established
by hand on device: b4/b6-privhist -> A39, b9b -> A40, b13 -> A3/A42, b14d -> A42,
b17b -> A39, b17e -> A22, b10-searchclass -> A37. All 8 now match. Three defects were
found and fixed during that exercise, which is the argument for keeping the corpus:
- it confidently mis-diagnosed b14d as an arrival timeout, because the absence-assertion
rule was keyed to the thrown-exception wording rather than the phrasing the trace
actually logs, and the arrival rule matched the harmless pre-check every navigateToPage
emits. A confidently wrong triage is worse than none.
- a crash was reported as a footnote, so the one failure mode that also fools effverify
printed "no rule matched".
- it counted one "Started try #1" block per test as a retry, marking every passing class
run as retried — the exact mistake CONVERSION-LESSONS warns about.
…(MTE-5770)
Tools and docs are versioned together, since they cite each other by gotcha/lesson id and a
split version would only create skew. CalVer rather than semver: these are internal scripts
read from a checkout, nobody pins them, and the only question anyone asks is "is my copy
older than the thing being discussed".
- VERSION at the tae-conversion root, currently 2026.08.13.
- CHANGELOG.md, curated rather than a commit log: entries are changes a consumer depends on
(a new gate, changed verdict semantics, changed flags), each citing its gotcha ids and Jira
keys so "when did this gate appear" is answerable.
- --version on all 12 tools (10 python, 2 shell), handled before any other arg parsing.
The version lookup deliberately resolves symlinks. These tools are routinely invoked through
another checkout's tools/ dir, where the eff* entries are symlinks into this repo; an
unresolved path would look for VERSION in the wrong repo and report "unknown" in exactly the
situation a staleness check exists for. That symlink layout is also how MTE-5764 stayed
invisible to us while being broken for everyone else, which is the argument for the stamp.
Verified: all 12 tools print a version, and effnext/effverify/efftriage/effcheck/effloop.sh
still behave normally (effloop's no-arg exit 1 is the pre-existing ${1:?} behaviour).
…E-5766) The eff* tools exist once but are reachable from two checkouts — testops-tools/tae-conversion/tools and ui-test-modernization/tools, where the entries are symlinks into the former. effwatch.sh computes its queue from `dirname $0`, which does NOT resolve symlinks, so the queue it watches is a function of the path it was launched from while the tools themselves are identical either way. Drop a request in the other checkout's queue and it sits there forever: nothing consumes it, nothing errors, and the run simply never happens. That has bitten us more than once, most recently this batch, so this stops relying on anyone remembering which checkout was used this time. effdoctor reads the live process list and prints the queue to actually use. Slice 1 checks, all read-only: - the toolchain map: canonical tools + queue, and per live effwatch its pid, launch path and the queue it is therefore watching, flagged canonical or not - more than one effwatch running (two gradle builds on one device read as flakiness, MTE-5768) - the alt checkout diverging: an eff* entry that is a SEPARATE copy rather than a symlink, or missing entirely — which is how a tool silently ages out of one checkout - effpretty resolvable under $REPO (MTE-5764 / gotcha A24) - a stale mach_android.lockfile with no process holding it (gotcha A38) - an adb device present Exit 1 on anything needing attention, so it can gate the start of a session. Run against the live setup it immediately reported both real problems: effwatch was launched from ui-test-modernization so the canonical queue was the wrong place to queue (the exact failure from earlier today), and effdoctor/efftriage were missing from the alt checkout. The latter is now symlinked, and resolves to the right VERSION through the symlink, which is what the realpath-based version lookup exists for.
… (MTE-5766) Its first live run misdiagnosed the very thing it exists to prevent. effwatch had been launched as `./tae-conversion/tools/effwatch.sh`, and the relative path was resolved with os.path.abspath — i.e. against effdoctor's cwd. Run from the firefox repo, it reported the live queue as firefox/tae-conversion/conversion-runs/_queue, a directory that has never existed, and advised queueing into it. A request dropped there is never consumed and never errors, which is the exact failure mode this tool was written to catch. Read the watcher's own cwd via lsof and resolve against that, so the answer no longer depends on where effdoctor was invoked from. Absolute launch paths are used as-is. Two guards for the class rather than the instance: a computed queue that is not a directory on disk is a FAIL, and a watcher whose cwd cannot be read is reported as undetermined with the lsof command to run. Both refuse to name a path instead of naming a wrong one, since a confidently wrong answer sends the reader somewhere silent. Verified: correct and identical from two different cwds, and the FAIL and multi-watcher paths exercised with a decoy watcher whose computed queue does not exist.
…nd (MTE-5766, MTE-5827) The toolchain had no tests. Its verdicts are what we decide "done" on, and the way we checked them was to run a tool, read its output, and judge whether it looked right — which is how two confidently wrong diagnoses survived review. Both were in the gates written to prevent exactly that. 44 tests, stdlib unittest, no device or Firefox checkout needed: ps/lsof/adb/git are stubbed and the edge cases build synthetic batch dirs. The efftriage rule tests instead run against REAL labelled conversion runs, now checked in under tests/fixtures/corpus with a labels.json manifest. That distinction is the point: hand-written traces only exercise the shapes their author imagined, so they go green while the tool mismatches real output — a false green in the suite meant to prevent false greens. The corpus also existed only as batch dirs in one person's conversion-runs/, unversioned and impossible to run in CI, which is a poor home for the campaign's main diagnostic asset. Four defects, three of them in code already reviewed by eye and pronounced fine: - efftriage diagnosed GREEN runs. Traces normally carry non-fatal [ERR] lines (nav-graph polling, tolerated absence checks) and the rules scanned them whatever the outcome, so a passing run was reported as an absence-assertion failure. Two labelled runs in our own corpus were affected. Still triaged on purpose: a retry-pass, where attempt 1 did fail and clean=false means flaky-not-done, and a crash, since crash mode is where `outcome: pass` is itself the lie (MTE-5822). - efftriage blamed the harness for a typo: a nonexistent batch dir came back as gotcha A24, sending the reader to check effpretty resolution and pull dumps off a device. It also tracebacked. - effdoctor named the wrong queue — the failure it exists to prevent. effwatch is launched by a relative path, resolved with abspath against effdoctor's cwd instead of the watcher's, so from a Firefox checkout it reported a queue under firefox/tae-conversion/ that has never existed and told us to use it. Requests there are never consumed and never error. Now reads the watcher's own cwd (/proc, then lsof); a computed queue that is not on disk is a FAIL and an unreadable cwd is "undetermined", because declining to answer beats answering wrongly. - effdoctor's self-exclusion skipped any ps line containing the substring "effdoctor", so a watcher under a directory with that name was invisible. Unnoticed watchers are the whole point of the tool. Recorded as known gaps rather than papered over: rules T3/T4/T5 have no labelled example in the 53-run corpus, so they are unvalidated rather than known-good, and 3 of 28 failures still get no diagnosis. Both are asserted, so the lists cannot go stale in either direction.
… MTE-5766) Baselining a legacy test for batch 2 produced outcome=pass, failures=0 — for a test that never ran. MainMenuTest.verifyTheMoreMainMenuSummarizePageButtonTest is @ignore'd on bug 2059592, so it skipped, and effloop's verdict was `failures == 0 and tests > 0`, which a skip satisfies. efftriage then read that outcome and said "run passed — nothing to triage". effverify was the only tool that got it right (ok:false, passed:false), so the disagreement between them was the tell. A skip asserts nothing, so it must never read as green: - effloop now counts what actually ran: outcome=skipped when nothing did, partial when only some did, both exiting 5 — a new code meaning "ran, but the verdict is incomplete", distinct from 1 (failed) because the test did not fail, it never started. - efftriage rule T11 diagnoses it from the per-test statuses rather than from `outcome`, since the field that should have raised the alarm was the one lying. It names the gate candidates: an Assume()/assumeTrue, an @ignore, a Nimbus flag, a pref, absent hardware. - Documented as gotcha A44, with the standing instruction not to read failures:0 as success on its own but to confirm results[].status == "pass" for every test expected to run. Run over the existing corpus, T11 immediately found 2 silently SKIPPED tests (browserSearchBarItemsTest, homeSearchBarItemsTest) inside an earlier 10-test run that nothing had ever reported — that run was recorded as a plain failure of one other test. The labelled fixture keeps both shapes: the skip scored as pass, and the mixed run. Verified end-to-end on device, not just in unit tests: re-running the same @ignore'd test now yields outcome=skipped with effloop_exit=5. Suite is 48 tests, green with ResourceWarnings as errors.
…ck (T12, T13, A45) Both found while converting the first batch-2 test, and both cases where the tool sent the reader somewhere unhelpful. T12 — a compile failure was answered with "no rule matched". status.json states compiled:false outright, so there is never a reason to make someone hunt through a trace that was never produced. It now names build-report.txt, where effloop already puts the Kotlin errors with file:line. This also re-labels an existing fixture: b18-fxsuggest was recorded as an "empty report" case and drew the misleading A24 advice (check effpretty resolution, pull dumps off the device) when it had simply failed to compile. T13 — the arrival check passed on the WRONG screen. Both requiredForPage anchors for a new Settings sub-page also resolved on the parent Settings screen (the row carrying the destination's own title, plus the ubiquitous Navigate up), so mozIsOnPageNow() reported arrival and navigateToPage skipped its own click. Documented as gotcha A45, alongside A42 and the nav-graph gap it rhymes with. T5 was tightened in the same pass. Its matcher keyed on "non-application window(s) present", but the status bar is itself a SYSTEM window, so that warning is in nearly every dump and the rule fired on almost any not-found. It answered the A45 case above with "a system overlay was over the app", blaming the status bar — its first real firing was a false positive, which is exactly why it was listed as unvalidated rather than known-good. It now requires that the app NOT hold focus. T13's first draft keyed on "not found" and stole the diagnosis from six other rules; the corpus tests caught that immediately. It is now narrowed to the group-level failure line and placed last so anything more specific wins first — a reminder that a new rule's blast radius needs checking against the whole corpus, not just the case that motivated it.
…ged row Cost a cycle on the never-translate conversion, and it is the reason legacy tests fall back to positional UiSelector().index(n) for checked state: in the unmerged a11y tree the stateful row has no text, content description or resource-id. Two traps stack — the Switch is blanked with clearAndSetSemantics so no ToggleableState exists at all (assert selected, not checked), and the state lives on the merging row, so COMPOSE_BY_TEXT resolves a stateless descendant. Also records that a surviving testTag is not evidence of assertable state: RadioButtonListItem tags the icon before the clearAndSetSemantics that strips selected/role/onClick.
…A46, MTE-5827) The never-translate conversion's real failure — mozVerifyElementIsChecked against a node that carries no state — came back as "no rule matched". It now names gotcha A46 and both halves of the fix: assert `selected` rather than `checked`, and match on the MERGED tree. This is the shape behind every legacy test that asserts checked state via positional UiSelector().index(n), so it will come up again. The first draft over-matched. Keying on "' is (not )?checked" also hit the `[CMD] Verifying ... is checked...` and `[OK] ... is checked` lines of runs that PASSED that assertion and failed for unrelated reasons, and it silently stole the diagnosis from two labelled gap cases (a keyboard-close PerformException and the ads-client NoHandle crash). The corpus tripwire caught it immediately — the second time in this batch that a new rule's blast radius was wider than the case that motivated it, so the matcher now requires the [ERR] marker. Fixture keeps the run as labelled evidence, including the detail that it predates the ScreenDump those verbs now emit, which is what made it opaque in the first place.
Both were consumer-facing and stale: 11 rules -> 15 (T0-T14), 44 tests -> 48. Also notes that only 12 of the 15 are trace-matching entries in RULES, since T10/T11/T12 are status-based checks in triage() and anyone counting RULES would otherwise think three were missing.
…TE-5763)
I filed a conversion bug with a TestRail id I had transcribed wrong. Comment 0 cannot be edited through
the Bugzilla API, and update only whitelisted summary/status/keywords/whiteboard/priority/severity plus
relations and assignee — so there was no way to annotate the bug at all, and the error would have stood
in a public bug indefinitely.
On create, `comment` remains the description; on update it is now a new comment. Blank is ignored rather
than posting an empty comment, and still counts as "nothing to change".
Adds effbug_tests.py, which stubs the one function that talks to Bugzilla and asserts what would be
sent. Worth pinning precisely because a wrong field writes to a public bug and there is no undo: it also
covers the existing rule that relations are wrapped as {"add": [...]} rather than sent as a bare list,
which would REPLACE the set and silently drop every other bug a meta bug tracks.
… not resolve (MTE-5827) EffScreenDumpRunner already exists in ui/efficiency/devtools and does exactly what is needed when a selector fails — an on-demand dump of a screen's live Compose tree, so selectors are authored against ground truth rather than inferred from legacy robots whose locators are often stale. It is easy to miss. Not knowing it was there, I spent several device cycles provoking failures with throwaway assertions purely to obtain a dump. efftriage now surfaces it, with the queue invocation, whenever the report contains a "not found", plus the caveat from its own docstring: graph-navigable pages only, since a launch-reached page like customTabs has no nav edge. This is a discoverability fix rather than a new capability, which is the cheaper half of most tooling problems here.
…ever fire, and record A47-A53
Hardened live against the batch-2 conversions (MTE-5827, MTE-5831). Every change here came from a real failure
during those conversions, and each new rule has a labelled corpus fixture taken from the run that motivated it.
RULE DEFECTS FIXED. All three looked correct and could never fire, which is worse than a missing rule because
"no rule matched" at least sends you to read the report:
* T4 claimed to detect an AMBIGUOUS Compose match but its regex matched Compose's ZERO-match messages
("Expected exactly '1' node but could not find any node...", "Can't retrieve node at index '0'"). Those
appear in T1-arrival-timeout, T7-multi and T2-selector-surface, so had T4 ever been ordered earlier it would
have confidently misdiagnosed three labelled runs. Now requires "but found 'N' nodes" with N > 1.
* T14 matched only "is (not) checked", so following the advice in its own gotcha (A46: switch to
mozVerifyElementIsSelected) produced "is not selected" -- a message the rule could not see. The gotcha
silently stopped applying the moment you obeyed it. Now matches both state words, and its fix text covers
what to do when the failure is already "is not selected".
* T9's first branch expected "Navigation to 'X' failed", but the ordinary arrival failure throws "Failed to
navigate to X", which only ever lands in the FAILURES summary and never inside an [ERR] window. The branch
was dead. Removed rather than extended, and the case is now handled by T18 in the report tier.
NEW REPORT-LEVEL TIER. Rules in REPORT_RULES match against status.json's primary failure message instead of a
window of trace around an [ERR] line. This exists because some failures leave NO [ERR] line at all -- a crashed
instrumentation process produces an ~11-line report -- and others put the real exception outside the window
(failure_windows takes 25 lines after the [ERR]; the ads-client stack lands 36 lines later). Widening the
window was rejected: it drags stack text into unrelated windows, which is the same cross-contamination that
makes T6 and T9 fire on the wrong evidence today (filed as MTE-5832).
* T16 / A37 - the instrumentation process crashed; there is no assertion to read.
* T17 / K13 - a UniFFI fake built with NoHandle had an un-overridden method called. Recurs in 3 of 19 corpus
runs and can fail a test AFTER its assertions have passed. Deliberately NOT matched report-wide: the same
text appears as a Suppressed exception in T0-absence-assert and T9-permission, so a substring match would
steal both.
* T18 / A6+A45+B7 - the thrown form of an arrival failure, pointing at the anchor's STRATEGY first (an
ESPRESSO selector cannot see a Compose node, and Settings screens are routinely a View toolbar over Compose
content).
TEST GUARD FIXED. efftriage_tests.rule_ids() only inspected RULES, so the three new report-tier rules were
admitted with no labelled example -- exactly what that guard exists to prevent. It now covers both tiers. Three
fixtures added from real runs: T14-selected-not-checked, T15-external-app-blocked, T18-arrival-wrong-tree.
Corpus 19 -> 21 labelled runs; two long-standing known_gap runs are now diagnosed. Suite: 58 tests, green.
DOCS. HARNESS-GOTCHAS gains a "How to write an entry" convention, which is the most important change here.
Claims about the HARNESS generalise and may be stated plainly; claims about PRODUCT code must be scoped to the
file they were observed in, must say what to verify first, and should carry a counter-example. A46 asserted
that Compose toggle rows expose state as `selected`; that is true of ListItem.kt and false of
SearchEngineShortcuts.kt, and reading it as a law cost two cycles. A46 is rescoped accordingly, with its
counter-example named.
New entries: A47 (requiresScroll is inert for the singular mozVerify, and mozSwipeTo's direction default is
the opposite of the precondition path's), A48 (an external-app assertion blocked by a system dialog, and the
legacy twin that swallows its AssertionFailedError), A49 (a rule deliberately NOT mechanised, with the reason
recorded so it does not read as an oversight), A50 (the stylus prompt steals keystrokes and causes no locate
miss, so OverlayRegistry never fires), A51 (mozEnterText can leave a Compose field looking filled while the app
never saw it), A52 (a custom search engine survives a retry but MockWebServer's port does not), A53 (some
suggestion rows sit outside the Compose test tree -- read the right block of the dump).
Also: efftriage's header no longer hard-codes a gotcha count. It said "57" and was wrong twice, which is the
same failure mode the rules are meant to prevent. It now states the two standing rules instead -- match only
text OBSERVED in a real run, and prefer "no rule matched" to a confident mismatch.
jjSDET
marked this pull request as ready for review
August 13, 2026 14:48
AaronMT
approved these changes
Aug 14, 2026
…g, gotchas A54-A59 Comes out of converting the seven site-permissions smoke tests (bugs 2064810/2064815/2064823/2064833). efftriage gains rule T19 for "No navigation path found from X to Y", which it previously left unmatched while its generic hint pointed at selectors and dumps - misleading, because that failure is a navigation graph gap where nothing was searched for on screen. Labelled corpus fixture T19-no-nav-path is a real run, so the rule is validated rather than listed as unvalidated. effnext now warns when a pick is already converted on another local branch. Its in-tree filter only sees the checked-out branch, so it offered CustomTabsTest.verifyLoginSaveInCustomTabTest, which has been converted and awaiting review on efficiency-batch-2 since last week. Advisory rather than a filter, and backup/* is excluded, because branches also carry abandoned work - the two faker conversions - and filtering on any branch would silently drop outstanding tests from the pool. That check shipped broken twice while being written, both times reporting a clean "nothing on other branches" while checking nothing at all: git grep -E cannot parse \b or \s, and subprocess was never imported so the NameError was swallowed by a bare except. Both paths now surface as branches_unchecked, and tests/effnext_tests.py covers them. HARNESS-GOTCHAS A54-A59 record what the device runs taught: option text is label plus subtext joined by a newline; permission-screen res-ids are shared across screens and ESPRESSO_BY_ID ignores visibility; BrowserPage has inbound edges only from HomePage and itself; a page-content text selector can resolve a non-clickable heading, giving "found" then a failed click; a system app-permission row title is present whether the permission is allowed or denied, so assert checkSelfPermission instead; and a page object missing from PageContext never registers its navigation. docs/SMOKE-CONVERSION-AUDIT.md is new: the standing burndown of remaining legacy @smoketest methods, what blocks each, and the parser to reproduce the count.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Three gates: explain a failure, map the toolchain, stamp a version
Follow-on to the effverify/effloop fixes. Those were corrections; these are additions aimed at
the same underlying problem — the loop knew things that people had to remember, and 97
documented gotchas only help if someone recalls the right one at the right moment.
Jira: MTE-5827, MTE-5766, MTE-5770
efftriage— map a failed run to the gotcha that explains it (MTE-5827)HARNESS-GOTCHAS is at 57 entries and CONVERSION-LESSONS ~40. Nobody recalls that mid-debug, so
the same failures get re-derived on device at 1-2 cycles each with the answer already written
down. efftriage reads the
run-report.txt/status.jsonthe loop already produced and names thelikely cause, citing the gotcha id and the fix.
Eleven rules (T0-T10), all keyed to patterns from real runs: an absence assertion that can never
come true because the selector matches another surface; an arrival check satisfied behind an
overlay;
clickAndSyncreporting a landed click as failed;mozEnterText's non-asserting locate;an ambiguous Compose match reported as not-found; a system overlay masking as absence; a long
press behaving as a tap; a stale
PageStateTrackerrouting through a destructive edge; amerged-vs-unmerged text miss; an arrival timeout; a crash rather than an assertion failure.
It also encodes the two triage rules people get wrong: scan the FIRST attempt (later attempts die
on leftover state, somewhere later and more confusingly), and treat
status.jsonas authoritativefor pass/fail while the trace explains why.
Read-only — it cannot touch the tree or the device, so it is safe to run on every failure.
effdoctorslice 1 — the toolchain map (MTE-5766)The eff* tools exist once but are reachable from two checkouts, and
effwatch.shderives its queuefrom
dirname $0— which does not resolve symlinks. So the queue it watches depends on the path itwas launched from, while the tools are identical either way. A request dropped in the other
checkout's queue is never consumed and never errors; it just hangs.
effdoctor reads the live process list and prints which queue to actually use, instead of relying on
anyone remembering which checkout was used this time. Also checks: more than one
effwatchrunning(two gradle builds against one device read as test flakiness), an alt checkout that has diverged
into a separate copy rather than a symlink,
effprettyresolvability, a stalemach_android.lockfilewith no process holding it, and an adb device. Exit 1 on anything needingattention, so it can gate the start of a session. Read-only.
VERSION,CHANGELOG.md, and--versionon every tool (MTE-5770)CalVer (
2026.08.13), not semver: these are internal scripts read from a checkout, nobody pinsthem, and the only question anyone asks is "is my copy older than the thing being discussed". One
version for tools and docs together, since they cite each other by gotcha/lesson id and a split
version would only create skew. The changelog is curated — changes a consumer depends on, each
citing gotcha ids and Jira keys — because a changelog that becomes a commit log stops being read.
The version lookup deliberately resolves symlinks: read through the other checkout's
tools/dir anunresolved path finds the wrong repo and reports "unknown" in exactly the situation a staleness
check exists for.
Verification
a cause established by hand on device. All 8 match.
effwatchlaunched from thenon-canonical path, and two tools missing from the alt checkout.
effnext/effverify/efftriage/effcheck/effloop.shstillbehave normally.
Reviewer notes
efftriage is advisory and never gates. The fair question is what happens when it is wrong — it
was wrong twice while I hardened it, which is why the labelled corpus exists. One was a
confidently wrong diagnosis (an arrival timeout instead of a selector matching the wrong surface),
which is worse than no diagnosis because it sends you down the wrong path; the other reported a
crash as a footnote so the one mode that also fools effverify printed "no rule matched". Both are
fixed and covered. Being wrong costs a reader one paragraph, not a build.
Expect
effdoctorto exit 1 on your machine if youreffwatchis launched fromui-test-modernization— that is the tool working, not a problem with the branch.No behaviour changes to existing tools beyond
--versionbeing handled before other argparsing.
Follow-ups, ticketed
MTE-5768 — make the queue drift impossible rather than merely visible: have
effwatchresolveits own path and/or publish a live-queue marker. Behaviour change for anyone deliberately running a
per-checkout queue, so it is proposed with an env override for sign-off.
MTE-5828 —
effcheckverb x selector-strategy compatibility matrix.MTE-5829 —
effparity, legacy-vs-port assertion parity, gating "a dropped assertion passes forthe wrong reason".
MTE-5830 —
Gate:annotations on every doc entry pluseffgates --coverage, making the docs anindex and the tools the enforcement.
Try it