fix(tlon): send fragment image URLs as media#104853
Conversation
Co-authored-by: chatgpt-codex-connector[bot] <199175422+chatgpt-codex-connector[bot]@users.noreply.github.com>
Co-authored-by: chatgpt-codex-connector[bot] <199175422+chatgpt-codex-connector[bot]@users.noreply.github.com>
|
Added a stronger real-user behavior proof in The new test exercises the actual outbound plugin send chain: It sends Validation: |
|
Codex review: needs maintainer review before merge. Reviewed July 12, 2026, 1:42 AM ET / 05:42 UTC. Summary PR surface: Source +6, Tests +38. Total +44 across 2 files. Reproducibility: yes. On current main, Review metrics: none identified. Root-cause cluster Members:
Proposal only: this assessment does not dispatch repair, suppress jobs, mutate sibling items, close, or merge anything. Merge readiness Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch. Next step before merge
Security Review detailsBest possible solution: Land the Tlon-local pathname classifier with both regression cases after the normal exact-head checks, then close the linked issue through the merged fix. Do we have a high-confidence way to reproduce the issue? Yes. On current main, Is this the best way to solve the issue? Yes. Matching the parsed pathname is the narrowest maintainable fix because it accepts fragments after real image paths while rejecting image-looking query or fragment text on non-image paths. AGENTS.md: found and applied where relevant. Codex review notes: model internal, reasoning high; reviewed against 8f8b44a3ce80. Label changesLabel justifications:
Evidence reviewedPR surface: Source +6, Tests +38. Total +44 across 2 files. View PR surface stats
What I checked:
Likely related people:
What the crustacean ranks mean
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
Review history (8 earlier review cycles)
|
Keep the PR evidence scoped to the helper-level regression test until live Tlon proof is available. The mocked outbound test was not sufficient for the reviewer-requested live behavior proof and triggered serialized-state review noise. Co-authored-by: chatgpt-codex-connector[bot] <199175422+chatgpt-codex-connector[bot]@users.noreply.github.com>
Co-authored-by: chatgpt-codex-connector[bot] <199175422+chatgpt-codex-connector[bot]@users.noreply.github.com>
Regenerate docs/docs_map.md after syncing with upstream/main so the docs map check passes on this PR branch. Co-authored-by: chatgpt-codex-connector[bot] <199175422+chatgpt-codex-connector[bot]@users.noreply.github.com>
Real Tlon behavior proofI added a local live proof pass against a real WSL-hosted Urbit comet, not a mocked sender. Setup
Runtime fallback proofThe live run hit the upload fallback path and preserved the original fragment-bearing URL: {
"ok": true,
"fallbackPreservedOriginalUrl": true,
"hasFallbackWarning": true,
"warnings": [
"[tlon] Failed to upload image, using original URL: MediaFetchError: Failed to fetch media from <redacted:TLON_MEDIA_URL>: getaddrinfo ENOENT raw.githubusercontent.com"
]
}Real Urbit poke payload proofThe outgoing {
"mark": "chat-dm-action",
"json": {
"diff": {
"delta": {
"add": {
"memo": {
"content": [
{
"inline": [
"OpenClaw PR #104853 captured live proof 2026-07-12T03:53:07.183Z"
]
},
{
"block": {
"image": {
"src": "<redacted:TLON_MEDIA_URL>",
"height": 0,
"width": 0,
"alt": ""
}
}
}
]
}
}
}
}
}
}The same run reported: {
"capturedPokeImageBlocks": [
{
"block": {
"image": {
"src": "<redacted:TLON_MEDIA_URL>",
"height": 0,
"width": 0,
"alt": ""
}
}
}
]
}Send receipt proofThe live send returned a Tlon media receipt: {
"primaryPlatformMessageId": "<redacted:TLON_SHIP>/170.141.184.508.057.993.689.001.489.075.536.727.638",
"parts": [
{
"kind": "media",
"threadId": "<redacted:TLON_TARGET>"
}
]
}Tlon UI proofAfter logging into the same real comet's Tlon UI, the Groups app showed the delivered DM row as an image message: {
"url": "http://localhost:8080/apps/groups/Home",
"containsProofNeedle": true,
"containsImageMarker": true,
"text": "... VectorPeak\n~pilbyt-nidnyt\nOpenClaw PR #104853 captured live proof 2026-07-12T03:53:07.183Z (Image)\n11:53 ..."
}This verifies the reviewed behavior through a live Tlon stack: a fragment-suffixed image URL survives upload fallback, is classified as an image block, is sent via a real Urbit poke, and appears in Tlon UI as an image message rather than a plain link. @clawsweeper re-review |
|
🦞🧹 I asked ClawSweeper to review this item again. Re-review progress:
|
Co-authored-by: chatgpt-codex-connector[bot] <199175422+chatgpt-codex-connector[bot]@users.noreply.github.com>
|
Updated the PR for the reviewer-requested patch-quality issue. What changed: I also added the negative regression case from review: Validation: The earlier redacted live Tlon proof remains valid for the positive behavior; this follow-up narrows the classifier so fragment text cannot create the false-positive media delivery case. @clawsweeper re-review |
|
🦞🧹 I asked ClawSweeper to review this item again. Re-review progress:
|
|
Current head has been synced with latest upstream/main and still only changes the Tlon-local classifier/test surface. The stale review marker is expected after the main sync. Requesting a fresh review for current head @clawsweeper re-review |
|
🦞🧹 I asked ClawSweeper to review this item again. Re-review progress:
|
|
Current head is now @clawsweeper re-review |
|
🦞🧹 I asked ClawSweeper to review this item again. Re-review progress:
|
|
Current head has been synced with latest upstream/main again to clear the BEHIND tail. Validation after sync: ` ext Test Files 2 passed (2) The PR diff remains limited to the Tlon-local classifier/test surface:
Requesting fresh exact-head review for $head. @clawsweeper re-review |
|
🦞🧹 I asked ClawSweeper to review this item again. Re-review progress:
|
Co-authored-by: VectorPeak <[email protected]>
|
Land-ready verification (maintainer):
No remaining proof gaps. |
|
Merged via squash.
|
* fix(tlon): keep fragment image URLs as media Co-authored-by: chatgpt-codex-connector[bot] <199175422+chatgpt-codex-connector[bot]@users.noreply.github.com> * test(tlon): prove fragment image media send path Co-authored-by: chatgpt-codex-connector[bot] <199175422+chatgpt-codex-connector[bot]@users.noreply.github.com> * test(tlon): remove mocked fragment outbound proof Keep the PR evidence scoped to the helper-level regression test until live Tlon proof is available. The mocked outbound test was not sufficient for the reviewer-requested live behavior proof and triggered serialized-state review noise. Co-authored-by: chatgpt-codex-connector[bot] <199175422+chatgpt-codex-connector[bot]@users.noreply.github.com> * docs: refresh docs map for CI Regenerate docs/docs_map.md after syncing with upstream/main so the docs map check passes on this PR branch. Co-authored-by: chatgpt-codex-connector[bot] <199175422+chatgpt-codex-connector[bot]@users.noreply.github.com> * fix(tlon): constrain fragment image URL matching Co-authored-by: chatgpt-codex-connector[bot] <199175422+chatgpt-codex-connector[bot]@users.noreply.github.com> * fix(tlon): harden image URL classification Co-authored-by: VectorPeak <[email protected]> --------- Co-authored-by: chatgpt-codex-connector[bot] <199175422+chatgpt-codex-connector[bot]@users.noreply.github.com> Co-authored-by: VectorPeak <[email protected]> Co-authored-by: Peter Steinberger <[email protected]>
Related: #104852
AI-assisted PR. I reviewed the diff, focused validation output, and redacted live Tlon proof before updating this PR.
What Problem This Solves
Fixes an issue where Tlon users sending image media URLs with fragment suffixes, such as
https://cdn.example/image.png#preview, could have those URLs classified as ordinary links instead of image blocks.The outbound media story path already treated plain image URLs and query-suffixed image URLs as image blocks. Fragment-bearing image URLs were the missing sibling case when the original URL reaches the story builder, especially after upload fallback preserves the source URL.
Why This Change Was Made
The fix stays inside Tlon's local image URL classifier, but it now follows the reviewer-requested narrower rule: classify image-ness from the parsed URL pathname, not from query or fragment text.
That keeps the desired behavior for
https://cdn.example/image.png#preview, while avoiding the false positive called out in review:https://cdn.example/page#preview.pngremains a normal link because its pathname is/page, not an image filename.This does not change upload behavior, URL fetching, auth, gateway routing, persisted state, migrations, schema, upgrade behavior, or emitted URL preservation. The original URL, including query or fragment, is still preserved as the emitted Tlon
block.image.srcwhen the path itself is an image.User Impact
Tlon users can send image URLs that include fragments and still get the expected image media block instead of a plain clickable link when the Tlon send path builds a story from that URL.
For example,
https://cdn.example/image.png#previewnow follows the same media story classification path ashttps://cdn.example/image.pngandhttps://cdn.example/image.png?token=1, whilehttps://cdn.example/page#preview.pngremains a link.Evidence
Before the fix, the focused local probe showed the mismatch:
The regression coverage now includes both sides of the reviewer-requested behavior:
I also added redacted live proof from a real WSL-hosted Urbit comet/Tlon setup in this PR discussion. That run exercised:
The proof comment includes redacted fallback output, the redacted outgoing
chat-dm-actionpayload containingblock.image.src, the media receipt shape, and a redacted Tlon UI observation. Private ship identifiers, login code, cookies, target, and media host details were not exposed.Possible call chain / impact
The relevant source-level fallback chain is:
This PR only changes the Tlon-local classifier used by that story-building branch. Sibling behavior for plain image URLs, query-suffixed image URLs, upload success, auth, routing, and non-image URL paths is intentionally unchanged.
Validation
Result: