Skip to content

Complete milestone 18 optional-field patch scope#266

Merged
axisrow merged 9 commits into
mainfrom
feat/issue-239-optional-fields-gate
May 24, 2026
Merged

Complete milestone 18 optional-field patch scope#266
axisrow merged 9 commits into
mainfrom
feat/issue-239-optional-fields-gate

Conversation

@axisrow

@axisrow axisrow commented May 24, 2026

Copy link
Copy Markdown
Owner

Summary\n- complete milestone 18 compact typed-flag scope for #242, #257, #256, #243, #246, #245, and #253\n- keep public CLI canonical: typed flags only, no public --json examples, single-line README examples\n- split FileFeed upload/base64 support to #264 and move broad parity epics out of milestone 18\n\n## Milestone state\n- milestone 18 (0.3.12) has 0 open issues / 10 closed issues\n- large follow-ups moved out of milestone with comments: #240, #244, #247, #249, #250, #251, #252, #254, #255\n\n## Verification\n- python3 -m pytest tests/test_cli.py tests/test_dry_run.py tests/test_wsdl_parity_gate.py\n- per-child PR review-cycle with @codex @claude, 5 minute waits, actionable comments fixed, CI green before merge\n\nDocs checked during child PRs include Yandex Direct API Feeds.add/update: https://yandex.ru/dev/direct/doc/en/feeds/add and https://yandex.ru/dev/direct/doc/en/feeds/update.

axisrow and others added 7 commits May 24, 2026 11:09
* Add adgroup tracking params flags

* test: cover adgroup tracking params add rejection

---------

Co-authored-by: axisrow <[email protected]>
* feat(sitelinks): support turbo page id

* test(sitelinks): cover turbo page json input

---------

Co-authored-by: axisrow <[email protected]>
* feat(retargeting): add description flags

* fix(retargeting): preserve empty description

---------

Co-authored-by: axisrow <[email protected]>
* feat(adgroups): add negative keyword flags

* fix(adgroups): report invalid negative set ids as usage errors

---------

Co-authored-by: axisrow <[email protected]>
* feat(vcards): add nested contact fields

* fix(vcards): preserve grouped flag usage errors

---------

Co-authored-by: axisrow <[email protected]>
Copilot AI review requested due to automatic review settings May 24, 2026 05:09
@chatgpt-codex-connector

Copy link
Copy Markdown

To use Codex here, create an environment for this repo.

@axisrow

axisrow commented May 24, 2026

Copy link
Copy Markdown
Owner Author

@codex @claude review. Focus on critical issues only: bugs, security vulnerabilities, logical errors, data loss risks, performance problems, CLI contract violations, WSDL/API mismatch, milestone-scope gaps, and incorrect issue closure/triage. Do NOT nitpick style, naming conventions, minor formatting, or subjective preferences; only flag issues that could break functionality, violate the public CLI contract, or make milestone 18 incomplete.

@claude

claude Bot commented May 24, 2026

Copy link
Copy Markdown

Claude finished @axisrow's task in 8m 8s —— View job


Review — PR #266 (milestone 18 optional-field gate)

Scope-filtered review per trigger: bugs, security, logic errors, data-loss risks, perf, CLI-contract, WSDL/API mismatch, milestone-scope gaps, triage. Style/naming/format intentionally skipped.

Verdict: no blocking issues. Two minor concerns worth flagging.


Verified clean


Minor — not blocking, worth a follow-up

  1. parse_sitelink_specs accepts negative TurboPageId via the pipe-spec pathdirect_cli/utils.py:524 uses int(parts[3]), which accepts -1, +12, etc. The JSON path's _coerce_turbo_page_id (direct_cli/commands/sitelinks.py:18) rejects non-digit strings via .isdigit() but accepts negative ints from JSON. Inconsistent guardrails; the live API will reject negative IDs either way, so user impact is small. Fix this →

  2. No way to clear AdGroup.NegativeKeywords / NegativeKeywordSharedSetIds — WSDL marks both nillable="true" (adgroups.xml:127-128), but the CLI uses if parsed_negative_keywords: so an empty list silently drops. The feeds module exposes --clear-feed-login/password for the analogous case; the adgroup module does not. Not part of the issue scope (adgroups add/update: add ad-group negative keyword typed flags #243 only asked for typed flags), so probably fine to defer — but worth tracking. Same applies to --tracking-params "" (also nillable in update path).


Things I deliberately did not flag

  • README example replaces the basic adgroups update --name … --status … --region-ids … line with a new-flag-only line. Doc-only, not a CLI-contract change.
  • retargeting --description "" produces Description: "" in payload while adgroups --tracking-params "" does not — asymmetric but covered by explicit tests; intentional.
  • int() for --price-extension-price accepts negatives; price units are documented as × 1,000,000 and the API will reject.
    · branch feat/issue-239-optional-fields-gate

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Completes the remaining milestone-18 optional-field parity work by adding typed CLI flags (and dry-run coverage) for several previously-unexposed WSDL optional fields, while keeping the canonical CLI contract (typed flags, single-line README examples, no public JSON payload forwarding).

Changes:

  • Add typed flags for: adgroups negative keywords/shared-set IDs + tracking params; TEXT_AD update video/price extensions; retargeting list description; sitelink TurboPageId; vCard InstantMessenger + PointOnMap; feeds UrlFeed RemoveUtmTags + credentials (with clear flags for update).
  • Extend dry-run tests and the WSDL parity/optional-field audit mappings to mark these fields as supported and to re-scope FileFeed follow-ups to #264.
  • Update README examples (EN/RU) to reflect the new canonical typed flags.

Reviewed changes

Copilot reviewed 11 out of 11 changed files in this pull request and generated no comments.

Show a summary per file
File Description
tests/WSDL_OPTIONAL_FIELD_AUDIT.md Updates optional-field audit classifications/counts; moves FileFeed follow-up to #264.
tests/test_wsdl_parity_gate.py Extends optional-field coverage mapping and follow-up registry for new typed flags.
tests/test_dry_run.py Adds dry-run payload tests + incompatibility/no-op guard tests for new flags.
README.md Updates canonical CLI examples/documentation for newly supported typed flags (EN/RU).
direct_cli/utils.py Extends sitelink spec parser to support optional TurboPageId segment.
direct_cli/commands/vcards.py Adds typed flags and payload builders for InstantMessenger + PointOnMap.
direct_cli/commands/sitelinks.py Adds TurboPageId support for both pipe-spec and JSON/file inputs with validation.
direct_cli/commands/retargeting.py Adds typed --description with max-length validation for add/update.
direct_cli/commands/feeds.py Adds UrlFeed.RemoveUtmTags + feed credentials; supports clearing credentials on update.
direct_cli/commands/ads.py Adds TEXT_AD-only VideoExtension.CreativeId + PriceExtension typed flags with subtype rejection.
direct_cli/commands/adgroups.py Adds typed negative keyword flags + shared-set IDs + tracking params, with validation and dry-run support.

@axisrow

axisrow commented May 24, 2026

Copy link
Copy Markdown
Owner Author

Addressed independent scope-review blocker in a5dbb45: CHANGELOG.md and CLAUDE.md now describe optional WSDL audit depth as unbounded, matching tests/WSDL_OPTIONAL_FIELD_AUDIT.md and the generator. Verification: rg confirms no stale 'depth 2' text; python3 -m pytest tests/test_cli.py tests/test_dry_run.py tests/test_wsdl_parity_gate.py -> 379 passed, 6 skipped.\n\n@codex @claude review. Focus on critical issues only: bugs, security vulnerabilities, logical errors, data loss risks, performance problems, CLI contract violations, WSDL/API mismatch, milestone-scope gaps, and incorrect issue closure/triage. Do NOT nitpick style, naming conventions, minor formatting, or subjective preferences; only flag issues that could break functionality, violate the public CLI contract, or make milestone 18 incomplete.

@claude

claude Bot commented May 24, 2026

Copy link
Copy Markdown

Claude finished @axisrow's task in 7m 0s —— View job


Review of PR #266 — milestone 18 optional-field scope

  • Pull diff vs origin/main and inspect changed files
  • Critical bugs / logical errors
  • CLI contract & WSDL/API mismatch
  • Milestone-scope gaps & issue triage
  • Post review

Critical findings

1. Milestone-scope gap — CHANGELOG.md 0.3.12 section does not describe the seven typed-flag additions this PR ships

pyproject.toml:3 is already at version = "0.3.12", and the PR description says this milestone closes #242, #243, #245, #246, #253, #256, #257. The 0.3.12 section in CHANGELOG.md:3-27, however, only documents:

None of the seven flag additions ship a public CHANGELOG entry, even though they ship under 0.3.12 as a public PyPI release:

Issue Surface Flags shipped on this branch In ## 0.3.12
#242 adgroups add/update --negative-keywords, --negative-keyword-shared-set-ids
#246 adgroups add/update --tracking-params
#245 ads update TEXT_AD --video-extension-creative-id, --price-extension-{price,old-price,price-qualifier,price-currency}
#253 (URL portion) feeds add/update --remove-utm-tags, --feed-login, --feed-password, --clear-feed-{login,password}
#243 retargeting add/update --description
#257 sitelinks add TurboPageId via pipe-spec and JSON
#256 vcards add --instant-messenger-{client,login}, --point-on-map-{x,y,x1,y1,x2,y2}

This is a real publish-readiness gap: PyPI users upgrading to 0.3.12 will see a one-bullet release that omits every flag this PR is named after. Either each child PR's CHANGELOG bullet needs to land in this branch's 0.3.12 section, or the milestone-rollup commit on this branch should add them in a single batch before tagging.

Fix this →


Non-blocking observations (verified clean)

  • a5dbb45 docs alignment is correct. OPTIONAL_FIELD_AUDIT_MAX_DEPTH = None in tests/test_wsdl_parity_gate.py:612, the generator emits unbounded (scripts/build_wsdl_optional_field_audit.py:159-162), and tests/WSDL_OPTIONAL_FIELD_AUDIT.md:4 shows unbounded. CHANGELOG.md and CLAUDE.md now match.
  • Subtype gates hold. ads.py rejects --video-extension-* / --price-extension-* on non-TEXT_AD types via _reject_incompatible_flags (direct_cli/commands/ads.py:626-712), confirmed by test_ads_update_text_ad_price_extension_flags_rejected_for_mobile_app_ad and the TEXT_IMAGE_AD variant.
  • PriceExtension enum values match WSDL. PriceCurrencyEnum (RUB, UAH, BYN, USD, EUR, KZT, TRY, CHF, UZS) and PriceQualifierEnum (NONE, FROM, UP_TO) in tests/wsdl_cache/ads.xml:311-330 are mirrored 1:1 in the click Choice enums.
  • Empty-payload no-ops fire correctly. feeds update raises if neither --name, --url, --remove-utm-tags, credential set nor clear flag is given (direct_cli/commands/feeds.py:204-209, locked by test_feeds_update_without_fields_errors). adgroups update does the same with the extended message (direct_cli/commands/adgroups.py:368-374). ads update empty-subtype gate still trips even when only one extension flag is set, since the if text_ad: ad_data["TextAd"] = text_ad branch promotes the dict (direct_cli/commands/ads.py:736-775, locked by test_ads_update_text_ad_extension_flags_count_as_updatable_fields).
  • UrlFeed credential-clear semantics. _url_feed_payload(...) emits {"Login": None} / {"Password": None} for clear-flags, and --feed-login + --clear-feed-login together are rejected with click.UsageError before any payload is built (direct_cli/commands/feeds.py:170-180). The conflict path is covered by test_feeds_update_rejects_setting_and_clearing_login.
  • vcards add PointOnMap requires all six WSDL minOccurs=1 coordinates if any single one is supplied (direct_cli/commands/vcards.py:39-66), which matches tests/WSDL_OPTIONAL_FIELD_AUDIT.md:6076-6082.
  • Sitelinks Href relaxation is correct API parity. _normalize_sitelink_row and the pipe-spec parser both require Href OR TurboPageId (no row drops a previously required field silently) — direct_cli/commands/sitelinks.py:43-48, direct_cli/utils.py:527-530. Existing Title|Href[|Description] callers stay unchanged.
  • WSDL parity registry covers everything new. Every new field-path is added to OPTIONAL_FIELD_CLI_OPTIONS in tests/test_wsdl_parity_gate.py:635-897, and #253 only remains as a missing_followup rationale for FileFeed rows (correctly split to feeds add/update: add FileFeed typed upload support #264).
  • Validation paths re-raise UsageError. adgroups.add and vcards.add both wrap user-facing validation with explicit except click.UsageError: raise ahead of the generic Exception handler, so character-limit and pair-incompleteness errors propagate as exit-code 2 rather than getting Abort()-swallowed.

I could not run pytest in this environment (no install permission). The trigger comment reports 379 passed, 6 skipped on tests/test_cli.py tests/test_dry_run.py tests/test_wsdl_parity_gate.py, which is consistent with the registry being kept in lock-step with the command additions.


Bottom line

No code-level critical bugs, security vulnerabilities, data-loss paths, CLI-contract violations or WSDL/API mismatches found. The only milestone-completeness blocker is the missing 0.3.12 CHANGELOG bullets for the seven typed-flag groups shipped on this branch — the release tag would otherwise misrepresent what 0.3.12 actually contains for PyPI users.
· Branch

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. You're on a roll.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@axisrow

axisrow commented May 24, 2026

Copy link
Copy Markdown
Owner Author

Addressed Claude's CHANGELOG publish-readiness blocker in ebffa64: 0.3.12 now documents all milestone-18 typed-flag groups shipped by this branch (#242, #243, #245, #246, #253 URL-feed portion, #256, #257) plus the existing #238/#239 entries. Verification: python3 -m pytest tests/test_cli.py tests/test_dry_run.py tests/test_wsdl_parity_gate.py -> 379 passed, 6 skipped.\n\n@codex @claude review. Focus on critical issues only: bugs, security vulnerabilities, logical errors, data loss risks, performance problems, CLI contract violations, WSDL/API mismatch, milestone-scope gaps, incorrect issue closure/triage, and release-note blockers. Do NOT nitpick style, naming conventions, minor formatting, or subjective preferences; only flag issues that could break functionality, violate the public CLI contract, or make milestone 18 incomplete.

@claude

claude Bot commented May 24, 2026

Copy link
Copy Markdown

Claude finished @axisrow's task in 2m 37s —— View job


Review of PR #266 — milestone 18 optional-field scope (post-ebffa64)

  • Inspect ebffa64 CHANGELOG diff vs prior review's blocker list
  • Cross-check CHANGELOG bullets against actual shipped flags on this branch
  • Re-verify no new critical issues introduced
  • Post final verdict

Verdict: no remaining critical issues. Publish-readiness blocker resolved.

ebffa64 closes the CHANGELOG gap. The 0.3.12 section now ships an Added: bullet for every typed-flag group on this branch, plus the prior #238 (callouts) entry and the #239 audit-soft-gate entry under Changed:. pyproject.toml:7 is version = "0.3.12", so the public release will match what landed.


Important correction — my prior review had the issue→surface mapping wrong

My earlier comment listed:

  • #242 → adgroups --negative-keywords (wrong)
  • #246 → adgroups --tracking-params (wrong)
  • #243 → retargeting --description (wrong)
  • #256 → vcards InstantMessenger + PointOnMap (wrong)

The new CHANGELOG ships the correct mapping, which I verified against the original tests/WSDL_OPTIONAL_FIELD_AUDIT.md rows (git history of the doc, before the closed-row removal):

Issue Surface (per audit history) CHANGELOG entry Match
#242 adgroups TrackingParams --tracking-params
#243 adgroups NegativeKeywords / NegativeKeywordSharedSetIds both flags
#245 ads TEXT_AD VideoExtension + PriceExtension all five flags
#246 vcards InstantMessenger + PointOnMap both flag groups
#253 (URL portion) feeds UrlFeed.RemoveUtmTags/Login/Password four flags + two clear flags; FileFeed split to #264
#256 retargeting Description --description
#257 sitelinks Sitelinks.TurboPageId TurboPageId pipe-spec / JSON

The CHANGELOG is correct as written; my prior table was the one with the scrambled labels. Apologies for the noise — the author addressed the real blocker (missing entries), not a phantom one. Nothing to change here.


Re-verified clean (unchanged from prior review)

  • WSDL ArrayOf* wrapping for NegativeKeywords/NegativeKeywordSharedSetIds correct (adgroups.py).
  • Subtype gates for TEXT_AD-only price/video extension flags (ads.py:626-712).
  • Required-children invariants on PointOnMap (six coords) and InstantMessenger (client + login) enforced via UsageError before request build (vcards.py:39-66).
  • UrlFeed credential clear semantics emit {"Login": None} / {"Password": None}; set+clear mutual-exclusion raised before payload build (feeds.py:170-191).
  • Empty-subtype no-op guards still fire on feeds update (feeds.py:204-209), adgroups update (adgroups.py:368-374), ads update even when only an extension flag is set (ads.py:736-775).
  • UsageError re-raise pattern preserved across all new code paths so validation errors propagate as exit-code 2 rather than being swallowed into Abort().
  • Audit doc / parity gate / OPTIONAL_FIELD_CLI_OPTIONS registry mirror the shipped flags consistently; only FileFeed rows remain as missing_followup pointing to #264.

The reporter's pytest tests/test_cli.py tests/test_dry_run.py tests/test_wsdl_parity_gate.py → 379 passed / 6 skipped is consistent with no regressions, but I could not re-run pytest locally in this environment.


Non-blocking carryover from prior review (still unaddressed, still not in milestone-18 scope)

  1. parse_sitelink_specs pipe path uses int(parts[3]) which accepts negative TurboPageId; the JSON path's _coerce_turbo_page_id uses .isdigit() which also still accepts negatives once parsed as int. Live API will reject either way — defer.
  2. No clear flag for AdGroup.NegativeKeywords / NegativeKeywordSharedSetIds / TrackingParams even though WSDL marks them nillable="true". Out of scope for #243/#242/#246; worth tracking as a follow-up but not a milestone blocker.
    · Branch

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. You're on a roll.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@axisrow
axisrow merged commit c8499be into main May 24, 2026
6 checks passed
@axisrow
axisrow deleted the feat/issue-239-optional-fields-gate branch May 24, 2026 05:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants