Skip to content

fix(channels): normalize phone identities with stray plus signs#100467

Merged
steipete merged 8 commits into
openclaw:mainfrom
morluto:fix/normalize-e164-phone-digits
Jul 6, 2026
Merged

fix(channels): normalize phone identities with stray plus signs#100467
steipete merged 8 commits into
openclaw:mainfrom
morluto:fix/normalize-e164-phone-digits

Conversation

@morluto

@morluto morluto commented Jul 5, 2026

Copy link
Copy Markdown
Contributor

What Problem This Solves

Phone-based channel identities could normalize to unusable values when copied input contained doubled or embedded + characters. Digit-free input could also become the truthy identity +, causing incorrect routing comparisons instead of a clean invalid-input result.

Why This Change Was Made

The shared normalizeE164 helper now strips provider prefixes and formatting, keeps digits only, returns exactly one leading +, and returns an empty string when no digits remain.

The maintainer rewrite also closes sibling cases exposed by that shared invariant:

  • iMessage keeps non-phone auto: handles instead of collapsing them.
  • Signal falls back to UUID identity when a source number contains no digits and rejects digit-free allowlist entries.
  • Signal operator account setup remains strict about embedded or duplicate plus signs.
  • WhatsApp setup tests exercise the real shared normalizer instead of a policy-copying mock.

User Impact

Equivalent phone identities compare canonically across shared channel routing even when copied input contains stray plus signs. Invalid digit-free identities fail closed, while valid non-phone iMessage handles and Signal UUID senders retain intended routing.

Evidence

  • Exact head: aea114112f975285bdf4c7bbf9ba163ca171e372.
  • Sanitized AWS Crabbox run run_2cb4c42e20e4 found the prefixed Signal setup regression after 96 focused assertions passed. The branch was repaired to preserve strict setup validation and gained a focused regression.
  • Source-blind final-code runtime contract passed: noisy UK/US numbers canonicalized; digit-free input returned empty; auto:Alice Smith remained auto:AliceSmith; digit-free Signal phone identity fell back to UUID; prefixed allowlist input parsed; embedded-plus setup input was rejected.
  • Exact-head hosted CI run 28761404940: all required checks passed.
  • Fresh exact-head autoreview: clean, no actionable findings, confidence 0.93.
  • Contributor-credit changelog entry is carried by final batch closeout PR fix(tlon): bound urbit scry JSON response reads #100376, sequenced after this merge.
  • Targeted oxfmt and git diff --check: pass.
  • Focused duplicate search found no competing open shared E.164, WhatsApp, iMessage, or Signal normalization fix.

Risk checklist

  • User-visible behavior: yes, malformed copied phone identities now canonicalize or fail closed.
  • Config/environment/migration: no change.
  • Security/auth/network/tool execution: no change.
  • Highest risk: channel identity matching and operator input validation.
  • Mitigation: one permissive shared normalizer, strict Signal setup boundary, sibling channel regressions.

AI-assisted disclosure

  • AI-assisted maintainer rewrite and review
  • Source-blind behavior proof
  • Fresh structured autoreview

@openclaw-barnacle openclaw-barnacle Bot added channel: whatsapp-web Channel integration: whatsapp-web size: XS labels Jul 5, 2026
@morluto
morluto marked this pull request as ready for review July 5, 2026 21:45
@chatgpt-codex-connector

Copy link
Copy Markdown
Contributor

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

@clawsweeper

clawsweeper Bot commented Jul 5, 2026

Copy link
Copy Markdown
Contributor

Codex review: found issues before merge. Reviewed July 5, 2026, 9:05 PM ET / 01:05 UTC.

Summary
The PR changes shared phone normalization plus Signal, iMessage, and WhatsApp-adjacent tests so noisy phone identities canonicalize to +digits or empty instead of preserving stray plus signs.

PR surface: Source +3, Tests +29, Docs +1. Total +33 across 8 files.

Reproducibility: yes. Source inspection on current main shows normalizeE164 can return a truthy bare + for digit-free input and preserve embedded plus signs, and the affected callers treat that as a usable identity.

