Repository navigation
1.9.79: redirector create, update, delete and whole-rule-set writes in the MCP connector - #265
Conversation
…or tools
PUT /rotators/{id}/rules replaces a redirector's rule set the way Setup ›
Redirector saves it, in one transaction: every rule the redirector is to
have, in the order the redirects try them. A rule or entry sent with its id
keeps it (its clicks and report rows stay its own), one without is added,
one left out is removed (remove_rotator_rule, as the page asks). What the
page refuses is refused (a rule with no criterion or destination, two
destinations without a split test, a split test without weights), every
problem named by its place in one 422 with nothing written; another
account's campaign or another redirector's rule is refused by name.
?dry_run=1 answers the set as it would be stored and each rule's change;
the route is stageable; PATCH is a 405. A GET body sent back changes
nothing.
The rules had no order but their ids, so a rule could only be added after
the others: 202_rotator_rules gains position (the 1.9.79 rung), read by
rtr.php and offrtr.php through Prosper202\Rotator\RuleOrder (in id order on
a database the upgrade has not reached, rather than failing the click), the
Setup page, the API and both rotator reports; the page's save and POST
.../rules write it. The API's rule writers lock the redirector's row.
A criterion's values are stored as the page stores them (split, trimmed,
`all` as ALL), and a device in capitals, which never matches, is refused.
p202 rotator set-rules <id> (--rules or --file, --dry-run) sends a set; the
MCP connector gains rotator_create, rotator_update, rotator_delete and
rotator_set_rules, whose rules argument is the set as JSON with a schema
(a tool argument can now be a JSON document). Hosted AI keys may write
redirectors.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VRoS2SViuxaucFaKWsFnLr
… tools Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VRoS2SViuxaucFaKWsFnLr
With the rotator tools the list is 84,564 characters; with #260's text-ad tools beside them, about 87,900. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VRoS2SViuxaucFaKWsFnLr
… them The merged tree's unit tier read three of the new statements as hidden from it: the criteria and destinations delete named its table through a variable, the click path's rule read was built in a class the cached-read scan does not follow, and two rule reads put a method call after their WHERE. The delete names each table literally, RuleOrder makes the click path's cached read itself, and the ORDER BY goes through a variable. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VRoS2SViuxaucFaKWsFnLr
… test 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. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 3560ac5fb7
ℹ️ 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".
… back
PUT /rotators/{id}/rules?dry_run=1 built each rule's criteria and
redirects in the order the body listed them, while the write updates kept
entries in place and GET reads them back by id, so a body that reordered a
rule's entries (keeping their ids) previewed one order and wrote another.
The nested order decides nothing on the click path: every criterion has to
match, and a destination is drawn by weight. So the plan now holds each
rule's entries as the write will read them back (the kept ones by id, the
new ones after them as sent), and the preview and the write answer the
same data. The rules keep the order sent. The API reference and the
OpenAPI description say so.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VRoS2SViuxaucFaKWsFnLr
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: da294b8396
ℹ️ 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".
| $step_ok = _upgrade_measurement_tables([ | ||
| \Prosper202\Database\Tables\ClickTables::clickLatestVisits(), | ||
| \Prosper202\Database\Tables\ClickTables::reClickBackfill(), | ||
| \Prosper202\Database\Tables\RotatorTables::rotatorRules(), |
There was a problem hiding this comment.
Add the column in a migration current installs will run
Existing installations whose database version is already 1.9.79—including installations upgraded from the parent release—skip this if ($prosper202_version == '1.9.78') block, so they never receive the new position column. The newly changed API, setup, and reporting queries then fail with an unknown-column error (only the click path has a fallback). Reconcile this schema change in a new version step or another path that also runs for databases already at 1.9.79.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
I'm declining this one. Here is the reasoning for the owner to weigh; I checked each point.
- 1.9.79 is not tagged.
git ls-remote --tags origin 'v1.9.7*'lists v1.9.76, v1.9.77 and v1.9.78, and nothing for 1.9.79.202-config/version.phpsays 1.9.79. - The 1.9.78 → 1.9.79 rung is still open. CLAUDE.md Fix PHP 8+ compatibility issues throughout Prosper202 #33 freezes a step only once its version is tagged, so the
== '1.9.78'rung that writes 1.9.79 is the open one. Its own comment says "A schema change for 1.9.79 belongs in this block, reconciled before the version is written". 1.9.79: capture gbraid, wbraid and ttclid on clicks #263 (202_google), 1.9.79: new accounts start with privacy at Enabled for European Traffic #264 (202_users_pref) and 1.9.79: PayKickstart's product IPN, verified with each campaign's key #268 (202_users_pref) add their tables to the same_upgrade_measurement_tables()list in that rung. - Only branch code has written 1.9.79. The only databases storing 1.9.79 were installed or upgraded by unreleased branch code. Every released install stores 1.9.78 or earlier, so it runs this rung.
- The released path is covered.
tests/live/upgrade-equals-install.shpassed 25/0. It installs 1.9.55 on PHP 7.4 and upgrades it with this branch.- I also installed v1.9.78 from its tag, served this branch on that database and ran the upgrade page. The page said Success!,
202_versionreads 1.9.79, andpositionwas added asint unsigned NOT NULL DEFAULT 0, as the installer defines it. Nothing was logged.PUT /rotators/{id}/rulesthen stored and read the rules in order. That run was 12/0.
I have not added a second migration. I'm leaving this thread unresolved: whether branch deployments already at 1.9.79 need anything more is the owner's call.
Generated by Claude Code
…rule
PUT /rotators/{id}/rules/{ruleId} read and checked the rule, then took the
redirector's lock, so a rule set that removed the rule could commit in
between: the update then matched no row and its criteria and redirects
were inserted for a rule that no longer existed (these tables have no
foreign keys), answered as a success. DELETE /rotators/{id}/rules/{ruleId}
checked the rule the same way and answered a removed rule's delete as done.
Both now take the redirector's lock, then the rule's own (a locking read,
which also holds off Setup › Redirectors' save, which takes neither), before
the first read of the transaction, and read the rule under them. A rule gone
by then is a 404, with nothing written; an update that matches no row is
checked by a current read and refused the same way. The redirector delete
no longer reads the redirector before its lock.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VRoS2SViuxaucFaKWsFnLr
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
1.9.79 gained #262. The only conflict was 202-config/changelogs.json, where both sides added 1.9.79 lines: 1.9.79's lines are kept and this branch's follow them. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VRoS2SViuxaucFaKWsFnLr
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 595bbc06a8
ℹ️ 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".
…a rule at once
A rule write read its answer back after its transaction committed, when
the redirector's lock was already let go: a writer waiting on that lock
could replace the set in between, and PUT /rotators/{id}/rules answered the
second writer's rules beside its own changes (POST and PUT of one rule
answered the second writer's set too). The read-back now runs inside the
transaction, under the locks, and is what the write answers.
A rule set's 422 promised every problem at once, and gave one per rule:
the name, the flags and each criterion and redirect threw at the first
problem. Every check of a rule now runs and each problem is answered by
its place (rules.0.criteria.1.value), several in one entry included; the
single-rule writes report every entry's problems the same way.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VRoS2SViuxaucFaKWsFnLr
The catalogue keeps #280's traffic source, account and category updates and deletes beside the rotator writes, and rotator_list takes #279's hosted omission of --all. Both changelogs keep the version branch's lines and add this branch's. The tool list is 89,469 characters with both; its budget is 90,000. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VRoS2SViuxaucFaKWsFnLr
…or_get answers #281 holds a hosted call's arguments to 1,000 JSON values, because each item of a list argument becomes a flag of its own. rotator_set_rules takes its rules as one JSON document, written to its command as one flag, and has no per-rule tool to fall back on: at 1,000 values a hosted call could send back about 30 rules of one criterion and two destinations, where rotator_get answers about 100 under the result bound. A tool that takes a JSON document is held to 5,000 values instead; at that bound the call allocated 5.5 MB, the list arguments at theirs 5.1. Past it the refusal says what to leave out and which command sends a larger set. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VRoS2SViuxaucFaKWsFnLr
"set the api url" ranked p202 system integrations level with config set-url once the rotator writes and #280's tasks were both in the index: the two had scored within half a percent on the version branch, and the new commands' words moved the weights enough to tie them. config set-url now has a search task with the words the docs use for what it sets, the install's base URL or instance URL. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VRoS2SViuxaucFaKWsFnLr
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 9e9661e20e
ℹ️ 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".
…B limit --file read the whole file with os.ReadFile before checking it against the API's 1 MB body limit, so a path naming something huge, or endless like /dev/zero, was read into memory before the size refusal could run. A path is now read through the same rotatorMaxRuleSetBytes+1 limit stdin already was, and anything past it is refused by the size check. TestRotatorSetRulesReadsAFileOnlyUpToTheLimit sends --file /dev/zero and expects the 1 MB refusal with no request; with os.ReadFile put back it dies with "fatal error: out of memory" under a 3 GB address-space limit. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VRoS2SViuxaucFaKWsFnLr
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
Brings in 1.9.79's new commits. The conflicts were in 202-config/changelogs.json, 202-config/functions-upgrade.php, changelogs.txt, where both sides added lines: the incoming lines are kept as they are and this branch's follow. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VRoS2SViuxaucFaKWsFnLr
… split them, not by splittest (#327) * 1.9.79: a rule's destinations are checked by weight, as the redirects 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 * 1.9.79: read the three request values 1.9.79's own merges left cast 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 --------- Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: tracking202 <306382+tracking202@users.noreply.github.com>
Backlog: BDY6
What changed and why
An agent helping someone set up a redirector could read one through MCP (
rotator_list,rotator_get,rotator_stats) but could not make or change one. The owner asked for whatever is easiest and most powerful for an agent to use. This PR builds the coordinator's reading of that: one declarative call for the whole rule set, plus create, update and delete for the redirector. There are no per-rule MCP tools.PUT /api/v3/rotators/{id}/rules(RotatorsController::setRules()). The body is{"rules": [...]}, each rule in the shapeGET /rotators/{id}/rulesanswers, listed in the order the rules are to be tried.Connection::transaction()or not at all. The transaction first locks the redirector's row (SELECT … FOR UPDATE), then plans the set from the rules as they stand, then writes.POST/PUT /rules/{ruleId}, the rule delete and the redirector delete take the same lock.remove_rotator_rule, as on Setup › Redirectors.changes.unchangedcovers every rule).rules.1.redirects.0.redirect_campaign).PayloadKeys::refuseUnknown/listErrors, subid missing in pixel url #4).?dry_run=1answers the set as it would be stored. Each rule's change is listed (add,updatewith what changed,keep,remove) and nothing is written. It is inDryRunRoutesTest's list ([CI] Fix PHP lint action #14) and in the write-preview router.?staged=1,p202 … --staged).PATCHis refused with 405. The router lets a PATCH reach a PUT route, and a PATCH naming one rule would remove the others.rtr.php.WriteCommittedException, so a failed read is never answered as a failed write.Rule order now exists.
rtr.phpandoffrtr.phptried rules in table order, which is id order. "Put this rule ahead of the catch-all" therefore could not be expressed, and a rule added after a match-everyone rule never matched.202_rotator_rules.position(unsigned int, default 0) is added in the 1.9.78 rung through the reconciler (_upgrade_measurement_tables). It is self-contained.position, id(Prosper202\Rotator\RuleOrder): both click scripts, Setup › Redirectors, the API's GET, Overview › Rotator Breakdown andGET /rotators/{id}/stats.ajax/rotator.php), and a rule added on its own goes last.ServerFailureAnswersAreListedTestpath).Criterion values are now stored as the page stores them, on
POST/PUTof one rule as well as on the set:allstored asALL, the only spellingoffrtr.phpreads as everyone;Mobile, which the redirects never match) refused, naming it.Before this,
"United States(US), Canada(CA)"kept the space and never matched Canada.CLI:
p202 rotator set-rules <id>.--rules '<json>'or--file <path>, with-meaning stdin. It may be an array or{"rules": [...]}.--dry-runpreviews and writes nothing.{"data": …}answer (the hint gives the jq path.data.rules), duplicate JSON keys, trailing JSON, a body over 1 MB, a terminal on stdin, and a missing file.p202 rotator list. A bare 404 means the server predates 1.9.79: upgrade it or use the per-rule commands. A 422 says that nothing was written.rotator updatenow validates its id.MCP:
rotator_create,rotator_update(name and default destination),rotator_delete(dry run first, with the cascade counted) androtator_set_rules, declared as the other write tools are.rotator_set_rulestakesrulesas a JSON argument, so the catalogue gained a JSON kind (mcpToolSpec.JSON). The schema is nested (rules → criteria and redirects, with the type and statement enums), compacted, and checked for duplicate keys.TestRotatorRulesSchemaIsTheControllersholds it to the controller's field lists.rotator_get, edit the list, send it all back,dry_run: truefirst.AiConnect::WRITE_AREASgainsrotators, so propose access mintsrotators:stageand write accessrotators:write. The approval page names redirectors.Search: a
p202 rotator set-rulestask with the words people use ("redirector rules", "rule order", "weighted split", …).setup-link-003is no longer a known miss.Docs:
documentation/api/12-rotators.md(the endpoint, rule order, the value rules),docs/openapi.yaml(setRotatorRules),docs/cli.md,documentation/cli/10-go-cli.md,documentation/cli/12-ui-parity.md,go-cli/cmd/COMMAND_CONTRACTS.md. The changelog lines are under 1.9.79 inchangelogs.txtand202-config/changelogs.json.Tests
tests/Api/V3/RotatorRuleSetIntegrationTest.php(@group integration, scratch database, 24 tests). It covers:RuleOrder;tests/Redirect/RotatorRuleSetInstanceTest.php(@group instance). It clicksrtr.phpon a running instance to prove the new order decides which rule a visitor gets, and that PATCH answers 405.Go:
rotator_set_rules_test.go(what the command sends, dry run and staged, file and stdin, every refusal, the hints, the exact argv an MCP call builds, the schema against the PHP constants) andTestJSONDuplicateKeyNamesTheKeyGivenTwice.Updated tests:
TestHostedMCPToolsStayInsideTheAIKeyAreasinputs,DryRunRoutesTest, andReportsControllerTest's statement key.PathIdsAreReadStrictlyTestnow skips$writePreviewRouter, an allow-list whose handlers never read a path.Agent evals (
tests/fixtures/agent-eval/cases/rotator-rules.json):rotator-rules-001: put a split rule ahead of the catch-all; checks the order, the weights and that the catch-all is unchanged.rotator-rules-002: preview a change without making it; checks that nothing changed.setup-link-003now acceptsrotator set-rules.Plants. Every check was planted against and caught by a named test.
--dry-runwriting;Each plant used a
mktempbackup checked withcmp, and the tree was restored and re-run green.MCP catalogue size (#260)
TestMCPCatalogStaysInBudgetmeasures compact JSON:The budget here is 90,000. #260 raises it to 84,000, which this branch alone already exceeds.
Live pass
Local instance on MariaDB 10.11 and PHP 8.3.6, port 8107, installed with
install-instance.shand seeded withseed.sh. The CLI was built from this branch, and every write went throughp202 mcp call <tool> --input -. Final tree: 34 passed, 0 failed.rotator_create, then a tracker on it whose link goes throughrtr.php.rotator_set_ruleswithdry_run: true: it answersdry_run: trueand two adds, and the database is unchanged.rotator_getreads the set in order.rotator_updatereplaced the default destination, and a click no rule matches follows it.--staged:chg_proposal and writes nothing;change applywrites the set;rotator_delete: the dry run counts the cascade, then the delete removes the redirector.Also run on the final tree:
rotator-rules-001,rotator-rules-002andsetup-link-003eachneeds_judge, with their deterministic halves passing (there is no judge here). Earlier, a naive agent that does not read the rules first FAILed both new cases.tests/live/upgrade-equals-install.sh(1.9.55 on PHP 7.4, upgraded by this tree): 25 passed, 0 failed.failed to create 202_customersand 14 more LTV tables, a rung this branch does not touch.Verification
.claude/skills/p202-verify/scripts/verify.sh --since origin/1.9.79 --changed. It ran on the final tree, which is merged with origin/1.9.79 at c42982b (#258), on PHP 8.3.6 with the fullvendor/. The schema tier ran against a scratch MariaDB 10.11 database. The unit tier ran 6073 tests, 5 skipped.Also run by hand on the final tree:
CI status: not yet observed.
What could not run here
needs_judge, as in CI withoutjudge_cmd.TestHostedMCPToolsStayInsideTheAIKeyAreas,WRITE_AREAS). Whether the relay passes the nested JSON schema ofrotator_set_rulesthrough unchanged was not observed.CronEntryPointsFailLoudlyTest::testWithoutAnInstallEveryCronJobSaysSoAndExitsNonZeroskipped because the live instance's202-config.phpwas in the checkout. That file is not committed.For the reviewer to check
positioncolumn. Order could not be expressed without it, and the coordinator's reading ("replace the whole ordered rule set") needs it.p202FailedQueryMessage.splittest/statuskeeps the held value. A held weight or name that equals what is stored is accepted as is, so a GET body always goes back unchanged.POST/PUTof one rule too. A client that sentMobilenow gets a 422 where it used to get a 201 for a rule that never matched.git merge-tree) conflicts in six files:202-account/connect-ai.php202-config/changelogs.jsonapi/v3/Support/AiConnect.phpgo-cli/cmd/mcp_hosted_test.gogo-cli/cmd/mcp_test.gogo-cli/cmd/search_tasks_test.gomcp_hosted_test.go,mcpWriteToolInputis nowmap[string]map[string]interface{}(a JSON argument is not a string), so 1.9.79: text_ad_create, text_ad_update and text_ad_delete in the MCP connector #260's entries need that type.PathIdsAreReadStrictlyTestskips$writePreviewRouter: it registers preview handlers that take no path id.api/v3/index.phpkeeps the column alignment every other line of that route table has, which PSR12 counts as an error. It also adds line-length warnings in the new controller code and the two new test files; CI runs no phpcs.Found outside this item (one line each)
offrtr.phpreads onlyALLas everyone, whilertr.phpalso readsall. A rule stored asallthrough the old API matches everyone on one path and no one on the other.", Canada(CA)") keep failing to match until their rule is saved again.tracking202/ajax/rotator.php) runs no transaction. A failure part way through leaves part of the old set and part of the new.ServerStateStorereplays old idempotency records. A database reinstalled under the same host and name replays the old install's records (the directory is keyed by host and database name). Here, a seed answered 201 and created nothing until the directory was removed._upgrade_query()logs no error for a non-transient failure. It answers false with no errno or message logged, soProsper202 upgrade: failed to create 202_customerscannot say why (seen once here, and not reproduced).p202 eval run --onlykeeps only the last value when repeated. It is a String flag, so--only a --only bruns onlybwith no warning. It takes a comma list.--sincebase. A new file, once committed, is its own baseline, and its findings read as pre-existing.Generated by Claude Code