Skip to content

1.9.79: the optimizer's card says when its webhook stops receiving - #298

Merged
tracking202 merged 3 commits into
1.9.79from
1.9.79-lpo-card-webhook-status
Oct 28, 2026
Merged

tracking202 merged 3 commits into
1.9.79from
1.9.79-lpo-card-webhook-status

Conversation

@tracking202

@tracking202 tracking202 commented Oct 28, 2026 •

Copy link
Copy Markdown
Owner

Backlog: BZZ9

Not stacked. This branch is made from 1.9.79 and depends on no open PR. The Revive button it points to arrives with #289. Until #289 merges, the card says "Fix the endpoint, then revive the webhook in LTV Settings." and uses none of #289's code.

What was wrong

My Account › API Integrations showed the Landing Page Optimizer pairing as Connected from lpo_status alone. The pairing makes a wildcard LTV webhook and stores its id in lpo_bridge_config. That webhook can stop receiving events in two ways:

Either way the optimizer stops receiving conversions and engagement, and the card still said Connected. I measured this live: with the webhook dead, deleted, or not named by the pairing record, the card on 1.9.79 read "Connected" every time.

What changed

  • Prosper202\Lpo\PairingWebhook::state() reads the webhook lpo_bridge_config names, through MysqlWebhookRepository::get(), so the read is scoped to the account. Its answers:

    • receiving: the webhook is active;
    • dead: the webhook is dead;
    • off: the webhook is disabled;
    • gone: no webhook of this account has that id;
    • unnamed: the record names no webhook, or names it as anything but a JSON integer above 0. It never takes (int) of what it finds, so "12abc" is no id.

    A database failure throws. The page logs it and reads it as unknown, never as an answer.

  • The card, local to the Landing Page Optimizer panel in 202-account/api-integrations.php:

state pill flash
receiving Connected none (as before)
dead Not receiving events (bad) "The optimizer is not receiving events: its webhook (#N) was stopped after a delivery to it failed 6 times. Fix the endpoint, then revive the webhook in LTV Settings." plus an LTV Settings button (tracking202/analyze/ltv.php?view=settings)
gone Not receiving events "…its webhook (#N) no longer exists on this install. Disconnect, then connect again to pair a new one."
off Not receiving events "…its webhook (#N) is switched off. Disconnect, then connect again to pair a new one."
unnamed Status unknown (warn) "This install's pairing record names no webhook, so whether the optimizer receives events cannot be told. Disconnect, then connect again to pair a new one."
the read failed Status unknown (warn) "The status of the optimizer's webhook could not be read just now, so whether it receives events cannot be told. Reload the page to try again."

The sentences live in PairingWebhook::notice(), so the page adds only a few lines. "6" is MysqlWebhookRepository::MAX_ATTEMPTS.

  • The card uses only kit components: p202-pill--bad/--warn, the p202_flash() helper (the kit's flash), and a btn in the existing p202-toolbar. Disconnect already handles a webhook that is gone: it catches RecordNotFoundException.

Decisions to check

  • "gone" and "off" say to pair again, not to revive.
  • "unnamed" and an unreadable status are "Status unknown", not "Not receiving". The optimizer's wildcard webhook may still exist and be receiving: enqueue() does not consult lpo_bridge_config. Saying it receives nothing would be a guess in the other direction.
  • The status is read only when lpo_status is active. Unpaired installs render as before.

Tests

  • tests/Lpo/PairingCardWebhookStatusIntegrationTest.php (@group integration, scratch database, 8 tests). It draws the real page as the account through tests/Setup/fixtures/page-runner.php, with the account holding access_to_api_integrations, and reads the card's <section>. The cases:
    • active: Connected, with no flash and no link;
    • dead: the bad pill, the exact flash, and the LTV Settings button;
    • gone: no link;
    • another account's webhook under the paired id reads as gone;
    • disabled;
    • six pairing records that name no webhook: '', not json, {}, "Nabc", 0 and a list;
    • a failed read, with 202_ltv_webhooks renamed away for the request;
    • an unpaired install.
  • tests/Lpo/PairingWebhookTest.php: each answer's sentence, and that only receiving says nothing.
  • Plants. 9, all caught by a named test. For each, the file was backed up to mktemp, compared with cmp and restored:
plant caught by
the pill from lpo_status alone testADeadWebhookSaysTheOptimizerGetsNothingAndWhereToReviveIt
the status never read (always active) same
a missing webhook reads as receiving testAWebhookThatNoLongerExistsReadsAsNotReceiving
another status reads as receiving testADisabledWebhookReadsAsNotReceiving
the id read with (int) testAPairingRecordThatNamesNoWebhookIsNotConnected
a failed read reads as connected testAWebhookThatCannotBeReadIsSaidSoNeverConnected
no LTV Settings link testADeadWebhookSays…
the flash not drawn testAWebhookThatNoLongerExists…
notice() says nothing for dead PairingWebhookTest::testADeadWebhookNamesItsIdAndWhereToReviveIt

MySQL 8 (a49cae7)

On 15c326d, CI's integration job failed in this test's tearDownAfterClass(), after all 8 tests passed. The teardown used RENAME TABLE IF EXISTS, which is MariaDB-only syntax; MySQL 8.0 refuses it. The teardown now reads information_schema.TABLES and then either:

  • renames a table left renamed away back with a plain RENAME TABLE; or
  • drops the copy, where the schema installer has made the table again.

Both branches ran here:

  • a leftover copy beside a fresh table was dropped;
  • with the test's own finally restore removed, the teardown renamed the table back.

Not verified against MySQL 8 locally (only MariaDB 10.11 is here). The statements left are SELECT … FROM information_schema.TABLES, RENAME TABLE and DROP TABLE, which MySQL 8 has. My other two branches' tests (#296, and 2RBE's) rename no table. Their DROP TRIGGER IF EXISTS and CREATE TRIGGER … FOR EACH ROW IF … END IF are MySQL 8 syntax, the form TrackerLinkTransactionIntegrationTest already runs in CI.

Live pass

tests/live/lpo-card-webhook-status.sh (new) ran on an instance from this branch: PHP 8.3.6 php -S through the eval router, MariaDB 10.11, port 8321. 14 passed, 0 failed. The same script against 1.9.79's page gave 4 passed and 10 failed: Connected in every state.

  1. The pass signs in through 202-login.php's own form and makes a wildcard webhook through POST /ltv/webhooks. It then writes the pairing as the card's Connect writes it: lpo_status = active, and lpo_bridge_config naming the webhook. Connect itself was not pressed, because it calls the hosted optimizer (pairInit/pairComplete), which a scratch instance must not reach.
  2. Active: Connected, and nothing more said.
  3. Dead (set in the database, as the dispatcher sets it): "Not receiving events", the exact sentence, and the LTV Settings link. The link answered 200 with the Customer LTV page.
  4. Deleted through DELETE /ltv/webhooks/{id} (204): "Not receiving events", the instruction to pair again, and no link.
  5. A pairing record that names no webhook: "Status unknown", not Connected.

The pass puts the account's pairing back as it found it on exit.

Licence (the stub is down)

  • Install. install.php's POST checks the key with my.tracking202.com, so I did not run it. A scratch script ran the installer's own steps without the licence form: INSTALL::install_databases(), InstallAccount::create(), the 202_api_keys row and install_database_partitions(). AutoCron and the daily email registration, which call outside services, were left out.
  • Signed-in pages. The web server ran with every proxy variable (HTTPS_PROXY, https_proxy, HTTP_PROXY, http_proxy) pointed at http://127.0.0.1:9, a closed port, and with NO_PROXY=localhost,127.0.0.1, so there was no bypass for my.tracking202.com. The licence check then gets no answer, and AUTH::is_valid_api_key() fails open, as it does on any network failure. Every licence check in the server log is Failed to connect to 127.0.0.1 port 9. Nothing reached the licence service, and no code or session was changed for it.
  • Not clicked in a browser. The card was read from the page's HTML.

Verification