Review metrics: 3 noteworthy metrics.

  • Exported Helper Behavior: 1 changed. normalizeE164 is exported through plugin SDK subpaths, so the helper change has compatibility impact beyond bundled channel code.
  • Open Normalizer Overlap: 1 overlapping PR. The no-digit subset is also proposed in fix(utils): normalizeE164 returns "" when there are no digits #95974, so maintainers should keep one canonical path.
  • Release-Owned Changelog: 1 modified. The diff still edits the release-generated changelog, which should be handled before merge.

Root-cause cluster
Relationship: canonical
Canonical: #100467
Summary: This PR is the broader canonical candidate for shared phone normalizer handling; the older open PR covers only the no-digit subset, while the Signal username PR is adjacent but distinct.

Members:

Proposal only: this assessment does not dispatch repair, suppress jobs, mutate sibling items, close, or merge anything.

Merge readiness
Overall: 🐚 platinum hermit
Proof: 🐚 platinum hermit
Patch quality: 🐚 platinum hermit
Result: ready for maintainer review.

Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch.

Rank-up moves:

  • Remove the CHANGELOG.md entry from this normal PR.
  • Get maintainer acceptance of the exported normalizeE164 contract change or switch to caller-local guards.

Risk before merge

  • [P1] Merging intentionally changes the exported normalizeE164 helper: plugin callers that previously received + or embedded-plus-preserving output for invalid/noisy input will now receive empty or canonical +digits output.
  • [P1] fix(utils): normalizeE164 returns "" when there are no digits #95974 overlaps on the no-digit subset, so maintainers should choose one canonical path and retire or supersede the other branch after landing.
  • [P1] The PR still edits CHANGELOG.md, which is release-owned for normal OpenClaw PRs.

Maintainer options:

  1. Accept The SDK Contract Correction (recommended)
    Maintainers can own the exported helper behavior change because the previous outputs were not valid phone identities, then remove the changelog entry before merge.
  2. Keep SDK Compatibility And Guard Callers
    Repair the branch so the shared helper keeps its old output while Signal, iMessage, WhatsApp, and session routing reject invalid identities locally.
  3. Pause For Overlap Cleanup
    Wait to merge until maintainers decide how this PR relates to the narrower open no-digit normalizer PR.

Next step before merge

  • [P2] Manual review is needed for the SDK-visible compatibility decision and overlap with the narrower open normalizer PR; the only mechanical branch cleanup is the release-owned changelog entry.

Maintainer decision needed

  • Question: Should OpenClaw accept the SDK-visible normalizeE164 behavior change for invalid and noisy phone input, or preserve the old helper contract and guard only selected callers?
  • Rationale: The shared helper change is the cleanest fix across Signal, iMessage, WhatsApp, and session routing, but the helper is exported through plugin SDK subpaths so compatibility acceptance is a maintainer choice.
  • Likely owner: steipete — This handle is assigned on the PR and authored the later branch commits that broadened the fix across the caller surfaces.
  • Options:
    • Accept Shared Helper Correction (recommended): Treat bare + and embedded-plus outputs as invalid old behavior, remove the changelog edit, land this broader PR, and retire the overlapping no-digit-only PR afterward.
    • Preserve SDK Behavior For Now: Keep normalizeE164 compatible and move empty/noisy-input guards into the specific channel/session callers that need stricter handling.
    • Pause For Consolidation: Hold this PR until maintainers decide whether this branch or fix(utils): normalizeE164 returns "" when there are no digits #95974 should own the normalizer fix.

Security
Cleared: The diff changes deterministic string normalization, channel validation/tests, and changelog text; no concrete security or supply-chain regression was found.

Review findings

  • [P3] Remove the release-owned changelog entry — CHANGELOG.md:41
Review details

Best possible solution:

Land this broader shared normalizer fix as the canonical path after maintainer acceptance of the SDK-visible behavior change, remove the changelog entry, and then retire the narrower overlapping PR.

Do we have a high-confidence way to reproduce the issue?

Yes. Source inspection on current main shows normalizeE164 can return a truthy bare + for digit-free input and preserve embedded plus signs, and the affected callers treat that as a usable identity.

Is this the best way to solve the issue?

Yes, if maintainers accept the SDK contract correction: the shared helper is the narrowest durable fix for the common invariant. The safer compatibility alternative is caller-local guards plus a later documented SDK change.

Full review comments:

  • [P3] Remove the release-owned changelog entry — CHANGELOG.md:41
    This was raised in the prior cycle and is still present. CHANGELOG.md is release-generated for normal OpenClaw PRs; the release-note context is already in the PR body, so keeping this entry makes release-owned output part of the contributor branch.
    Confidence: 0.9

Overall correctness: patch is correct
Overall confidence: 0.88

AGENTS.md: found and applied where relevant.

Codex review notes: model internal, reasoning high; reviewed against 5a31666a06e5.

Label changes

Label justifications:

  • P2: This is a focused channel identity bug fix with limited blast radius and no evidence of an urgent live outage.
  • merge-risk: 🚨 compatibility: The PR changes the behavior of an exported Plugin SDK helper used by bundled and external plugin callers.
  • rating: 🐚 platinum hermit: Overall readiness is 🐚 platinum hermit; proof is 🐚 platinum hermit and patch quality is 🐚 platinum hermit.
  • status: 👀 ready for maintainer look: ClawSweeper has no concrete contributor-facing blocker left for this PR. Sufficient (live_output): The PR body and maintainer closeout include source-blind runtime proof for the changed identity behavior, and the exact-head Real behavior proof check succeeded.
  • proof: sufficient: Contributor real behavior proof is sufficient. The PR body and maintainer closeout include source-blind runtime proof for the changed identity behavior, and the exact-head Real behavior proof check succeeded.
Evidence reviewed

PR surface:

Source +3, Tests +29, Docs +1. Total +33 across 8 files.

View PR surface stats
Area Files Added Removed Net
Source 3 15 12 +3
Tests 4 39 10 +29
Docs 1 1 0 +1
Config 0 0 0 0
Generated 0 0 0 0
Other 0 0 0 0
Total 8 55 22 +33

What I checked:

  • Current main reproduces the bad normalizer output: On current main, normalizeE164 preserves plus characters and returns + when no digits remain, which makes invalid phone-like identities truthy. (src/utils.ts:56, 5a31666a06e5)
  • PR head changes the shared helper contract: At PR head, normalizeE164 strips non-digits and returns an empty string for digit-free input, making noisy copied phone input canonical and invalid input falsy. (src/utils.ts:56, 5eab941568c9)
  • Plugin SDK export surface is affected: The helper is re-exported through plugin SDK subpaths, so this is visible to bundled and third-party plugin callers rather than only one internal channel. (src/plugin-sdk/account-core.ts:21, 5a31666a06e5)
  • Sibling channel callers are covered: The PR guards Signal sender and allowlist parsing against empty normalized phones and preserves fallback to UUID when the source number is digit-free. (extensions/signal/src/identity.ts:25, 5eab941568c9)
  • Strict setup boundary is preserved: Signal setup still rejects embedded or duplicate plus signs while allowing the explicit signal: prefix before normalization. (extensions/signal/src/setup-core.ts:44, 5eab941568c9)
  • Regression tests cover the shared invariant: The PR adds table coverage for duplicate and embedded pluses, provider prefixes, and digit-free input returning empty. (src/utils.test.ts:60, 5eab941568c9)

