Concept
The versioned execution primitive.
A trajectory is Weles's reusable execution primitive: an authorized objective plus an ordered tool history, session policy, terminal contract, and evidence identity that can be reviewed, versioned, and replayed.
Primitive contract#
| Part | Meaning |
|---|---|
Identity | A stable action name and source path. Production admission binds it to one organization, origin, and allowlisted action. |
Objective | The exact external effect the run must reach; it is narrower than permission to automate an entire site. |
Steps | An ordered history of tool plus structured arguments. Generic replay stores { tool, args, result? }; checked-in trajectories can encode richer control flow in source. |
Session policy | Browser engine, persona, proxy/sticky identity, profile reuse, credential references, and whether keeper-first discovery is required. |
Terminal contract | Explicit completed, failed, or needs_human_approval state plus the observable result required before completion. |
Evidence identity | Trajectory path, source revision, browser release coordinate, action-log row, artifacts, and optional signed receipt. |
Lifecycle#
- Discovery starts from an authorized objective. The writer can produce a draft using navigate, click, fill, credential, email, CAPTCHA, state-reading, and terminal tools.
- Keeper-first discovery drives one persistent WSession with humanized atoms and records the successful action history.
- A successful keeper-first run saves that history as a reusable trajectory; a model draft alone is not production authorization.
- Review fixes the origin, action, credentials, browser/session policy, terminal condition, and failure behavior.
- Production resolves the reviewed version and replays it in a verified browser build. A scope or behavior change creates a new reviewed version.
Tool vocabulary#
The generic trajectory writer currently targets navigate, click, fill, fill_credential, store_credential, type_text, focus, press_key, scroll, wait, read, select_option, set_control, js_click, solve_captcha, check_email, generate_identity, save_account, done, and give_up. A checked-in trajectory may use WSession directly, but it still owes the same authorization, terminal-state, evidence, secret, and browser-release boundaries.
The writer answers through one required write_trajectory function call whose arguments are the draft: a list of short imperative steps and a list of risk or extraction notes. Its reply is never searched for JSON in text. A writer that cannot be reached, whose arguments are not that object, that gives a step or note which is not a nonblank string, or that gives no step at all fails the draft-first run with that cause in its result; no generic draft is put in its place. A run started with WELES_DISABLE_TRAJECTORY_WRITER=1 carries no draft, and the objective and constraints alone guide it.
What a trajectory decides on#
A checked-in trajectory decides its next step from what the page offers to act on, never from the page's prose: a control by role and accessible name, a form field by its name or autocomplete attribute, a frame by its origin, the URL's host and path, the HTTP answer the page's own request received, and the session's cookies. src/trajectories/_shared/page/offers.mjs holds those readers (control, field, autocompleteField, frameFrom, clickFirst); a site's wording, language or copy change does not change what the trajectory does, and a page that offers no control to continue ends the run with that sentence instead of guessing.
Claude's Google sign-in handoff treats only a visibly rendered challenge frame as a captcha. After the solver answers, it observes visibility rather than requiring the provider to remove the iframe: a retained hidden frame does not prevent the consent step. A visible challenge still blocks progression; hiding it is not proof of authentication, which requires the exact account's independent grant and successful provider refresh. The real Brama subscription-capacity journey claude_captcha_sign_in_finishes_with_a_live_grant requires an observed claude_captcha_answer stage and a persisted active grant for the selected member; a run without that provider challenge cannot qualify this path.
Humanized locator clicks read the target's geometry across browser-rendered frames after requesting a scroll. Moving geometry remains pending instead of failing after a number of timed reads. The click proceeds only when consecutive rectangles are identical and inside the viewport. A missing box or a stationary off-screen target is refused with the observed geometry; a failed scroll or viewport read keeps its actual cause instead of being ignored.
The per-page CDP input adapter advances mouse path points, button presses and key events when the browser acknowledges them, without inserted hover, hold or inter-key sleeps. Its click and keyboard methods take no delay argument. The native input transport remains a separate choice. Humanized fill observes document load and quiet rendered DOM frames before refocusing and clearing the field, rather than assuming a click-triggered update finishes after a fixed interval. Input and page-observation errors propagate to the trajectory. Sequential fields in the LinkedIn signup probe and NCBR sections 1.1 and 3.1 use this same preparation without an extra idle gap between fills.
Recorded pointer replay derives spatial segments, not key-gap, key-hold, mouse-step, reaction or inter-click delay samples. Usable pointer geometry does not depend on recorded keyboard events being present. When no usable segment exists, the pointer path uses the Bezier generator.
The shared scroll action observes the page after each acknowledged wheel input instead of sleeping between wheel deltas or bursts. Positive distances scroll down and negative distances scroll up. A wheel or readiness failure ends the action with its cause; a trajectory must not report that it scrolled after silently discarding that failure. The shared click path likewise does not insert a reaction pause before dispatching the click.
The native bridge adds no pointer-step or inter-key pauses. Pointer templates retain geometry without waypoint dt values or a step-duration sampler. A failed cliclick command reports its operation, OS error code, exit status, signal and error output; typed characters are redacted. Failed availability probes are not cached as success, and a failed frontmost-application read preserves its cause. Command completion is not proof of the application's resulting state. The native dependency still imposes pacing: cliclick 5.1 documents 20 milliseconds as both the default and minimum for -w, so the native transport must not be described as clock-free. Weles does not patch that external tool or silently switch the selected native transport to CDP.
humanHoverLocator observes target geometry and the rendered page, and optionally moves to an off-target viewport corner; it does not infer reading time or a displayed hovercard from elapsed milliseconds. Missing and zero-area targets fail before pointer input. An unavailable off-target corner, a detached target, or a failed viewport read is an error rather than a silent false result. The generic_capture hover step leaves the pointer on its target. To require a resulting panel, follow it with a wait_selector step naming that panel—for example, hover with value #menu followed by wait_selector with value #menu-panel. The optional Reddit author hover records an absent visible link as omitted, but propagates real lookup and input errors.
For generic_capture, record_seconds remains the minimum requested media duration, not a page sleep. The source recording path writes its first captured viewport JPEG to the encoder before starting scripted actions, then captures while they run. It continues until the steps and minimum frame count finish, and writes a final frame captured after the action callbacks completed. This brackets fast actions without treating callback completion as proof of a later provider effect; use the plan's explicit state-reading steps for that. Encoding uses 25 frames per second and repeats the previous observed frame to preserve capture spacing, including static pages; it does not invent animation between captures. Slow observations can miss intermediate visual changes, and the final frame can extend the file beyond the requested minimum. The separate WSession recorder is disabled for this action. Document-load errors are not discarded. An encoder or capture failure rejects the first-frame gate, closes the owned session and settles input and encoding instead of reporting a completed video.
Video capture uses the managed runtime's ffmpeg with JPEG image2pipe input and libvpx output. WELES_FFMPEG_BIN pins the encoder; an explicit missing path raises CAPTURE_ENCODER_UNAVAILABLE before session startup instead of selecting another executable. Encoder startup, input and exit failures report their operation, path and available process state. After encoding exits successfully, an isolated page in the same managed browser reads the written WebM file and decodes its first frame. No external ffprobe installation or instrumentation of the captured product page is required. CAPTURE_VIDEO_PROBE_FAILED reports file-loading or decoder failure, and CAPTURE_VIDEO_INCOMPLETE refuses missing dimensions or a shorter-than-requested duration. The WebM sidecar takes width, height and duration_seconds from the file as read by the browser, not the CSS viewport or elapsed wall time. frames_submitted counts encoder input, not independently decoded frames; loading the first frame is not a full-video decode audit. capture_result.json retains error_details and does not claim a video upload on refusal.
On the dedicated Weles host, node src/trajectories/generic/capture.mjs consumes GENERIC_CAPTURE_PLAN; for example, {"batch":"review","site_slug":"weles","source_url":"https://weles.wisent.com/docs","axis":"composition","viewport":{"width":640,"height":480,"device_scale_factor":1},"full_page":false,"steps":[],"record_seconds":1,"artifact_prefix":"stado://weles-captures/review/"}. Optional hover and scroll atoms resolve when their step runs, with CAPTURE_INPUT_UNAVAILABLE naming a missing export rather than substituting another input method.
Shared analytics entry, the LinkedIn authorization gate and Overleaf's Google sign-in transition observe document load and quiet rendered DOM frames before inspecting the resulting page, instead of applying short or long idle pauses. Readiness is not authentication: the existing account probe and final URL checks still decide whether sign-in succeeded, and a page-observation failure remains the run's error. The cross-login OAuth entry uses the same readiness observation. A provider-button lookup or click error propagates instead of becoming an absent button or a successful click; the redirect condition, authentication probe and cookie persistence still decide completion.
Signing a platform in through another platform's account (cross_login, Threads through Instagram, Product Hunt through X) takes the provider's saved cookies, the provider control the vision model finds, the provider's consent screen and the way back. The saved cookies are set as the browser reported them when the account was saved; a row missing a field the browser needs is refused with oauth_cookie_unusable naming the row and field, and the account must be signed in again. The consent screen is recognised and its grant control found by the vision model in whatever language the provider shows: oauth_consent_ungrantable names a consent screen with no control that grants, and oauth_consent_not_accepted names one still shown at the same address after its grant was clicked. The provider has handed the page back once the page is on the target's own host (or one of its subdomains) again; no list of provider hosts decides it, so a provider address naming the target in its query is not mistaken for the return. A reCAPTCHA gate after the return is submitted through the form that holds the solved token; recaptcha_gate_unsubmitted names a page without the token field, without the written token or without its form.
Claude and Codex sign in through Google with one shared step: Google Identity Services offers only accounts Google already holds, so the run opens Google's own sign-in page, enters the account there (with the flow's approval label for a phone approval) and only then loads the provider's authorize address. Google's sign-in host is read from one declaration, and a Google page of the handoff is recognised by its host, never by a substring of its address. A pending Claude control (the Google button or the grant screen's Authorize while they load) is waited out on that very control enabling, or on any page of the handoff moving on.
The OpenAI mailbox invitation and email-verification helpers observe document load and quiet rendered DOM frames after opening an acceptance or verification link and after dispatching the offered control, without deliberate or long pauses. The returned URL records where the flow was observed, not proof that a workspace membership or verified account was persisted; the calling authentication flow still owns that decision. Page-observation failures propagate instead of returning an apparently completed mailbox step.
The exported generic agent login observes a URL change after submitting the offered control. A failed URL read or browser navigation observation raises an error that names the operation and retains the browser cause; an empty URL is refused rather than treated as a successful sign-in. A changed URL alone is not proof of authentication, and a page that never changes URL can remain pending; use a provider-specific outcome observer for that case.
Gmail result detection observes rendered pages until it sees result rows, an explicit empty result, or a login route; idle pauses never count as a completed search. Gmail and Google Ads login share post-login dismissal in google_sso/sign_in/redirect_watch.mjs, without an attempt budget. GOOGLE_POST_LOGIN_CONTROL_UNAVAILABLE names a page with no dismissal control, and GOOGLE_POST_LOGIN_STATE_REPEATED refuses to click the same control at the same URL twice. A document replacement during a control read returns to page observation; other browser errors propagate. Neither condition claims that authentication succeeded.
Google Ads campaign helpers observe document load and quiet rendered DOM frames after click, fill and list-entry actions instead of adding short idle gaps. An absent control can still return false, but failed control reads, input, navigation, foregrounding and page-text reads propagate their actual browser errors. A failed click does not try another selector that could duplicate an already dispatched action. These conditions describe local browser interaction, not a saved or published campaign.
Drive search observes result rows or the explicit empty-result marker without a read budget. Navigation and Google sign-in transitions use page readiness rather than short or long idle gaps. DRIVE_SEARCH_SIGN_IN_FAILED reports an unsuccessful sign-in helper; DRIVE_SEARCH_SIGN_IN_REQUIRED reports the observed Google account route during result collection. Document export uses the same readiness observation and reports DRIVE_DOCUMENT_SIGN_IN_FAILED or DRIVE_DOCUMENT_EXPORT_HTTP_ERROR with the actual HTTP status and export URL. These runtime errors pass through session cleanup instead of exiting inside the open session.
Document rename observes title geometry and the visible focused title input instead of inserting hydration or post-click idle gaps. Selection and typing proceed without artificial pauses. DRIVE_DOCUMENT_TITLE_FOCUS_LOST refuses a changed input focus, and DRIVE_DOCUMENT_TITLE_VALUE_MISMATCH refuses to press Enter when local input readback differs from the requested title. After Enter, page readiness precedes the rendered-label check; DRIVE_DOCUMENT_TITLE_NOT_COMMITTED retains a mismatch and session cleanup runs. A matching rendered label is local UI evidence, not proof of server persistence.
Drive version-history evidence observes page readiness after navigation, sign-in, version-panel entry and each acknowledged wheel action, without deliberate or long idle gaps. Its existing identical-frame comparison decides when a scroll series ends. This comparison describes the captured viewport, not proof that every historical revision was loaded; retained screenshots and their hashes remain the evidence. A per-file readiness failure reaches the existing capture-error manifest and partial-result exit. Initial navigation failures and a detected signed-out page abort the run.
Google Play release entry proceeds from its existing page and upload-state conditions without extra short idle gaps. Requested release-name and notes fields must be offered; missing fields report PLAY_RELEASE_NAME_UNAVAILABLE or PLAY_RELEASE_NOTES_UNAVAILABLE. Missing track or advance controls report PLAY_RELEASE_TRACK_UNAVAILABLE or PLAY_RELEASE_ADVANCE_UNAVAILABLE instead of assuming the desired page or a saved draft. A failed status read preserves its browser error, and an absent release status reports PLAY_RELEASE_OUTCOME_UNOBSERVED rather than PASS for a rollout click alone. The Release summary heading is not a release status. Every post-session return, including staged mode and provider refusals, passes through session close.
Keyword Planner account selection, keyword input and Google account transitions observe page readiness without deliberate pauses. Failed locator, cookie, document and navigation reads propagate their browser cause instead of looking like an absent control, a signed-out session or a successful fallback. A selected account control or filled input is not evidence that keyword results were returned; the planner's result collection remains a separate operation.
The LinkedIn signup-form guard follows observed page transitions without an attempt budget or a reload loop. It skips a join link that leads back to the current URL. If the visible form still cannot be reached, signup_form_unavailable names state_repeated or no_signup_transition and includes the observed page and last action. A failed click or page-summary read keeps its actual error rather than becoming an empty, apparently healthy page. Navigation to a returned challenge URL also observes page readiness instead of inserting a deliberate pause.
The LinkedIn post-registration role and school helper observes page readiness between navigation, field entry, selection and the final profile read instead of inserting short, deliberate or long idle gaps. Its existing stage handlers retain readiness failures as role, school or view errors. The returned roleAdded and schoolAdded flags describe completed local control sequences, not independently verified saved profile data; viewable remains the existing page observation. No account creation or profile change is implied by source qualification.
The LinkedIn challenge form returns its submission result without an artificial post-submission pause. A submitted challenge is not an accepted challenge: the caller still has to observe whether the provider cleared it before reporting completion.
The LinkedIn API-solver caller observes document load and quiet rendered DOM frames after token submission before checking its existing visible challenge controls, rather than inserting a medium idle gap. A readiness failure propagates. The control check reports whether the challenge is still visible; it does not establish a persisted account or a provider-side rejection reason.
Brave proof-of-work returns its existing solved result after observing the solution field or verified button, without a deliberate post-result pause. State-read and calculation-click failures propagate instead of becoming an absent form or a false result. Only an execution-context replacement permits document-load observation before reading the form again; page closure and other observation errors retain their actual cause. This challenge result does not establish that registration was accepted or an account was persisted.
FunCaptcha token injection observes document load and quiet rendered frames after posting the challenge-complete message instead of inserting a deliberate pause. A failed message dispatch or page observation propagates instead of returning a successful injection. The returned injection result does not prove that the target accepted the token, cleared its challenge or persisted an account.
The reCAPTCHA image-grid solver observes a checked anchor or a visible instruction with a loaded grid, rather than waiting for a future frameattached event. It clicks the checkbox once only when no challenge is already visible; an active challenge with loading images is not clicked again. Classification uses the observed three- or four-column grid and the same frame receives tile clicks. RECAPTCHA_INSTRUCTION_EMPTY identifies a visible empty instruction; RECAPTCHA_GRID_UNSUPPORTED includes the observed column count; RECAPTCHA_IMAGE_LOAD_FAILED includes the failed image URL. Browser state-read and screenshot errors propagate instead of becoming a guessed three-column grid or an empty diagnostic image.
The image solver observes rendering after each selected tile instead of adding idle pauses, requires a visible, enabled Verify control, and clicks Verify once. An unchanged instruction after submission is not an outcome: the solver observes the checked anchor or a visible provider rejection without classifying or clicking the grid again. RECAPTCHA_TILE_CLICK_FAILED preserves the tile number and browser cause; RECAPTCHA_VERIFY_DISABLED names the disabled control; RECAPTCHA_REJECTED includes the visible provider message or state. RECAPTCHA_OUTCOME_UNOBSERVED reports a missing challenge frame before a checked anchor was observed. Observation errors, navigation and detached frames are not converted into a solved challenge.
Apple registration-frame entry, YouTube email entry and comment composition, Snapchat add-friend selection, TikTok profile-grid return and avatar-apply entry, and Twitter fallback-like and direct-message-send entry use their existing visible-control conditions without an extra idle gap first. TikTok return navigation and YouTube comment-section observation preserve their actual errors; the YouTube failure handler sets exit status 1 and allows session cleanup. These are entry and error-propagation conditions, not confirmation of registration, login, profile persistence or a delivered social action.
Reddit, PacketStream and SadCaptcha login entry observes the username field. Reddit message entry observes the recipient field, and Twitter post entry observes the compose control. These entry paths use their control condition directly instead of first inserting an idle pause.
Discord and Instagram recipient selection observes the requested visible row. Instagram Chat, LinkedIn Send and the form-scoped Reddit comment submit control must be visible and enabled, including their aria-disabled state. Instagram and LinkedIn composer entry and TikTok's Following-state check likewise observe their target directly without a preceding idle gap. Control readiness and local UI confirmation do not prove server persistence or message delivery.
Snapchat and TikTok message composition observe their existing visible search input and recipient-row locators without adding short or deliberate pauses first. Snapchat login likewise enters through its visible username field, and Discord reaction selection observes rendered message rows without an extra deliberate gap. A field-observation failure reaches the existing failure path; the Snapchat login and both message commands retain the actual outer error, set exit status 1 and run session cleanup instead of exiting inside that catch.
responseAfterAction exposes REQUEST_FAILED, PAGE_CLOSED and PAGE_CRASHED with the observed request method, request URL and page URL; request failures also retain the browser's errorText.
The NCBR correction runner's clickSafe re-resolves the visible, enabled control instead of retaining an element handle that can detach during rendering. It observes document load and quiet DOM frames after dispatch instead of a short pause. Row-editor readiness requires the matching field to be visible and adds no idle gap. Save correlates a new matching non-GET beneficiary request with its own response, so a failed request cannot remain hidden behind a response-only observer. A required parent-section save uses that same completion path instead of a long pause. Request acknowledgement does not replace the runner's reopening and field-value verification, or its submission-control refusal.
Both Gmail search trajectories read an opened message after its body locator becomes visible, without an extra short pause. A requested thread-body read or return-to-results failure keeps its thread diagnostic, propagates to the command's failure handler and stops later thread reads instead of continuing from an unknown page or reporting success. LinkedIn profile editing propagates a failure of its required first-name-field readiness check and adds no deliberate pause after it. Native observation errors keep their cause; these conditions do not prove authentication, a complete mailbox export or a persisted profile update.
LinkedIn intro Save correlates a new POST matching its existing profile-action predicate with that request's own response. Blur, pointer-input and request failures are not converted into a missing response or a successful no-op. It reports LI_PROFILE_SAVE_NOT_ENABLED for no visible enabled Save control, LI_PROFILE_SAVE_HTTP_ERROR for an unsuccessful HTTP response, and LI_PROFILE_SAVE_RESPONSE_FAILED when response completion fails; request details and the original cause remain available. Tab blur has no extra short pause. The command's failure handler prints the actual error and sets exit status 1 without bypassing session cleanup; successful no-ops also reach cleanup. Explicit WELES_KEEP_OPEN=1 review observes closure or a termination signal only while the page exists and removes its observers afterward. A completed HTTP response does not establish persisted profile values; profile readback remains a separate requirement.
Umami email/password login correlates its new POST matching the existing /api/auth/login predicate with that request's response. Its click callback performs input only; DOM observation follows the completed response and the URL leaving login/signin, rather than treating quiet frames as a failed redirect. The existing Enter submission is used only when no matching sign-in control is offered. Visibility-read errors propagate instead of becoming absent controls or an assumed existing login. UMAMI_LOGIN_REFUSED retains an unsuccessful response's status and body; UMAMI_LOGIN_RESPONSE_FAILED retains the failed body read, request URL, status and cause. Other analytics clickFirst callers retain their default post-click DOM observation.
urlMatching rejects an already closed page with PAGE_CLOSED before checking its last URL, so a matching stale URL is not success and a nonmatching closed page cannot remain pending for a closure that already happened. Closure and crash during observation expose PAGE_CLOSED or PAGE_CRASHED with pageUrl and release their listeners.
Discord send_friend_request observes page readiness after opening Friends and requires its visible Add Friend tab, username input and Send Friend Request control, without idle gaps after navigation or tab selection. It correlates its new POST to users/@me/relationships with the response to that request instead of observing any matching response. Target entry uses a real Tab blur and value readback without a short pause: DISCORD_FRIEND_TARGET_MISMATCH refuses a different entered handle, and DISCORD_FRIEND_REQUEST_NOT_ENABLED refuses a disabled send button. A stable Skarbiec account id is required before browser work or sending. The existing HTTP 204 success rule and metadata.friend_requests_sent outcome history remain; provider refusals and command errors retain exit status 1 while session cleanup runs, rather than bypassing cleanup or being overwritten by a final successful exit. Request failure, closure and crash retain the shared observer's operation diagnostics, and the outer DISCORD_FRIEND_REQUEST_FAILED diagnostic prints the complete error. On the dedicated Weles host, set DISCORD_TARGET_HANDLE to the intended recipient and run node src/trajectories/discord/actions/send_friend_request.mjs; ACCOUNT_USERNAME can select the source account.
Discord forum creation proceeds on page readiness, visible controls and its own new POST to the selected forum channel's threads endpoint, without idle gaps. Set FORUM_CHANNEL_PATH to guild_id/forum_channel_id, POST_TITLE and POST_BODY, then run node src/trajectories/discord/actions/post/create_forum_post.mjs on the dedicated host. A malformed channel path reports DISCORD_FORUM_CHANNEL_INVALID before credentials or browser work. Title entry is read back after Tab; requested tag names are matched literally without silently dropping click failures. Forum creation and friend requests reject an observed login wall or unreachable page through the shared reachability check before looking for their controls.
Forum creation reads the provider's channel response and its nested initial message, as documented at https://docs.discord.com/developers/resources/channel#start-thread-in-forum-or-media-channel. It requires the requested parent channel, public-thread type, exact title and message content, and matching thread/message channel identifiers. A changed URL alone is not success. DISCORD_FORUM_RESPONSE_READ_FAILED retains the body-read cause; DISCORD_FORUM_POST_REFUSED includes HTTP status and provider text; malformed JSON reports DISCORD_FORUM_RESPONSE_INVALID. A successful HTTP reply that does not confirm the requested post reports DISCORD_FORUM_POST_UNCONFIRMED with the observed body and requires reconciliation before another attempt. A confirmed reply produces a JSON acknowledgement with thread and message identifiers and their URL. This is provider acknowledgement, not an independent later read-back or proof of tag persistence. Failure exits nonzero after session cleanup.
Discord create_thread proceeds on page readiness and visible toolbar, name, composer and Create controls, without idle gaps. On the dedicated host, run node src/trajectories/discord/actions/post/create_thread.mjs with SERVER_CHANNEL_PATH, TARGET_MESSAGE_SUBSTRING and THREAD_NAME; THREAD_FIRST_MESSAGE is optional. A malformed channel path is refused before credentials or browser work. The parent message's rendered identifier must name that channel, and the entered thread name is read back after Tab. The history reader reports DISCORD_THREAD_HISTORY_PROGRESS_UNCONFIRMED with before/after message identifiers when rendering does not advance; it does not claim that this proves there are no older messages.
Thread creation observes its new POST to the selected message's threads endpoint. Discord documents that the returned thread shares the source message identifier at https://docs.discord.com/developers/resources/channel#start-thread-from-message. The response must match that identifier, parent channel, name and public or announcement thread type. If a first message was requested, its separate new POST is observed from before the Create action; the returned message must match the created thread and requested content. A failed create removes the pending message observer through the shared action-failure path. HTTP, body-read, malformed-JSON and unconfirmed-response failures keep their stage and provider details. A first-message failure retains the already acknowledged thread identifier, not permission to create it again. Neither a sidebar title nor a changed URL is success. Session cleanup runs on failure, and the success JSON identifies the acknowledged thread and optional message.
Discord reply_message proceeds on page readiness, visible Reply and composer controls, and the new message POST caused by Enter, without idle gaps. Set SERVER_CHANNEL_PATH, TARGET_MESSAGE_SUBSTRING and REPLY_TEXT, then run node src/trajectories/discord/actions/comment/reply_message.mjs on the dedicated host. A malformed channel path reports DISCORD_REPLY_CHANNEL_INVALID before credentials or browser work. Reply and thread creation share message-target selection and provider-response reading. Target selection verifies the rendered message's channel and identifier, and rechecks the target after a scroll before considering unchanged history. Unchanged history without the target reports the operation's HISTORY_PROGRESS_UNCONFIRMED, not a claim that older messages do not exist.
A reply acknowledgement must name a message in the requested channel with the exact requested content, reply type 19, and message_reference.message_id equal to the selected parent. These fields follow https://docs.discord.com/developers/resources/message#create-message. Existing matching text elsewhere in the DOM is not evidence of a new reply. DISCORD_REPLY_RESPONSE_READ_FAILED, DISCORD_REPLY_HTTP_REFUSED and DISCORD_REPLY_RESPONSE_INVALID preserve response failures; DISCORD_REPLY_UNCONFIRMED keeps the provider body for reconciliation before another attempt. Success JSON contains the acknowledged message, parent and URL, not an independent later read-back. Session cleanup runs on failure.
Discord view_profile navigates on page readiness and an observed visible profile containing nonempty display-name or username text, without idle gaps. Extraction is scoped to that same visible element, not the first possibly hidden profile elsewhere in the document. Set DISCORD_TARGET_USER_ID and run node src/trajectories/discord/actions/passive/view_profile.mjs on the dedicated host. A nonnumeric target is refused before credentials or browser work with DISCORD_PROFILE_TARGET_INVALID, so it cannot become a path outside the profile output directory. A profile that loses its identity text before extraction reports DISCORD_PROFILE_CONTENT_UNCONFIRMED with the observed fields and page URL rather than writing an empty profile. Other errors retain their cause and session cleanup runs. The file is written under runOutputPath('discord_view_profile'), named for the requested id; this is visible DOM data for the requested navigation, not an independent provider identity attestation.
Discord join_server clicks its observed visible Accept Invite control without an idle gap first. INVITE_URL must be an HTTPS discord.gg/code or discord.com/invite/code URL without embedded credentials or a non-default port; other inputs report DISCORD_INVITE_URL_INVALID before account lookup or browser work. The init script writes the account token only on the exact discord.com origin, and cookie selection requires discord.com or its subdomains. Cookie installation and script-registration failures are reported and run session cleanup. Ban diagnostics use a carried signal or make at most one read after navigation produced a page; failures in that read or in writing ban_signal.json remain visible and fail the action. The post-click rules handling and channel-URL outcome criterion decide membership and gate completion.
Discord engagement/dm proceeds without idle gaps. Set RECIPIENT_HANDLE (username, with optional leading @) and DM_MESSAGE, then run node src/trajectories/discord/actions/engagement/dm.mjs on the dedicated host. Both are required and nothing is sent on the account's behalf by default: a blank handle fails before account or browser access with DISCORD_DM_RECIPIENT_REQUIRED, and a blank message with DISCORD_DM_MESSAGE_REQUIRED. The other platforms' DM trajectories (Instagram, X, LinkedIn, Reddit, Snapchat, TikTok) require the same RECIPIENT_HANDLE and DM_MESSAGE, and Reddit also DM_SUBJECT, refusing a missing one by name. After navigation the action observes the visible User Settings control or login route before probing authentication. It chooses one quick-switcher entry point, selects a visible result containing the exact handle, and requires a private-channel URL and visible editable composer. It replaces the search and composer contents rather than appending to a retained draft. DISCORD_DM_COMPOSER_NOT_EDITABLE reports the observed page and requested recipient. The action observes only its own new message POST caused by Enter; success requires a numeric message id, the selected channel, exact requested content and ordinary-message type 0. An old matching DOM message cannot qualify a send. DISCORD_DM_HTTP_REFUSED preserves HTTP status and provider text; response-read and JSON failures keep their cause; DISCORD_DM_UNCONFIRMED retains an unexpected provider body for reconciliation before another attempt. The JSON receipt names the requested handle and acknowledged channel/message, not independent provider proof of recipient identity or later read-back. Cookie-state write failures are reported alongside the original failure, and errors run session cleanup.
Discord email_verify proceeds without idle gaps. Run node src/trajectories/discord/actions/email_verify.mjs on the dedicated host, optionally setting ACCOUNT_USERNAME. An unset WELES_SKRZYNKA_ORGANIZATION reports DISCORD_EMAIL_INBOX_UNCONFIGURED naming the variable; a named organization is not a successful mail read. The mail is read from Skrzynka by the exact recipient and a discord.com sender, newest first. Only a tracking URL on the exact HTTPS click.discord.com origin is fetched, and only a redirect to HTTPS discord.com/verify containing a token can be opened as verification. Lookalike hosts and embedded credentials are not accepted. Redirect failures retain the actual URL, status, location, body or read cause. The token init script is restricted to the exact discord.com host. A missing visible resend control reports DISCORD_EMAIL_STATUS_UNCONFIRMED with the observed settings text rather than declaring the email already verified.
The resend action observes its new POST to /api/vN/auth/verify/resend and requires an empty HTTP 204 acknowledgement. It records email_verify_requested_at before clicking, then persists it only after acknowledgement; a message received during the request is not excluded by a timestamp taken after the reply. DISCORD_EMAIL_VERIFICATION_PENDING means the resend was acknowledged but verification has not been observed, not that delivery is proven or that the inbox is still empty. Another run reads the inbox once. Verification observes its new POST to /api/vN/auth/verify, preserves unsuccessful HTTP replies through DISCORD_EMAIL_VERIFY_HTTP_REFUSED, and requires visible Email Verified text on the trusted verification page before saving email_verified_at and emitting a JSON receipt. The receipt combines provider acknowledgement, visible confirmation and the metadata write, not an independent later account read-back. Errors preserve their cause and session cleanup runs.
Discord engagement/accept_friend_request proceeds without idle gaps. Run node src/trajectories/discord/actions/engagement/accept_friend_request.mjs on the dedicated host with optional ACCOUNT_USERNAME and a required positive integer ACCEPT_LIMIT, the number of pending requests the account accepts; no number is assumed. An unset value, malformed values, zero and unsafe integers fail before account access with DISCORD_ACCEPT_LIMIT_INVALID; a numeric prefix such as 3garbage is not accepted as 3. The action observes the selected Pending tab and an acceptance control, explicit empty-state text or outgoing-request control before interpreting the list. No acceptance control without the explicit empty state reports DISCORD_ACCEPT_PENDING_UNCONFIRMED with the actual page text, rather than claiming there are no pending requests. Each click is scoped to a stable observed row id or data-list-item-id; a missing row identity reports DISCORD_ACCEPT_TARGET_UNCONFIRMED instead of substituting an unknown target.
Acceptance observes its new PUT to /api/vN/users/@me/relationships/{target_id} and requires an empty HTTP 204 reply before counting it. The receipt records the target id from that request and the observed row label, or the actual id when no label was available. Each acknowledged entry is persisted in friend_requests_accepted before observing removal of that row's acceptance control. The metadata update reads the current account and preserves its other fields; it does not replace history from the run's initial account snapshot. A later failure retains the acknowledged targets in diagnostics, reports screenshot failures separately and runs session cleanup. The JSON result distinguishes accepted targets from an observed empty state; it does not claim that unrendered requests were enumerated or that another account independently read the relationship.
Discord new-location verification correlates the authorization tab's own new authorize-ip POST, reads its response before closing the tab, and rejects an unsuccessful reply with DISCORD_IP_AUTHORIZATION_REFUSED. A failed body read raises DISCORD_IP_AUTHORIZATION_RESPONSE_FAILED with request URL, status and cause. The owned authorization tab closes on success and failure; a refused authorization is not announced as accepted and does not trigger the subsequent login attempt. Missing account or password prerequisites stop before browser work with DISCORD_ACCOUNT_UNAVAILABLE or DISCORD_PASSWORD_UNAVAILABLE instead of setting an exit code and continuing. Login failures retain available operation fields in ban_signal.json details and print the actual error; inability to write that diagnostic is also reported. Authorization acknowledgement remains separate from the existing login-token and session checks.
Umami website creation clicks Add website once and observes its visible dialog instead of repeating the click after quiet frames or a swallowed input error. Visibility-read failures retain their native cause. Save requires an enabled control and correlates its own new POST using the existing Umami website-endpoint predicate. UMAMI_WEBSITE_SAVE_NOT_ENABLED reports a disabled control, UMAMI_WEBSITE_SAVE_HTTP_ERROR retains the unsuccessful status and provider body, and UMAMI_WEBSITE_RESPONSE_FAILED retains a failed body read and operation details. A request failure does not become an unobserved response that proceeds to the website list. The post-save domain readback remains; response acknowledgement and a domain text match do not independently prove a newly created unique website record.
LinkedIn registration correlates each submit's first new POST matching its existing signup or createAccount address predicate with that request's own response, instead of pairing independent request and response observers. Disabled controls raise LINKEDIN_SIGNUP_SUBMIT_DISABLED; unsuccessful HTTP replies raise LINKEDIN_SIGNUP_HTTP_ERROR; failed response-body reads raise LINKEDIN_SIGNUP_RESPONSE_FAILED; invalid createAccount JSON raises LINKEDIN_CREATE_ACCOUNT_RESPONSE_INVALID. Available request details, status, cause and the pre-submit snapshot are retained in submit1_diagnostics or submit2_diagnostics under operation_error. A diagnostic-file write failure is logged without replacing the original operation error. Unreadable request or response bodies are recorded as unknown with post_data_read_error or body_read_error, not as empty request data or invented response text. Successful diagnostics retain the post-submit snapshot. An HTTP 200 challengeUrl still ends the run through the existing challenge refusal; no challenge is solved. These observations do not themselves prove a registered or persisted account, and require qualification through the real registration flow.
popupOrNavigation owns the input callback: await popupOrNavigation(page, pattern, () => humanClickLocator(page, control)). It observes only that page's popup and main-frame navigation, returns the popup or null for a matching original-page URL, and removes its popup, navigation, close and crash listeners when the operation ends. It does not race an uncancelled popup waiter against a separate URL waiter. PAGE_CLOSED and PAGE_CRASHED retain pageUrl; input and URL-predicate failures propagate after the input action settles. urlMatching also rejects a predicate failure during navigation instead of throwing outside its returned promise. Both action observers invoke their required callback explicitly, so a missing callback is an error rather than an ignored Promise handler.
All seven popupOrNavigation callers in Decodo signup, Tencent login, 5sim balance/topup and JuicySMS balance/dashboard/topup pass the action through this ownership boundary. The five 5sim/JuicySMS entries observe their visible, enabled Google control and use humanized locator input instead of a long idle gap followed by a raw click. Decodo reports DECODO_GOOGLE_CONTROL_UNAVAILABLE with pageUrl when its offered sign-in control is absent. Handoff and unsuccessful SSO errors reach the command's failure handler without bypassing session cleanup; handlers retain the actual error and exit status 1, JuicySMS dashboard failures included. These are control and handoff observations, not proof of authentication, a balance, a payment or CAPTCHA clearance. Later payment completion and post-login pauses are separate conditions.
The account, proxy, service-credential and subscription helpers resolve Skarbiec's verified executable on their first vault operation, not while importing a trajectory. A successful lookup is reused; a failed lookup is not cached. Stado's release and executable checks still apply, and a real vault operation reports their failure rather than choosing another binary. This lets preconditions report their own refusal before an unused credential dependency: env TOPUP_CONFIRM=0 TOPUP_USD=10 node src/trajectories/fivesim/topup.mjs and the corresponding juicysms/topup.mjs command report TOPUP_CONFIRM=1 required before charging with exit status 2. Setting TOPUP_USD=0 reports invalid TOPUP_USD=0 with exit status 1. Those refusals do not start the browser or authorize a charge.
submitAnswered observes the form URL and the supplied message locator in one sequence driven by rendered browser frames. It leaves no URL observer or locator waiter running after the other condition wins. The GitHub and Supabase callers keep their form-route patterns, message locators and navigated/message result contract. An already closed page raises PAGE_CLOSED with pageUrl. A replaced document is read again; other browser and locator errors propagate rather than being hidden by Promise.any. The resulting page still passes through pageSettled. A displayed message or a changed URL is an observed UI outcome, not independent proof of a persisted repository change or an authenticated session.
Shared service Google sign-in uses submitAnswered with an exact previous-URL predicate for identifier, chooser, consent and password outcomes instead of separate URL/message waiter races. The helper accepts the same RegExp, predicate or text-pattern contract as urlMatching. Page readiness in the redirect watcher has no competing close waiter: only closure of its owned OAuth popup can replace readiness; a main-page closure or another observation error remains a failure. Shared sign-in entry propagates account, alternative-account, consent and identifier visibility-read errors, and never retries a failed humanized chooser or consent click with a forced raw click. None of this replaces the caller's authentication checks, and an OAuth popup closing does not mean sign-in succeeded.
GitHub profile editing proceeds on page readiness, owned upload responses and a fresh profile-page read, without page, avatar-menu or post-save idle gaps. Avatar policy, upload and commit each correlate their new POST with its own response; HTTP and response-completion failures report GH_PROFILE_HTTP_ERROR or GH_PROFILE_RESPONSE_FAILED with the operation, request and status. Avatar work finishes before text entry so document replacement cannot discard newly typed fields. A disabled avatar commit is refused. Text entry requires visible editable controls, uses humanized filling and Tab, and rejects a mismatched input. Save observes the native form's declared same-origin POST; unsupported forms raise GH_PROFILE_FORM_UNSUPPORTED rather than guessing an endpoint. A fresh authenticated profile response must contain every requested nonempty name, bio and location value before Skarbiec is updated with the observed name. GH_PROFILE_READBACK_MISMATCH reports a differing field and lengths. Errors retain exit status 1 and session cleanup; no-ops also close the session. An avatar commit acknowledgement is not independent image-persistence proof.
The GitHub registration rotation wrapper starts its next attempt after register.mjs exits with code 42 (Arkose blocked the captcha for that proxy exit), without an additional pause. MAX_ATTEMPTS is required and names how many proxy exits to try; the wrapper refuses to start without it ("set MAX_ATTEMPTS to how many proxy exits to try"). Success and other failure statuses are propagated. Run src/trajectories/github/register_rotate.sh only on the dedicated Weles host with an authorized registration objective. LinkedIn login probes fresh proxy exits, cycling oxylabs, iproyal, pingproxies and brightdata, for at most LINKEDIN_PROXY_EXIT_BUDGET exits across all rotations; that setting is required and the login refuses without it. Discord phone verification searches the countries DISCORD_PHONE_COUNTRIES lists and spends at most DISCORD_PHONE_MAX_TRIES numbers; both are required and a missing one is returned as the reason. TikTok login spends at most TIKTOK_CAPTCHA_SOLVE_BUDGET paid SadCaptcha solves on a rotate captcha, refreshing the puzzle between them; the setting is required and a captcha without it fails as captcha_challenge naming the setting.
Operator review in the Apple Ads API setup probe, Apple Ads report harvest and LinkedIn profile editor observes closure of its page or context, or SIGINT/SIGTERM, through reviewUntilClosed. An already closed page needs no review; an observed crash raises PAGE_CRASHED with operation=operator_review and the page URL. The helper removes all page, context and signal listeners when review ends. Both Apple commands close their session afterward and propagate cleanup failures instead of staying on an unresolved promise or swallowing a close error, under their close-after-run settings. Apple setup clickText reads optional control visibility once, propagates read failures and observes page readiness after a dispatched click without an idle gap. LinkedIn's login-form probe also uses page readiness before its visible-email-input read. Neither readiness nor control visibility proves authentication or a persisted change.
Oxylabs balance's KEEP_OPEN review uses owned closure and stop-signal observations instead of an unresolved promise. Oxylabs keeps a failed operation's exit status 1 and actual error, allows configured review on success or failure, and closes its session afterward; an error does not bypass cleanup through process.exit. Already closed pages do not announce a new review.
App Store Connect submission adds no short idle gaps after build-row selection, page readiness, version entry or release-note entry. Its existing clickAny readiness check remains, and an offered but disabled control raises ASC_CONTROL_DISABLED with the selector, label and page URL instead of reporting an ineffective click. Version and release-note entry require an editable field, dispatch Tab and read back the entered value. ASC_FIELD_NOT_EDITABLE names the field and page; ASC_INPUT_MISMATCH also gives expected and observed lengths. Visibility-read errors for these inputs propagate. The outer error handler prints the actual error, sets exit status 1 and reaches session cleanup. These observations concern local input and control readiness, not build processing, attachment, persisted release notes or submission to App Review.
JuicySMS dashboard inspection adds no post-login, page-capture or order-history idle gaps. Its shared provider page reader correlates each selected navigation's document request, follows that request's HTTP redirect chain, checks the final response and observes page readiness. JUICYSMS_PAGE_HTTP_ERROR, JUICYSMS_PAGE_RESPONSE_FAILED and JUICYSMS_PAGE_DESTINATION_MISMATCH retain the requested address, observed response or route, status and available cause. A failed page is never silently skipped. On a dedicated Weles host, node src/trajectories/juicysms/dashboard.mjs writes the selected HTML captures, all_orders.json and orders_pages.json under the run-output root's juicysms_dashboard directory; WELES_RUN_OUTPUT_DIR selects that root and the normal fallback is ~/.weles/runs. The history reader uses the deployed MyOrders component's orders.data, order id and orders.links contract, with no page ceiling. It follows an offered same-origin /myorders URL for the next consecutive page. Missing or malformed state, unsuccessful HTTP replies, fetch/body failures, redirects, invalid links, missing or repeated identifiers, a pagination gap and an empty page that still offers another page produce specific JUICYSMS_ORDERS_* or JUICYSMS_ORDER_* errors with the requested page and URL. Entity decoding is single-pass. The page report records the actual response URLs, statuses and row counts; export files are written only after traversal succeeds. This is traversal of the provider's offered history, not an atomic account snapshot.
JuicySMS balance inspection uses the same checked navigation and reads a fresh /myaccount response instead of an idle gap and a whole-page dollar regex. node src/trajectories/juicysms/balance.mjs requires the MyAccount component and an auth.user.email matching the selected Google sign-in email. An alias or different provider email raises JUICYSMS_ACCOUNT_MISMATCH rather than choosing an account implicitly. The reader uses auth.user.balance in EUR and the provider's numeric positive currency.rates.USD quote, matching the shipped client's EUR-to-display-currency conversion. It rounds the USD result to two decimal places for the existing balance_usd record. Missing or invalid balances, missing quotes and non-finite conversion results are refused; no fabricated rate or other currency's displayed number becomes USD. The shared page reader accepts the provider's JSON response or HTML Inertia data-page state and diagnoses missing state, invalid JSON, unsupported content types and wrong components with response details. JUICYSMS_BALANCE_PERSIST_FAILED reports an unsuccessful service-record update. Errors reach session cleanup instead of process.exit; there is no /dashboard navigation fallback and no silent artifact write.
The AntiCaptcha, CapMonster and Bright Data balance entrypoints observe document readiness instead of idle gaps before Google sign-in and the balance read. Their offered Google controls use humanized input and report a disabled control explicitly. On the dedicated host, run node src/trajectories/anticaptcha/balance.mjs, node src/trajectories/capmonster/balance.mjs or node src/trajectories/brightdata/balance.mjs. An incomplete sign-in, missing labelled USD balance or unsuccessful stored-record update is a named failure with exit status 1; the owned session closes on success and failure. Optional HTML or screenshot capture errors retain their own cause instead of disappearing. Bright Data checks its billing document's HTTP status and response completion; a failed navigation cannot leave an unrelated page to be parsed as the billing result. Page readiness alone is not proof of account identity, balance correctness or persistence.
The CapSolver and Oxylabs balance readers own the Google click and its popup-or-navigation observation together. A click failure removes the handoff observers; popup readiness failures propagate. Neither reader adds an idle gap before handoff or the balance read, and 5sim likewise reads page readiness instead of pausing after sign-in. The 5sim reader refuses an unexpected post-sign-in destination rather than silently navigating to another page. CapSolver and 5sim report missing labelled balances and unsuccessful stored-record updates through their failure handler and session cleanup. All three report optional diagnostic-capture errors with their causes. Their dedicated-host entrypoints are node src/trajectories/capsolver/balance.mjs, node src/trajectories/fivesim/balance.mjs and node src/trajectories/oxylabs/balance.mjs. Existing provider balance interpretation, proxy probes and Oxylabs operator review remain separate from page readiness and are not established by a successful click.
IPRoyal, NopeCHA and Pingproxies balance reads also use document readiness instead of idle pauses. NopeCHA observes its offered Google control after opening the sign-in dialog and reports NOPECHA_NO_ACTIVE_KEYS only as the provider's displayed state, not proof of authentication or a reason to buy anything. The IPRoyal dashboard and NopeCHA manage-page reads require a successful, completed document response; transport, HTTP and body-completion failures keep the requested operation and observed cause. Named sign-in, balance-read and persistence failures exit with status 1 after session cleanup, and optional diagnostic capture failures remain visible. The dedicated-host commands are node src/trajectories/iproyal/balance.mjs, node src/trajectories/nopecha/balance.mjs and node src/trajectories/pingproxies/balance.mjs. A loaded document does not independently verify their account, balance unit or persisted record.
PacketStream and SadCaptcha balance login use the native form's declared same-origin POST, not delayed URL samples. The shared submitLoginForm helper owns the Enter action and its new main-document request, follows that request's redirects and checks response completion before inspecting the returned page. NATIVE_LOGIN_FORM_UNSUPPORTED refuses a missing or unsupported form; NATIVE_LOGIN_INPUT_INVALID exposes the invalid field names and browser validation messages without copying credentials. Request, response-body, redirect and HTTP failures retain their observed details. NATIVE_LOGIN_NOT_COMPLETED means the completed submit returned to the login route or left the provider origin; its page text is evidence of what the provider displayed, not an inferred wrong-password verdict. The readers then load their dashboard and reject HTTP or document-completion failures instead of parsing a stale page. Balance-read and persistence failures reach session cleanup with a nonzero exit status. Run node src/trajectories/packetstream/balance.mjs or node src/trajectories/sadcaptcha/balance.mjs on the dedicated host. Submission and navigation are owned by this helper; the balance parser and stored-account policy are the readers' own.
JuicySMS topup does not advance checkout through idle gaps or treat a success-looking URL as a charge. On the dedicated Weles host, node src/trajectories/juicysms/topup.mjs requires TOPUP_CONFIRM=1, a positive TOPUP_USD representable in whole cents, and TOPUP_CARD_NUMBER, TOPUP_CARD_EXP and TOPUP_CARD_CVC. It reads fresh AddFunds state, requires the selected Google account, USD display currency and terms already accepted by the operator, and does not switch currency or accept terms itself. The submitted amount must remain valid and match the request. The new POST /addfunds must carry pMethod=CC and that amount; its CreditCardPayment component must supply a production Stripe session. The browser must reach that exact session on checkout.stripe.com. Loader failures, provider handoff errors, page closure and crashes remain errors rather than a successful handoff.
Card fields must be editable and retain the supplied input, and the enabled Stripe submit control must show the exact requested USD total before dispatch. The run writes checkout_attempt.json under the run-output root's juicysms_topup/<run-id> directory; paymentDispatchStarted records the start of dispatch, not proof of a charge. After dispatch, the run observes rendered checkout state and its own returning main-document response without a clock budget. /payments/failed must contain PaymentFailed and reports JUICYSMS_PAYMENT_REFUSED, preserving the provider's failed-or-cancelled outcome without inventing a bank ledger result. /payments/success must contain PaymentSuccess with paid=true, a nonempty orderKey and the requested USD amount and currency. payment_receipt.json retains the available observed receipt fields, including an unconfirmed or mismatched success-page result. JUICYSMS_PAYMENT_UNCONFIRMED and JUICYSMS_PAYMENT_RECEIPT_MISMATCH require checking the provider before another attempt. PASS-CHARGED follows only a matching paid receipt and successful cleanup; it explicitly does not assert that the balance was credited. A later screenshot, artifact or cleanup error does not erase an already observed paid receipt or authorize another payment.
An observed embedded Stripe or 3DS verification frame opens a durable juicysms-payment-verification operator request rather than clicking a test authorization or declaring approval after a pause. The account owner completes the existing bank verification, without submitting another payment. The run still needs the provider receipt and closes its own request with the observed outcome. A live request owned by another run is refused, not adopted or closed. weles runs list/show and Weles Desktop Running show the request the run waits on with its paging diagnostics.
Pingproxies top-up on the Stado-selected Weles host accepts an explicit TOPUP_USD and TOPUP_CONFIRM=1 through node src/trajectories/pingproxies/topup.mjs, or the generic top-up CLI node src/trajectories/_shared/services/topup/orchestrator.mjs pingproxies <usd> with the same confirmation. A dedicated purchase card is supplied as TOPUP_CARD_JSON from the credential vault or complete TOPUP_CARD_NUMBER, TOPUP_CARD_EXP and TOPUP_CARD_CVC fields from a card file; malformed, incomplete or conflicting JSON refuses before a browser starts rather than selecting another file. Weles Desktop Runs submits pingproxies_topup with topup_usd and topup_confirm, then displays the worker's terminal output and recording; the card comes from the worker's secret environment, not the JSON parameters. The Add balance dialog must expose a writable amount matching the requested whole-cent USD value and exactly one checkable saved card ending in the purchase card's last four digits. Missing, ambiguous or unselected cards and changed amounts refuse without pressing Add store credit. The run correlates the resulting Stripe payment-intent confirmation with its own request and requires one successful response with the requested USD amount and a nonempty intent id. An absent request, setup intent only, multiple confirmations, transport failure, HTTP error or other status is an unconfirmed outcome; reconcile before another payment. PASS-CHARGED proves the observed Stripe intent, not wallet credit. Read the provider's balance separately after a charge. The isolated payments journey requires a dedicated account and card and is not qualified until executed on that host.
The real Pingproxies payment journey is tests/payments/pingproxies-topup.mjs on the dedicated host. It requires WELES_TEST_DEDICATED_HOST, PROXY_URL, WELES_TEST_PAYMENT_ACCOUNT_EMAIL, WELES_TEST_PAYMENT_USD, TOPUP_CONFIRM and WELES_FRESH_PROFILE, plus WELES_TEST_PAYMENT_CREDENTIALS and WELES_TEST_PAYMENT_CARD files confined to build/real-tests. The credentials file has the scoped Google SSO identity; the card fixture is the one saved in the isolated provider account. The test runs refusal commands for absent confirmation, malformed card JSON and a sub-cent amount before measuring the provider's visible wallet balance, making one real charge, and measuring the balance again. A payment-attempt marker beside the card fixture prevents an automatic second charge after an unconfirmed result; reconcile that account and use a new isolated fixture for another run. The retained report records the source revision, commands, exit statuses, screenshots and observed balances without the complete card data. No fixture or test execution is evidence of payment until that real journey has run.
FENG SMART field entry passes WSession.page to the keyboard and pointer helpers and advances through its configured visible controls without idle gaps between text, select-option and radio actions. Manual login and end-of-run review use durable operator requests rather than a read budget or a timed review window. Login observes the URL leaving its login route, while review observes page closure and reports a page crash. Neither event proves server persistence or submission. Field errors retain a non-zero exit status, and session closure runs on both success and failure. The shared URL observer also rejects a crashed page and removes its event listeners.
FENG selector discovery uses the same durable operator-request lifecycle as SMART field entry. Run FENG_VALUES=/path/to/values.json node src/trajectories/feng/dump_selectors.mjs; the default input is build/values.json in the Weles checkout. Missing input reports FENG_VALUES_MISSING before starting a browser, while invalid JSON reports FENG_JSON_READ_FAILED with the file path and parser cause. The login request observes the browser leaving its login route instead of counting page reads. That event proves neither the account nor the selected application. Discovery observes page readiness after optional FENG_WNIOSEK_URL navigation and refuses a remaining login page with FENG_SELECTOR_LOGIN_REQUIRED. It writes discovered_selectors.json, proposed_field_map.json and missing_mappings.txt under build/feng, not beside the source. Existing explicit field selectors are retained. The session is closed after export or failure; simultaneous work and closure failures retain both causes.
The Google sign-in helpers used by Codex and Claude require a visible, enabled, non-readonly input before typing. Direct Google prelogin advances from navigation commit to the identifier-field condition without an idle gap. Native input attributes do not prove that Google's handlers have hydrated: the helpers read the value back after input dispatch and refuse a mismatch with observed and expected lengths, without exposing the field contents or blindly retyping. Required matching buttons are observed on browser frames rather than through timed polling or attempt budgets. A button click has no added settling delay and is not proof of authentication; the caller's navigation and session checks still decide completion. Condition results release their browser handles after the value is read.
Optional invitation-acceptance and email-verification submit controls are read once after page readiness. The result distinguishes a click from not_offered and document_replaced; the latter states neither require a control to appear nor prove automatic submission. Browser and pointer-input failures propagate instead of becoming an absent control. The workspace_invite_control_clicked stage records a click, not invitation acceptance.
The shared Google chooser reader matches the requested email against data-identifier after trimming whitespace and ignoring letter case. A sole different account is not selected. Use another account is read from its own offered control, not from a different identity's row, and blank identifiers do not count as accounts. Codex and Claude diagnose an unmatched chooser without that control rather than selecting an identity by position. Identifier and password fields count as offers only when they have rendered geometry. Device approval also uses the exact identified row and reports gis_control_unavailable when its click cannot be dispatched.
Codex reads the offered Google handoff control instead of requiring an optional account row or Continue button to appear. An identifier field enters credentials directly, an exact account row selects that account, and an offered other-account control opens fresh entry. An affirmative control is separate from account selection. Unchanged URL/control pairs are not clicked repeatedly. google_account_not_offered reports a chooser with no matching account or other-account control; google_identifier_repeated reports repeated fresh entry; gis_control_unavailable includes the actual click failure. The flow follows its own popup descendants and re-reads them as they change, without treating unrelated context pages as login popups. Credential and click failures propagate; a popup that closes while its document loads is not read.
Claude tracks an acted-on handoff state separately for each page and uses the complete observed URL, not a clipped diagnostic URL. URL and popup observation removes its listeners after the event, and a browser-page crash raises an error. An unavailable tagged control raises gis_control_unavailable with its actual reason instead of becoming an unchanged state that the flow waits to leave.
Codex and Claude observe document load and quiet rendered DOM frames after navigating to the authorization URL instead of inserting deliberate idle gaps. The same observation follows their existing explicit authorization re-navigation paths, including Codex's return after invitation handling. A navigation or readiness failure propagates. Readiness does not establish a Google session, consent, a callback code or a usable provider token; the remaining handoff controls and terminal checks still decide those outcomes.
Claude resumes its page-state observation immediately after dispatching a Google handoff, account selection, other-account selection, confirmation or OAuth consent click. It adds no idle pause; unchanged acted-on states still use the existing navigation, popup and closure observation rather than repeated clicks. Codex observes page readiness after its optional email-first submit. A failed visibility read, input verification, submit or readiness observation is returned to the caller, not logged and discarded before another handoff attempt. Neither handoff accepts an idle-pause control from the shared subscription authorization entrypoint. Provider callbacks and the existing terminal checks, not elapsed time or input acknowledgement, still determine completion.
After Codex or Claude submits a Google verification code, a challenge that remains displayed is pending, not a rejection inferred from quiet frames or elapsed time. The observer follows browser frames until the challenge is absent or a rendered code input is marked aria-invalid=true. That input state raises provider_challenge_refused with the observed host, path, field attributes and page context; browser-observation failures keep their own cause. The reported invalid state is not independent proof of a new server response. There is no added long pause before this condition, and leaving the challenge is not proof of an authenticated account session.
After password submission, Codex and Claude observe the password-step outcome without an idle pause. A password field or password-challenge path that remains displayed is pending, not a wrong-password inference. A rendered password input marked aria-invalid=true reports GOOGLE_PASSWORD_REJECTED; Google's existing browser-security or failed-attempt refusal text reports BROWSER_NOT_SECURE or provider_challenge_refused with the observed page state. These diagnostics describe the current UI, not proof of a new server response. Leaving the password step only permits the next authentication check and does not prove a signed-in session. Credential entry accepts and forwards no idle-pause control.
Before choosing an authenticator method, the Google credential flow inspects whether a code field is already visible. A field that can appear only after method selection does not block that selection. Only after the method-selection click does the flow require the code field to appear. Observation failures are not converted into a false no-code result, and fresh-entry errors leave the Codex or Claude handoff instead of being logged and suppressed so the handoff can try again.
Subscription device approval reads the resulting page after document load and quiet rendered frames instead of adding long idle gaps after device-code submission and Google handoff. The Google handoff is dispatched only once from the same page and URL; an unchanged offered button does not trigger repeated clicks while the provider is pending. An offered disabled Google control raises gis_control_unavailable at device_google_handoff. Browser and input failures propagate through the existing session cleanup. These observations do not establish provider approval or a usable subscription token.
GitHub registration performs its initial pointer and scroll actions without idle gaps. Email, password and username entry observes a visible, enabled, writable input, verifies its value after typing and then dispatches Tab. A mismatch identifies the field and the observed and expected lengths without printing its contents. The Create account path has no added post-scroll pause. Errors reaching the outer registration handler set the exit status and still run session closure. These checks do not prove captcha acceptance, email-code receipt or a verified account.
The GitHub coordinate solver delivers its pointer gesture through acknowledged moves, button-down and button-up operations without inter-waypoint idle gaps, over its six drag waypoints. Once button-down starts, button release is attempted even if the gesture fails; if both fail, an AggregateError retains both causes. A failed gesture cannot produce the later clicked/swipe success log. Registration does not convert a thrown solver failure into an unsupported-puzzle result, and its outer handler prints the complete error and exits with status 1 while closing the session. It does not infer a proxy-rotation status from words in an exception. Explicit solver return values and the captcha outcome checks remain distinct from input failures; successful input delivery is not proof that the captcha was accepted.
On the Stado-selected Weles host, the GitHub star, follow and watch entrypoints observe document readiness instead of inserting idle gaps. Run node src/trajectories/github/star/run.mjs or node src/trajectories/github/actions/watch_repo.mjs with REPO_URL, or node src/trajectories/github/actions/follow.mjs with TARGET_USER. Their search and exploration choices remain available when an explicit target is absent. A cookie-installation error never becomes an apparently authenticated run. Star requires the visible Unstar form. Follow dispatches the offered submit control, checks its new target-specific POST /users/follow response and then requires the visible Unfollow form; GITHUB_FOLLOW_RESPONSE_FAILED retains the body-completion cause and GITHUB_FOLLOW_REFUSED includes the observed HTTP status and URL. Watch requires its offered Participating and @mentions control and then observes an accepted Watch label or a visible .flash-error, which reports GITHUB_WATCH_REFUSED. Read, pointer and observation errors retain their complete causes; they do not become missing controls. Authentication refusal still closes the session even when stale-cookie recording fails. Ban observation and report-write failures are printed, and a report-write failure sets a nonzero exit status. These control observations are not independent read-backs of GitHub's stored relationships.
WSession.waitFor(selector, { state }) observes attached, visible, hidden or detached state and reports the actual lookup failure when observation fails. WSession has no timed dwell atom, and the low-level CDPPage offers no millisecond sleep method; its selector, document-load and URL conditions are what a trajectory observes. Declared observation IDs are a separate worker contract: their reviewed trajectories are not calls to a dwell atom.
The same holds off the page: a provider's mail is told by its sender and the link it carries (messagesTo in src/_shared/mail/skrzynka-inbox.mjs, which asks Skrzynka for the mail one address received from one sender), a proxy row by its endpoint and the pool type its Skarbiec item declares, and success by the account's session cookies, not by a banner.
A session's check_email tool (the sign-up and sign-in code of every account a registration or a Brama purchase makes) and every journey that reads a provider's mail (Discord login, registration and email verification, GitHub login, registration and password reset, LinkedIn's PIN and confirmation link, Pangram, the twocaptcha balance confirmation, the Semantic Scholar key follow-up) read it from Skrzynka, the product that holds Wisent's mail: Weles runs skrzynka --organization <WELES_SKRZYNKA_ORGANIZATION> message list --recipient <address> --sender <hint>, with the skrzynka program WELES_SKRZYNKA_BIN names or the one on PATH, newest first. check_email answers a numeric code in the newest message's subject or text; a message without one is reported with its text. An unset WELES_SKRZYNKA_ORGANIZATION is refused with WELES_SKRZYNKA_ORGANIZATION names no Skrzynka organization, so the mail to <address> cannot be read, and a refusal of Skrzynka's own is answered as skrzynka … failed: <Skrzynka's sentence>, so a mail-path failure is diagnosed in Skrzynka; the GitHub password reset ends on it instead of retrying. No message yet answers no email … has arrived at <address> yet, and the journey asks again. The address's mail reaches Skrzynka through its inbound-domain mailbox; Weles fetches no address's mail from the mail provider itself. Only the inbound-domain probe (src/trajectories/resend/verify_domain_status.mjs) still reads the receiving provider's own list, because it checks that the domain receives at all.
Interrupted page reads#
When navigation replaces a document during a read, the trajectory can continue observing the new page. If the page closes or crashes, the run fails with the page address and the browser's cause; it does not keep waiting for a page that is gone.
An interrupted read does not establish whether an earlier action succeeded. Review the failed run's evidence and check the provider's resulting state before repeating an action that could submit a form, change an account or make a payment.
Correcting an NCBR application#
An NCBR application is corrected through one plan-driven action, src/trajectories/ncbr/apply_correction.mjs. A worker task on platform ncbr runs it as ncbr_apply_correction, which writes the plan's values, or as ncbr_verify_correction, which only checks them; the task's objective parameter carries the plan JSON. Run from the checkout, it reads the plan from NCBR_CORRECTION_PLAN_FILE or NCBR_CORRECTION_PLAN and the mode from NCBR_CORRECTION_MODE, one of apply, verify or inspect. A missing plan is refused with NCBR_CORRECTION_PLAN_FILE or NCBR_CORRECTION_PLAN is required, and a missing or unknown mode names the three modes; none is assumed.
The run attaches over NCBR_CDP_ENDPOINT, the DevTools endpoint of the signed-in browser, and drives only the open tab of the plan's project; a browser with no page of that project is refused with NCBR_PROJECT_TAB_MISSING. It detaches at the end without closing the browser. It never submits the application, uploads an attachment or edits a reviewer's recommendation.
The plan has schema weles.ncbr.correction-plan.v1. Its project names the LSI2 project UUID, neverSubmit: true, the application number, a title needle, the statuses the application may be in and the correction deadline; a write after the deadline is refused. A plan carrying correctionCard is refused, because the KPW recommendation text belongs to the reviewer. Every section and collection URL must be one of the project's projekt_step pages or its recommendations page, where only beneficiaryExplanation fields may be written, and every collection row names rowNeedle, matchField and matchNeedle. A field takes a literal value, or a list of edits replacing one exact text with another; an edit whose text is not found is refused with Expected text not found unless it is marked optional.
SECTION limits a run to the section or collection of that label; a run that selects no section or row fails with The requested scope selected no sections or rows. Each changed field is saved on its own, because LSI2 drops text typed into a second field before the first is saved; the page is then read again and every field checked against its expected value. inspect records each scope's fields without preparing or writing any.
The run keeps weles.ncbr.correction-report.v1 as correction-report.json in its recordings directory: the run id, mode, project and application number, the plan's SHA-256, the source revision, the application's identity before and after, every scope's fields before, expected and after with screenshots, and submissionClicked false. A failure records the failed page address and a screenshot and ends the run with a failing exit status.
Failure and continuation#
Failure records the exact step, terminal reason, browser/session metadata, and retained artifacts. MFA, passkeys, legal ambiguity, unavailable mailbox access, or an unsolved challenge close or pause explicitly; retrying the same submission is not a substitute for a continuation token or reviewed trajectory change.