.claude/skills/p202-verify/scripts/verify.sh --since origin/1.9.79 --changed, on 15c326d, PHP 8.3.6. The unit tier ran 6,106 tests with 5 skipped: the 4 documented "User class requires DB singleton" skips, and CronEntryPointsFailLoudlyTest's executed case, which skips while the worktree holds the live instance's 202-config.php. a49cae7 changes only the integration test's teardown; that suite was rerun after it (8 tests OK).

Scope report
------------
  patterns   PASS
  phpcs      FAIL
  phpstan    PASS
  syntax     PASS
  unit       PASS

Not covered by this script:
  live      stand up an instance, drive the path a user would take
  agent     add and run a case under tests/fixtures/agent-eval/cases/
  ci        a workflow still in_progress has not passed; read the run

Report the lines above verbatim. Do not write "tests pass".

Why phpcs FAILs. The ratchet counts 202-account/api-integrations.php as made worse: 861 errors against 832 at HEAD, with 90 warnings at both.

Beyond the script:

  • PairingCardWebhookStatusIntegrationTest against a scratch MariaDB 10.11 database: OK, 8 tests, 96 assertions.
  • shellcheck --severity=warning on the new live script: clean.

Not run:

  • a browser pass;
  • the real Connect/Disconnect, which call the hosted optimizer;
  • CI's MySQL 8.0 locally (see above);
  • the full tests/run-integration-suites.sh;
  • CI, until it runs on this PR.

Merging

🤖 Generated with Claude Code

https://claude.ai/code/session_01VRoS2SViuxaucFaKWsFnLr

My Account › API Integrations showed the Landing Page Optimizer pairing as
"Connected" from lpo_status alone. Once the webhook the pairing made was
marked dead (one delivery failed 6 times) or deleted in LTV Settings, the
optimizer stopped receiving events and the card still said Connected.

Prosper202\Lpo\PairingWebhook::state() reads the webhook lpo_bridge_config
names by id, scoped to the account, and answers receiving, dead, off
(disabled), gone or unnamed (the record names no webhook, or not as an
integer above 0). The card reads "Connected" only while it is receiving.
Dead, off and gone read "Not receiving events", with a sentence naming the
webhook: a dead one is revived in LTV Settings once its endpoint answers
again, and the card links there; one that is gone or off is paired again
with Disconnect and Connect. An unnamed record, or a status read that
fails (logged), reads "Status unknown": a read that does not know is
never "Connected" (CLAUDE.md #11).

Tests: PairingWebhookTest (each answer's sentence); PairingCardWebhook
StatusIntegrationTest draws the real page as the account (page-runner)
for each state, another account's webhook under the paired id, a failed
read (the table renamed away) and an unpaired install.
tests/live/lpo-card-webhook-status.sh is the live pass.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VRoS2SViuxaucFaKWsFnLr
Copilot AI balanced review requested due to automatic review settings October 28, 2026 08:38

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has insufficient remaining quota.

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ⚠️ Failed 2026-10-28T08:38:50.715146Z
We couldn’t complete this review. Comment @codex review to try again.
15c326d PR opened
ℹ️ 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" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@github-actions

Copy link
Copy Markdown
Contributor

Code review: CLEAN

For 15c326d: 0 issue(s). The review is encrypted for the maintainers in the review-findings artifact of this run.

@github-actions

github-actions Bot commented Oct 28, 2026 •

Copy link
Copy Markdown
Contributor

Adversarial review: NON-BLOCKING

For 1b1549a: 4 finding(s). The report is encrypted for the maintainers in the review-findings artifact of this run.

Rewritten on every push; the check fails on BLOCKING.

tearDownAfterClass() put a renamed-away 202_ltv_webhooks back with
RENAME TABLE IF EXISTS, which is MariaDB's: MySQL 8.0 refuses it, and CI's
integration job failed there after all eight tests passed. It now reads
information_schema.TABLES and renames the table back, or drops the copy
where the schema installer has made the table again.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VRoS2SViuxaucFaKWsFnLr
Brings in 1.9.79's new commits. The conflicts were in 202-config/changelogs.json, changelogs.txt, tests/live/README.md, 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
@tracking202
tracking202 merged commit 58ce647 into 1.9.79 Oct 28, 2026
23 checks passed
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.

3 participants