Likely related people:

  • steipete: This handle is assigned on the live PR, authored the later Signal/WhatsApp/changelog repair commits on the branch, and current shallow blame points the existing helper/channel code to a recent main commit by Peter Steinberger. (role: recent area contributor and likely decision owner; confidence: high; commits: 8c19dbfb627c, 9abb08a67056, 065b4764dab8; files: src/utils.ts, extensions/signal/src/identity.ts, extensions/signal/src/setup-core.ts)
  • vincentkoc: Merged prior WhatsApp onboarding allowlist normalization work that shares the phone identity normalization boundary affected by this PR. (role: adjacent WhatsApp normalization contributor; confidence: medium; commits: 486aa6ce299a, 50655635590b; files: extensions/whatsapp/src/channel.setup.test.ts, extensions/whatsapp/src/normalize-target.ts, extensions/whatsapp/src/setup-finalize.ts)
  • MatthewDelprado: Authored the recent merged iMessage target-normalization fix that prevents non-phone identifiers from falling through to phone normalization. (role: adjacent iMessage normalization contributor; confidence: medium; commits: aa20678215f9; files: extensions/imessage/src/normalize.ts, extensions/imessage/src/normalize.test.ts, extensions/imessage/src/targets.ts)
  • he-yufeng: Authored the still-open related PR that fixes the no-digit subset of normalizeE164, making them useful context for consolidation even though the branch is not the broader current-main owner. (role: overlapping normalizer PR contributor; confidence: medium; commits: 2d40ae3be89d; files: src/utils.ts, src/utils.test.ts)
What the crustacean ranks mean
  • 🦀 challenger crab: rare, exceptional readiness with strong proof, clean implementation, and convincing validation.
  • 🦞 diamond lobster: very strong readiness with only minor maintainer review expected.
  • 🐚 platinum hermit: good normal PR, likely mergeable with ordinary maintainer review.
  • 🦐 gold shrimp: useful signal, but proof or patch confidence is still limited.
  • 🦪 silver shellfish: thin signal; proof, validation, or implementation needs work.
  • 🧂 unranked krab: not merge-ready because proof is missing/unusable or there are serious correctness or safety concerns.
  • 🌊 off-meta tidepool: rating does not apply to this item.

Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics.

How this review workflow works
  • ClawSweeper keeps one durable marker-backed review comment per issue or PR.
  • Re-runs edit this comment so the latest verdict, findings, and automation markers stay together instead of adding duplicate bot comments.
  • A fresh review can be triggered by eligible @clawsweeper re-review comments, exact-item GitHub events, scheduled/background review runs, or manual workflow dispatch.
  • PR/issue authors and users with repository write access can comment @clawsweeper re-review or @clawsweeper re-run on an open PR or issue to request a fresh review only.
  • Maintainers can also comment @clawsweeper review to request a fresh review only.
  • Fresh-review commands do not start repair, autofix, rebase, CI repair, or automerge.
  • Maintainer-only repair and merge flows require explicit commands such as @clawsweeper autofix, @clawsweeper automerge, @clawsweeper fix ci, or @clawsweeper address review.
  • Maintainers can comment @clawsweeper explain to ask for more context, or @clawsweeper stop to stop active automation.
Review history (4 earlier review cycles)
  • reviewed 2026-07-05T22:16:30.088Z sha 4e04390 :: needs real behavior proof before merge. :: none
  • reviewed 2026-07-05T22:35:21.794Z sha 4e04390 :: needs maintainer review before merge. :: none
  • reviewed 2026-07-06T00:00:22.165Z sha 67d01b9 :: found issues before merge. :: [P3] Remove the release-owned changelog edit
  • reviewed 2026-07-06T00:44:40.750Z sha d7743db :: found issues before merge. :: [P3] Remove the release-owned changelog edit

@clawsweeper clawsweeper Bot added rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. P2 Normal backlog priority with limited blast radius. merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. labels Jul 5, 2026
@morluto

morluto commented Jul 5, 2026

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

@clawsweeper

clawsweeper Bot commented Jul 5, 2026

Copy link
Copy Markdown
Contributor

🦞🧹
ClawSweeper re-review requested.

I asked ClawSweeper to review this item again.
Action: item re-review queued (workflow sweep.yml, event repository_dispatch).
Result: the existing ClawSweeper review comment will be edited in place when the review finishes.

@steipete steipete self-assigned this Jul 5, 2026
@clawsweeper clawsweeper Bot added proof: sufficient ClawSweeper judged the real behavior proof convincing. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. and removed rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. labels Jul 5, 2026
@steipete
steipete force-pushed the fix/normalize-e164-phone-digits branch from 4e04390 to c6e38db Compare July 5, 2026 23:46
@openclaw-barnacle openclaw-barnacle Bot added channel: imessage Channel integration: imessage channel: signal Channel integration: signal size: S and removed size: XS labels Jul 5, 2026
@steipete
steipete force-pushed the fix/normalize-e164-phone-digits branch 3 times, most recently from 87daa1c to d7743db Compare July 6, 2026 00:33
@clawsweeper clawsweeper Bot added proof: sufficient ClawSweeper judged the real behavior proof convincing. and removed proof: sufficient ClawSweeper judged the real behavior proof convincing. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. labels Jul 6, 2026
@clawsweeper clawsweeper Bot added rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. labels Jul 6, 2026
@steipete

steipete commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Maintainer closeout at exact head aea114112f975285bdf4c7bbf9ba163ca171e372:

  • Centralized phone normalization in normalizeE164: provider prefixes and formatting are stripped, digit-free inputs stay empty, and valid numbers produce exactly one leading +.
  • Migrated WhatsApp, iMessage phone handles, and Signal identity/setup paths to that contract while preserving non-phone iMessage handles and Signal UUID fallback.
  • Tightened Signal setup validation for embedded or duplicate + without rejecting an accepted signal:+... prefix.
  • Sanitized AWS Crabbox run run_2cb4c42e20e4: 96 focused assertions passed and exposed the initial Signal setup regression before the final fix.
  • Source-blind final runtime probe passed canonicalization, digit-free rejection, iMessage handle preservation, Signal UUID fallback, invalid allowlist rejection, accepted prefixed setup, and embedded-plus rejection.
  • Exact-head hosted CI run 28761404940: all required checks passed.
  • Fresh exact-head autoreview: clean, no actionable findings, confidence 0.93.
  • Targeted formatting and git diff --check: pass.
  • Contributor-credit changelog entry is carried by final batch closeout PR fix(tlon): bound urbit scry JSON response reads #100376, sequenced after this merge.

Proof gap: the organization-wide Crabbox lease cap prevented a second sanitized AWS allocation after the final one-line validation repair. The repaired exact head is covered by source-blind runtime proof and hosted CI.

@openclaw-barnacle openclaw-barnacle Bot added scripts Repository scripts size: M gateway Gateway runtime and removed size: S labels Jul 6, 2026
@steipete
steipete force-pushed the fix/normalize-e164-phone-digits branch from 70e26ec to 5eab941 Compare July 6, 2026 00:58
@openclaw-barnacle openclaw-barnacle Bot added size: S and removed gateway Gateway runtime scripts Repository scripts size: M labels Jul 6, 2026
@steipete
steipete force-pushed the fix/normalize-e164-phone-digits branch from 5eab941 to aea1141 Compare July 6, 2026 01:06
@steipete
steipete merged commit f1a374a into openclaw:main Jul 6, 2026
102 checks passed
@steipete

steipete commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Merged via squash.

github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request Jul 6, 2026
…claw#100467)

* fix(channels): canonicalize phone identity digits

* test(channels): cover phone identity normalization

* test(whatsapp): use shared phone normalizer

* fix(channels): reject invalid normalized phone identities

* fix(signal): preserve strict account input validation

* fix(signal): preserve prefixed allowlist input

* docs(changelog): stabilize phone identity entry

* chore(changelog): defer phone identity entry

---------

Co-authored-by: Peter Steinberger <[email protected]>
giodl73-repo pushed a commit to giodl73-repo/openclaw that referenced this pull request Jul 8, 2026
…claw#100467)

* fix(channels): canonicalize phone identity digits

* test(channels): cover phone identity normalization

* test(whatsapp): use shared phone normalizer

* fix(channels): reject invalid normalized phone identities

* fix(signal): preserve strict account input validation

* fix(signal): preserve prefixed allowlist input

* docs(changelog): stabilize phone identity entry

* chore(changelog): defer phone identity entry

---------

Co-authored-by: Peter Steinberger <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

channel: imessage Channel integration: imessage channel: signal Channel integration: signal channel: whatsapp-web Channel integration: whatsapp-web merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. P2 Normal backlog priority with limited blast radius. proof: sufficient ClawSweeper judged the real behavior proof convincing. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. size: S status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants