Worked examples.
Copy-paste recipes for the patterns Threads actually use. Grouped by what you're trying to accomplish.
1. Post a tweet on X via XHR replay (bypass Draft.js)
X composer is Draft.js — synthetic clipboard events don't reach EditorState. The proven path is replaying the GraphQL mutation that posting fires.
# 1. Install persistent XHR interceptor — survives navigations
inject_on_new_document(session="slot-1", tab="x-tab", script="""
(() => {
const oO=XMLHttpRequest.prototype.open, oS=XMLHttpRequest.prototype.send, oH=XMLHttpRequest.prototype.setRequestHeader;
XMLHttpRequest.prototype.open=function(m,u,...r){this.__url=u;this.__method=m;this.__headers={};return oO.apply(this,[m,u,...r])};
XMLHttpRequest.prototype.setRequestHeader=function(k,v){if(this.__headers)this.__headers[k]=v;return oH.apply(this,[k,v])};
XMLHttpRequest.prototype.send=function(b){
if(this.__url?.includes('CreateTweet')){
window.__xhrCaptures = window.__xhrCaptures || [];
window.__xhrCaptures.push({url:this.__url, method:this.__method, headers:this.__headers, body:b});
}
return oS.apply(this,[b]);
};
})();
""")
# 2. User posts ONE seed tweet manually (humans, not bots — captures auth/csrf/transaction-id)
# 3. Replay with new text
eval_js(code="""
(async (newText) => {
const c = window.__xhrCaptures.at(-1);
const body = JSON.parse(c.body);
body.variables.tweet_text = newText;
const r = await fetch(c.url, {method:'POST', credentials:'include', headers:c.headers, body: JSON.stringify(body)});
const data = await r.json();
return {ok: !!data?.data?.create_tweet?.tweet_results?.result, status: r.status};
})("Hello from WebLoom")
""")2. Reply to a tweet via the UI (draftjs_set_text)
If you don't want to fight XHR signing, drive the reply composer directly:
navigate(url="https://x.com/yourhandle/status/<tweet_id>") # click the reply button (data-testid stable) click(selector='[data-testid="reply"]') # fill the reply composer — defaults to 80ms/char which is safe for most builds draftjs_set_text(container_selector='[data-testid="tweetTextarea_0"]', text="Your reply") # submit click(selector='[data-testid="tweetButtonInline"]')
3. Submit a Reddit comment
One-call helper that handles Lexical composer mounting + Reddit's churned selectors:
reddit_submit_comment(
post_url="https://www.reddit.com/r/sideproject/comments/abc123/...",
markdown="My honest take is...",
verify_landed=true,
)4. Upload a book cover + manuscript to KDP
KDP uses AjaxInput — programmatic input.files clears on assignment. Use xhr_upload after capturing the real endpoint.
# 1. Capture a real upload
capture_network_start(session="slot-1", tab="kdp")
# user uploads one cover manually
capture_network_stop(full=true) # returns URL + form fields
# 2. Replay programmatically for subsequent books
xhr_upload(
url="<captured-url>",
files=[{"path": "/abs/path/cover.jpg", "field": "cover_image"}],
fields={"book_id": "GD5FHHAM7P9", "_csrf": "..."}
)5. Crack a canvas-rendered widget (vision fallback)
Some sites render their critical UI to canvas (Figma comments, drawing tools, custom video editors). DOM strategies see nothing. Vision_check is the unlock.
v = vision_check(question="click coords for the publish button", session="slot-1", tab="t1")
# returns {ok: true, answer: "...", click: {x: 1240, y: 64}}
if v["click"]:
click_at_coords(x=v["click"]["x"], y=v["click"]["y"])
else:
pause_for_human(reason="Publish button not visible — please confirm UI state")6. Heal a broken selector (drift)
When pre-flight reports a selector missing, ask the engine for replacement candidates instead of failing.
hints = drift_heal_suggest(
old_selector='[data-testid="oldid"]',
descriptor="post tweet button"
)
# hints = {ok: true, candidates: [{selector: "...", score: 7, reason: "data-testid present"}, ...]}
# Use the top candidate, log to playbook, push patched Thread7. Cross-post: same content to 5 platforms in parallel
run_parallel(
max_concurrency=3,
calls=[
{"tool": "navigate", "args": {"session": "slot-1", "tab": "x", "url": "https://x.com/compose/post"}},
{"tool": "navigate", "args": {"session": "slot-1", "tab": "li", "url": "https://www.linkedin.com/feed/?showShareBox=true"}},
{"tool": "navigate", "args": {"session": "slot-1", "tab": "bs", "url": "https://bsky.app/"}},
{"tool": "navigate", "args": {"session": "slot-1", "tab": "ih", "url": "https://www.indiehackers.com/"}},
{"tool": "navigate", "args": {"session": "slot-1", "tab": "th", "url": "https://www.threads.net/"}},
],
)
# Then fill each composer in turn (each platform has its own quirks)8. Defeat a Cloudflare challenge with stealth + captcha
# 1. Apply stealth patches BEFORE the page loads its challenge
enable_stealth(session="slot-1", tab="cf-target")
navigate(url="https://target.example.com/")
# 2. If a Turnstile or hCaptcha still shows up, solve it
# Pull the site_key from the page
key = eval_js(code="document.querySelector('[data-sitekey]')?.getAttribute('data-sitekey')")
result = solve_captcha(type="turnstile", site_key=key)
# result = {ok: true, token: "<the token>"}
# Submit the token via the page's expected flow (varies; usually a hidden input + submit)9. Schedule a tweet for later (no native scheduling)
X requires Premium for native scheduling. Workaround: capture a CreateTweet body, store it, run a cron that replays at the target time.
# At authoring time, capture once + store
captured = eval_js(code="JSON.stringify(window.__xhrCaptures.at(-1))")
# save 'captured' + target_time to your DB
# Later (via cron job, scheduled function, etc):
# Reload the body, substitute tweet_text, replay
replay_xhr(url=captured.url, method="POST", headers=captured.headers,
body={"variables": {"tweet_text": "Scheduled post text"}, ...})10. Detect drift and ship a patch
Pre-flight runs before every recipe. When a check fails:
# 1. Pre-flight ran, selector X is gone. Engine surfaces drift. # 2. Open the site, drift-heal: candidates = drift_heal_suggest(old_selector="...", descriptor="...") # 3. Confirm the top candidate works: click(selector=candidates["candidates"][0]["selector"]) # 4. Update the Thread JSON's proven_actions to the new selector # 5. weaver publish thread → buyers' copies auto-update
What's missing from this cookbook?
Open a Thread for any site whose pattern isn't covered here. The author share is 75%. The patterns above are the building blocks — the Threads are the specific recipes.