feat(fetch): describe a media URL instead of decoding it as text #1120

Merged
coilyco-ops merged 1 commit from aos/claude/fetch-media-1029 into main 2026-08-22 23:13:06 +00:00
Member

Milestone 17 phase 6, working #1029 and #996. This does not close either, so the slice is filed as #1119 and closed here.

The gap the design note implied and nothing enforced

#1029 says the curated list holds two shapes: text-reference sites fetch can read, and media hosts whose URL returns the thing itself. Only the first worked. fetch_url read up to 32 KB of any response and returned it as text regardless of content type, so a gif arrived as thousands of NUL bytes with invalid runes stripped. With the check removed the test prints it:

result = "200\nGIF89a\x00\x00\x00\x00\x00..."

So allowlisting tenor, giphy, or imgur without this buys nothing. The model spends a round to receive garbage and still cannot tell whether the URL is a real asset. That is the same waste #940 measured on this tool from the other direction.

What it does now

A non-text content type returns its type and length and no body. Discord renders a bare media URL as an embed, so the question is only whether the URL is a real asset of the right kind, which is what #1029's own per-entry test asks: "does fetching it return the actual thing".

Readability is matched by shape rather than by an enumeration: text/*, plus +json and +xml by suffix, so a new vendor type does not fall out of a list and arrive as broken text. An absent content type is read, because that is what a plain page used to send.

On #996 and #1029

The milestone asks to check these two for merge before working both. They should merge: #996 is a subset. Its ask is tenor gifs specifically, and #1029's curated list names tenor with its direct asset hosts alongside giphy, imgur, knowyourmeme, and xkcd, plus the exclusions and the per-entry test. I have said so on #996 rather than closing someone else's issue on my own reading.

What stays with deploy

SIRENS_ECHO_FETCH_HOSTS is a deployment variable, so the curated list itself is deploy's to add. This is the harness half that makes adding it worth anything.

closes #1119
Refs #1029, #996, #940

Milestone 17 phase 6, working #1029 and #996. **This does not close either**, so the slice is filed as #1119 and closed here. ## The gap the design note implied and nothing enforced #1029 says the curated list holds two shapes: text-reference sites fetch can read, and **media hosts whose URL returns the thing itself**. Only the first worked. `fetch_url` read up to 32 KB of any response and returned it as text regardless of content type, so a gif arrived as thousands of NUL bytes with invalid runes stripped. With the check removed the test prints it: ``` result = "200\nGIF89a\x00\x00\x00\x00\x00..." ``` **So allowlisting tenor, giphy, or imgur without this buys nothing.** The model spends a round to receive garbage and still cannot tell whether the URL is a real asset. That is the same waste #940 measured on this tool from the other direction. ## What it does now A non-text content type returns its **type and length and no body**. Discord renders a bare media URL as an embed, so the question is only whether the URL is a real asset of the right kind, which is what #1029's own per-entry test asks: "does fetching it return the actual thing". Readability is matched **by shape rather than by an enumeration**: `text/*`, plus `+json` and `+xml` by suffix, so a new vendor type does not fall out of a list and arrive as broken text. An absent content type is read, because that is what a plain page used to send. ## On #996 and #1029 The milestone asks to check these two for merge before working both. **They should merge: #996 is a subset.** Its ask is tenor gifs specifically, and #1029's curated list names tenor with its direct asset hosts alongside giphy, imgur, knowyourmeme, and xkcd, plus the exclusions and the per-entry test. I have said so on #996 rather than closing someone else's issue on my own reading. ## What stays with deploy `SIRENS_ECHO_FETCH_HOSTS` is a deployment variable, so the curated list itself is deploy's to add. This is the harness half that makes adding it worth anything. closes #1119 Refs #1029, #996, #940
feat(fetch): describe a media URL instead of decoding it as text
All checks were successful
ci / image-build (pull_request) Successful in 51s
ci / test (pull_request) Successful in 1m15s
ci / publish-echo-image (pull_request) Has been skipped
ci / publish-observed (pull_request) Has been skipped
100ad10941
#1029's design note names two shapes in the curated list: text-reference
sites whose pages fetch can read, and media hosts whose URL returns the
thing itself. Only the first worked. fetch_url read up to 32 KB of any
response and returned it as text regardless of content type, so a gif came
back as thousands of NUL bytes with the invalid runes stripped, which is
what the test reports when the check is removed.

Allowlisting tenor, giphy, or imgur without this buys nothing: the model
spends a round to receive garbage and still cannot tell whether the URL is
a real asset.

A non-text content type now returns its type and length and no body, which
is what the caller actually needs, because Discord renders a bare media URL
as an embed and the question is only whether the URL is a real asset of the
right kind.

Readability is matched by shape rather than by an enumeration: text/*, and
+json and +xml by suffix, so a new vendor type does not fall out of a list
and arrive as broken text. An absent content type is read, because that is
what a plain page used to send.

The allowlist entries themselves are SIRENS_ECHO_FETCH_HOSTS and belong to
the deployment, so this is the harness half only.

Refs #1029, #996

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: Kai Siren <coilysiren@gmail.com>
Co-authored-by: Claude <noreply@anthropic.com>
Agent-Role: engineer
Author
Member

Two things before this merges, one of them time-sensitive

Darren (director seat). The change itself is right, and the reasoning that a media URL only needs to be confirmed as a real asset, because Discord renders the embed, is the part that makes the small answer the correct one rather than the lazy one. Matching readability by shape with text/* plus +json and +xml suffixes, rather than by an enumeration, is what keeps it from rotting the next time a vendor invents a type.

1. Refs #1029, #996, #940 will close all three

Refs #N is acting as a closing keyword in this Forgejo. Twice today already:

  • #1106 carried Refs #1083 and said in its body that #1083 stays open. #1083 closed on merge and I reopened it.
  • #1114 carried Refs #940 and opened with "This does not close #940". #940 closed on merge and I reopened it.

Your body opens with the same sentence and the same trailer, so #1029 and #940 will close again the moment this merges, and #940 will have been closed against its author's explicit written intent twice in one evening.

closes #1119 already satisfies the merge verb's closing-reference requirement, so the second line costs you nothing to drop. Reference them in prose instead and the record survives the merge.

2. #996's disposition changed forty minutes ago, and yours is now the second one

You read #996 as a subset of #1029 that should merge into it. That is a defensible reading and it is overtaken.

Kai decided #996 belongs in agentic-os and I refiled it there as coilyco-flight-deck/agentic-os#1190 at 22:15, closing #996 as moved. She had said so twice on the issue on 2026-08-19, and I asked her to confirm before acting. The retrieval flow it describes is generic web tooling rather than Echo runtime, so it left this milestone's tail entirely.

So the record now has two dispositions for one issue. Yours is not wrong about the overlap, and #1190 should probably carry a line pointing at #1029 for the reading half, but the merge-into-#1029 route is closed. Worth editing that section before this lands so the next reader is not choosing between two answers.

On the deploy half

You are right that SIRENS_ECHO_FETCH_HOSTS is deploy's. That grant is already filed, at coilyco-bridge/deploy#772, from the handoff #1029 announced in a comment on 2026-08-19 and nobody filed until today. Link it in your body so the harness half and the grant find each other, since between them they are the whole of #1029.

## Two things before this merges, one of them time-sensitive **Darren (director seat).** The change itself is right, and the reasoning that a media URL only needs to be confirmed as a real asset, because Discord renders the embed, is the part that makes the small answer the correct one rather than the lazy one. Matching readability by shape with `text/*` plus `+json` and `+xml` suffixes, rather than by an enumeration, is what keeps it from rotting the next time a vendor invents a type. ### 1. `Refs #1029, #996, #940` will close all three **`Refs #N` is acting as a closing keyword in this Forgejo.** Twice today already: * #1106 carried `Refs #1083` and said in its body that #1083 stays open. #1083 closed on merge and I reopened it. * #1114 carried `Refs #940` and opened with "This does not close #940". #940 closed on merge and I reopened it. Your body opens with the same sentence and the same trailer, so **#1029 and #940 will close again the moment this merges**, and #940 will have been closed against its author's explicit written intent twice in one evening. `closes #1119` already satisfies the merge verb's closing-reference requirement, so the second line costs you nothing to drop. **Reference them in prose instead** and the record survives the merge. ### 2. #996's disposition changed forty minutes ago, and yours is now the second one You read #996 as a subset of #1029 that should merge into it. That is a defensible reading and it is overtaken. **Kai decided #996 belongs in `agentic-os` and I refiled it there as `coilyco-flight-deck/agentic-os#1190` at 22:15, closing #996 as moved.** She had said so twice on the issue on 2026-08-19, and I asked her to confirm before acting. The retrieval flow it describes is generic web tooling rather than Echo runtime, so it left this milestone's tail entirely. So the record now has two dispositions for one issue. **Yours is not wrong about the overlap**, and #1190 should probably carry a line pointing at #1029 for the reading half, but the merge-into-#1029 route is closed. Worth editing that section before this lands so the next reader is not choosing between two answers. ### On the deploy half You are right that `SIRENS_ECHO_FETCH_HOSTS` is deploy's. That grant is already filed, at `coilyco-bridge/deploy#772`, from the handoff #1029 announced in a comment on 2026-08-19 and nobody filed until today. **Link it in your body** so the harness half and the grant find each other, since between them they are the whole of #1029.
coilyco-ops deleted branch aos/claude/fetch-media-1029 2026-08-22 23:13:07 +00:00
Sign in to join this conversation.
No reviewers
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
coilyco-gaming/sirens-echo!1120
No description provided.