Repository navigation
1.9.79: a rule's destinations are checked by weight, as the redirects split them, not by splittest - #327
Merged
Conversation
… split them, not by splittest
The rule check (RotatorsController::planRule(), used by PUT
/rotators/{id}/rules, the MCP tool rotator_set_rules, and POST and PUT of one
rule) refused a rule with two or more destinations and splittest 0, saying
"splittest is 0, so the rule sends every visitor it matches to one
destination". Neither redirect reads splittest: rtr.php and offrtr.php send
a rule's one destination every visitor it matches, and split them between
more by weight with getSplitTestValue(), the first taking them all when every
weight is 0. Measured on a live instance, 24 clicks each through rtr.php and
through offrtr.php: splittest 0 at 50/50 split 13/11 and 11/13, as splittest
1 did; 100/0 and 0/0 sent every click to the first destination under either
value. So the refusal named behaviour that does not happen, a GET body
holding such a rule could not be sent back to the set, such a rule could not
be renamed through PUT of one rule, and a POST the 1.9.78 API took was
refused.
The check now asks every rule with more than one destination for a weight on
each and at least one above 0, whatever splittest says, and says why in its
message; a rule with one destination is taken whatever its weight, since
the redirects send it every visitor. splittest is stored as sent: it is
Setup › Redirectors' Split test switch.
Setup › Redirectors showed a rule with more than one destination and
splittest 0 with the switch off, which shows and posts only the first
destination, so saving the redirector removed the others with nothing said
(measured in a browser, on 1.9.79 and before). Such a rule now opens as a
split test, each destination with its weight, and saves as one; the list
names it a split test and shows the weights.
RotatorRuleSplitIntegrationTest holds the check to the click path: it runs
the real getSplitTestValue() from connect2.php over each shape of weights,
expects a rule taken exactly when the redirects send its visitors where its
weights say, the same with splittest 0 and 1 and through all three writes,
and fails if rtr.php, offrtr.php, connect2.php or RuleOrder.php comes to read
splittest or to choose a destination another way.
RedirectorPageShowsSplitsIntegrationTest renders the page for such a rule.
SyncRotatorRulesIntegrationTest syncs one, as a set and one rule at a time;
the check made that sync fail. RotatorRuleSetInstanceTest clicks through
rtr.php for a rule sent with splittest 0. Follows up #265, which merged
without this.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VRoS2SViuxaucFaKWsFnLr
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
1.9.79's PHPStan job has been red since the merges at 15:46Z. #308 extended ForbidNumericCastOfRequestParamRule to the pages and baselined the casts already there. Three PRs merged beside it each added a cast that its baseline does not count, and each PR passed on its own: - Personal settings (#283) cast the same-IP fallback after checking it is a key of its choices. It now stores 1 for '1' and 0 for '0', the two keys the check admits. - Setup > Offline Conversions (#293) cast a feed id after a regex test. It now reads it with PostedId::read(). - Setup > Traffic Sources (#271) cast the edited account's id to read its stored postbacks. The page's own ownership check compares the raw value in SQL, where MySQL reads '12abc' as 12. So a save from ?edit_ppc_account_id=12abc changed account 12 (one of the account's own, not the one named) and held that account's postbacks. Measured on a live instance: the old page saved the tampered edit onto account 1. The id is now read exactly, and anything else is refused in the page's words before anything is read or written. The baseline counts are unchanged: each file now holds only the casts they counted. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VRoS2SViuxaucFaKWsFnLr
…lit-check 1.9.79's PHPStan job is red on its own tip: three request casts that its merges left. #328 fixes them, and it is merged here so this branch's PHPStan check runs green. When #328 merges into 1.9.79, the commit is already there and this merge adds nothing. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VRoS2SViuxaucFaKWsFnLr
Owner
Author
|
What is red here, and why it is not this PR's:
Generated by Claude Code |
Owner
Author
|
@copilot resolve the merge conflicts in this pull request |
Co-authored-by: tracking202 <306382+tracking202@users.noreply.github.com>
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.
Follows up #265, which merged without this. Backlog: K2SP.
What was wrong
RotatorsController::planRule()checks every redirector rule the API writes:PUT /rotators/{id}/rules(and the MCP toolrotator_set_rules), and since #290POST /rotators/{id}/rulesandPUT /rotators/{id}/rules/{ruleId}. It refused a rule with two or more destinations andsplittest0: "splittest is 0, so the rule sends every visitor it matches to one destination".Neither redirect reads
splittest.rtr.phpandoffrtr.phpsend a rule's one destination every visitor it matches. With more than one destination they split the visitors by weight (getSplitTestValue()), and when every weight is 0 the first destination takes them all.Measured on a live instance built from origin/1.9.79 (dee579c, whose rotator code is unchanged at c6d5167). Rules were stored by SQL, then 24 clicks per rule went through
rtr.php(a tracker link) and 24 throughoffrtr.php(record_adv.php, then?rpi=):On the same instance, the API refused all of these:
GET /rotators/{id}/rulesbody sent back toPUT /rotators/{id}/rules;PUT /rotators/{id}/rules/{ruleId}with{"rule_name": …}) of the 50/50 splittest-0 rule;POSTof a rule with two destinations andsplittest0. The 1.9.78 API took that body; without weights it stored 100/100, which the redirects split 50/50.Setup › Redirectors was measured in a browser (Playwright, the page's own JS). A rule with
splittest0 and two destinations opened with the Split test switch off, which shows and posts only the first destination. Saving the redirector, with no edit, deleted the second destination. The list named both destinations without their weights.What changed
planRule()no longer readssplittest. A rule with more than one destination needs a weight on each and at least one weight above 0. The message says why: "The redirects split the visitors it matches by weight … whether or not splittest is 1". A rule with one destination is taken whatever its weight, since the redirects send it every visitor.splittestis stored as sent: it is the page's Split test switch. The set,POSTandPUTof one rule, and the MCP tool all go through this check.tracking202/setup/rotator.php). A rule with more than one destination opens as a split test, each destination with its weight, and Save keeps them all (and storessplittest1). The list marks it "split test" and shows the weights. Measured after the change: Save on the same rules kept both destinations, and the clicks split as before (13/11 throughrtr.php, 11/13 throughoffrtr.php).documentation/api/12-rotators.md,docs/openapi.yaml(the set, POST, PUT and both rule schemas),docs/cli.md, theset-ruleshelp, and the MCP catalog'ssplittestandweight.rotator-rules-001no longer requiressplittest == 1. The clicks split either way; the check still holds the order, the equal weights and the kept id.Where the page, the set and the single-rule writes still differ
min="1", andajax/rotator.php'sarray_filterreads'0'as missing. So a redirector holding a destination weighted 0 cannot be saved on the page until that weight is raised or the destination removed. This predates the change. Before it, the page silently cut such a rule whensplittestwas 0. Now it shows the 0 and refuses to save. Left for a separate item.ajax/rotator.phpwithsplit=falseand several destinations stores them all. The page's JS never sends one.Tests
tests/Api/V3/RotatorRuleSplitIntegrationTest.php(new). It holds the check to the click path:getSplitTestValue(), extracted fromconnect2.phpas a closure, over 11 weight shapes.splittest0 and 1, through the set, POST and PUT of one rule.rtr.php,offrtr.php,connect2.phporRuleOrder.phpcomes to readsplittest, or chooses a destination another way.tests/Setup/RedirectorPageShowsSplitsIntegrationTest.php(new) renders the page through the page runner for rules with one destination (splittest0 and 1) and with two destinations atsplittest0.RotatorRuleSetIntegrationTest:splittest0 and 1 alike.{"splittest": 0}on two destinations is taken and keeps both destinations.SyncRotatorRulesIntegrationTest: a source rule stored withsplittest0 and weights 60/40 syncs whole, both as a set and one rule at a time, with its weights. On 1.9.79 the target refused it, so the sync of its redirector failed.RotatorRuleSetInstanceTest: a set sent withsplittest0 splits 24rtr.phpclicks between two even destinations, and a destination weighted 0 takes none of 8.Plants. Each defect was planted with a
mktempbackup, run on a fresh database, and restored with acmpcheck. Every one failed a named test:testAPutOfOneRuleIsCheckedAsTheRuleItWouldLeave).testARuleSplitWithTheSwitchOffSyncsWithItsWeights).testTwoDestinationsSplitTheClicksByWeightWhateverSplittestSaysanswered 422.$isSplitreverted, or the editor switch reverted:RedirectorPageShowsSplitsIntegrationTestfailed each time.rtr.phporoffrtr.phpchoosing bysplittest:testTheRedirectsChooseADestinationWithoutReadingSplittestfailed each time.Verification
verify.sh --since origin/1.9.79 --changed, on 01fa4f3 (origin/1.9.79 at c6d5167 plus this commit), PHP 8.3.6, MariaDB 10.11:202-account/account.php,tracking202/setup/offline_conversions.php,tracking202/setup/ppc_accounts.php), allprosper202.numericCastOfRequestParamand itsignore.count. The same 5 errors appear on origin/1.9.79 itself (c6d5167), measured with the same config on those three files.phpstan.neon.distoverapi/v3/Controllers/,tracking202/redirect/andtracking202/setup/rotator.phpreports no errors, andphpstan-strict.neonreports no errors.Integration suites. Each ran alone on a fresh
xk2sp_database (MariaDB 10.11):All OK.
Instance suites against the live server: RotatorRuleSetInstanceTest (3 tests) and RtrClickInstanceTest (3 tests), both OK.
Live pass before and after, on the same instance: the click table above, the API answers and the page save.
Could not run:
p202 mcpagainst the instance. The licence stub is down, so the instance was installed with install.php's own steps without the licence check, and the CLI would get 402. The API was driven directly; the MCP tool's write is the set's controller call.For the reviewer
splittestsays. The 1.9.78 API defaulted a missing weight to 100 forsplittest0. Such a body, two destinations without weights, is now a 422 naming each weight. GET always answers weights, so a body read back is unaffected.splittest1 is now taken with weight 0 or no weight. 1.9.79 refused it. The redirects send that destination every visitor either way.splittest1. So a page save changes the storedsplittestof a rule the API stored with 0. Routing is unchanged, butp202 diffand sync will see the field change.Generated by Claude Code