feat(masters): add --add-metrika-counter/--remove-metrika-counter to masters update - #801
Conversation
…`masters update` (#648) Implements Metrika counter linking/unlinking for "Счетчики Яндекс Метрики" on the campaign edit page, mirroring the existing audience-tag pattern (_add_audience_tag/_remove_audience_tag/_read_audience_tags) field-for-field: _add_metrika_counter/_remove_metrika_counter/_read_metrika_counters in direct_cli/browser/masters.py, wired into update_master (snapshot-before, mutate, verify-after via _verify_saved's new metrika_counters_before/ add_metrika_counters/remove_metrika_counter_indices multiset check), and --add-metrika-counter/--remove-metrika-counter CLI flags on `masters update`. Testid shapes (MetrikaCountersTagGroup.*) are confirmed via live read-only recon (2026-08-06, campaign 713277109) covering the DOM before and immediately after opening the section's editor. The actual add/remove COMMIT behaviour and the exact expected input text are NOT live-verified — documented explicitly as such in code comments, docstrings, and the CHANGELOG entry, pending a later live verification pass. Adds get_by_role support to the offline _FakeLocatorHandle test double (tests/test_masters.py) to cover the autocomplete-suggestion matching loop, which the pre-existing _add_audience_tag had no direct unit coverage for. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FF5MKQjSX89UpwHfFG3uBe
Live recon via mcp claude-in-chrome (campaign 713277109) confirms the
autocomplete suggestion's accessible text is a single line shaped
"{label} • {domain/path} • {numeric counter id}" (e.g. "Ксамата •
yandex.ru/maps • 88834924") — typing just the label surfaces the same
suggestion interactively, but the exact-match lookup in
_add_metrika_counter needs the whole string. Adding/removing a counter
itself remains NOT LIVE-VERIFIED — the recon session closed the popup
with Escape and reloaded without saving to avoid mutating this
production campaign's counter set.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FF5MKQjSX89UpwHfFG3uBe
…ormat Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FF5MKQjSX89UpwHfFG3uBe
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FF5MKQjSX89UpwHfFG3uBe
|
Закрываю и переоткрою для форсирования нового CI-прогона — тот же рассинхрон, что и на #800. |
… text
_verify_saved's metrika_counters block built its expected multiset
from add_metrika_counters (the user-supplied autocomplete-suggestion
text, "{label} • {domain/path} • {id}", one line) and compared it
directly against _read_metrika_counters's read-back of an already-
linked tag ("{domain} • {id}\n{N} целей", two lines). These are two
different string formats for the same counter and can never be equal,
so every successful `--add-metrika-counter` raised a false
BrowserSessionError claiming the save failed -- even though the
counter was actually added and saved. update_master only catches
BrowserAuthError, so this propagated as a hard command failure on the
happy path; a user who trusted the error and retried would create a
duplicate counter link.
Both text formats agree on one thing: the counter's own numeric id is
the last " • "-delimited token on the first line. _metrika_counter_
identity extracts that id from either format, and _verify_saved now
compares multisets of ids instead of raw text.
Found via cycle-review (Codex), independently verified against the
code before fixing.
🔍 Local review (cycle 1) — round 0e565e97-94a1-45bf-8a96-dc881c99ac9eReviewed locally (
Fixed in 3915f45: post-save verification now compares Metrika counters by their stable numeric id (extracted from either text format) instead of raw display text. Note: the built-in |
|
@claude review PR #801 at exact head 3915f45 (round 808c98c1-c3de-4a83-9a47-993b803a2592). Focus on critical issues: bugs, security vulnerabilities, logical errors, data loss risks, performance problems. Do NOT nitpick style, naming conventions, minor formatting, or subjective preferences — only flag issues that could break functionality or cause real harm in production. |
|
Claude finished @axisrow's task in 2m 19s —— View job Review — PR #801 (Metrika counters in
|
📋 Review summary — all cycles
Totals: 1 FIX (resolved), 0 SKIP, 0 UNVERIFIED. Note: local mode's built-in |
The 5s pre-reload wait for issue #681's confirmed-live save-commit race was scoped to _audience_touched only, so a metrika-counters-only save skipped it entirely. The Metrika counters widget shares the exact same tag-group DOM pattern audience tags does (same add/remove-by- position mechanics, same _read_until_matches-based verification), and there's no basis to assume it's immune to the identical race just because no live recon has confirmed either way for this specific widget -- the wait is cheap insurance against the same false-negative "did not save as requested" failure mode issue #681 fixed for audience tags. Found via cycle-review (/review, delayed report after the round otherwise finalized).
📋 Review summary — all cycles (re-review after cleanup commit)
Totals: 2 FIX (both resolved), 1 SKIP, 0 UNVERIFIED. Both fixes were independently re-verified by Codex against the final head. The PR remains explicitly NOT LIVE-VERIFIED end-to-end (self-disclosed by the author); the fixes in this cycle address code-level correctness of the verification logic itself, not the live browser-interaction assumptions. |
📋 Review summary — all cycles (final, after 2 re-reviews)
Totals: 3 FIX (all resolved), 3 SKIP, 0 UNVERIFIED. Re-review cap reached (2/2). Both |
Summary
Implements Yandex Metrika counter linking/unlinking in
direct masters update— part of issue #648's Этап C (structural sections: sitelinks, audience, Metrika counters/goals). Browser-driven (Мастер кампаний has no API), mirroring the--add-audience-tag/--remove-audience-tag(#681) add/remove-by-position pattern almost field-for-field, since the "Счетчики Яндекс Метрики" section's DOM shape (MetrikaCountersTagGroup.*) turned out to be structurally identical to the audience-tags widget.--add-metrika-counter TEXT— types into the section's search input, clicks the matching autocomplete suggestion.--remove-metrika-counter POSITION— removes a counter by its 0-based on-page position._read_metrika_counters/_add_metrika_counter/_remove_metrika_counterindirect_cli/browser/masters.py, wired intoupdate_masterand_verify_saved(multiset check), same as audience tags.Testid structure was confirmed via live read-only recon against campaign 713277109 (Playwright +
mcp claude-in-chrome, no mutations saved). Actual add/remove/save behavior has not been exercised end to end.One thing IS live-confirmed (via
mcp claude-in-chromeagainst the real Chrome session, 2026-08-06): the exact text format--add-metrika-counterneeds. Typing a partial query (e.g. just a counter's label, "Ксамата") surfaces the same suggestion as typing more, but the suggestion's accessible text is a single line shaped"{label} • {domain/path} • {numeric counter id}"(confirmed:"Ксамата • yandex.ru/maps • 88834924"). Since the exact-match lookup compares against the WHOLE string, callers must pass that full string, not just the label — this is now documented in the CLI help text and the_add_metrika_counterdocstring.Still open:
match.click()actually commits the counter and whether a save persists it — the recon session closed the suggestion popup withEscapeand reloaded the edit page without saving, specifically to avoid mutating this production campaign's counter set.Escapeclosing the suggestion dropdown but leaving the typed text sitting in the input (the field had to be explicitly cleared before navigating away). This mirrors the class of bug found in issue masters add --draft: клик 'Сохранить как черновик' не создаёт кампанию, redirect timeout (5/5 live-попыток) #796 (_set_target_action_price's price popup not being closed before the terminal click) — worth revisiting whether_add_metrika_counter's error path needs the same explicit-clear treatment once this is exercised end to end, not just anEscapepress._AUDIENCE_TAG_SUGGEST_TIMEOUT_MS's 5s budget with no independent calibration.MetrikaCountersTagGroup.Expander's purpose — left unimplemented/ignored, its function was not determined from recon.None of the add/remove/save behavior is claimed as working in the docstrings/CLI help. A live mutation-verification pass is planned as a follow-up (browser session currently queued for other in-flight work).
Test plan
pytest tests/test_masters.py -k "Metrika or metrika"— 15 passedpytest -n auto— 3392 passed, 23 skipped, no regressionsblack/flake8clean on all changed filesRelates to #648 (no dedicated sub-issue for this specific section).
🤖 Generated with Claude Code
https://claude.ai/code/session_01FF5MKQjSX89UpwHfFG3uBe