--- name: sessionize-automation description: | Use when inspecting or automating authenticated Sessionize organizer workflows, especially bulk session status changes, proposal accept/decline staging, speaker/session invite flows, hidden form submit paths, anti-forgery tokens, nested NewSpeakers fields, and verification after private organizer UI writes. --- # Sessionize Automation Use this skill for Sessionize organizer tasks where there is no supported public API and the useful write surface is the authenticated web app. ## When To Use - Bulk accept, decline, waitlist, queue, or nomination status changes. - Tracing Sessionize organizer pages to discover private form or XHR endpoints. - Automating speaker/session invite flows that only exist in the logged-in UI. - Reconciling local review decisions against Sessionize session IDs. - Verifying that Sessionize-side counters and row states changed correctly. Do not claim Sessionize has an official organizer API unless you have verified a current official API surface for the exact operation. ## Prerequisites - Use tab-bound Chrome control when the task depends on the user's logged-in Sessionize organizer state. Follow [resilient-computer-use](../resilient-computer-use/SKILL.md#chrome-default-bind-to-a-tab) so the user and other Codex chats can use Chrome concurrently. - Claim the intended Sessionize tab by stable ID when available and not in use by another chat; otherwise use a dedicated tab. Keep reads, writes, and any network inspection scoped to that handle. Coordinate shared focus before a native desktop fallback. - Use Browser only as a fallback for non-authenticated inspection or screenshots. - Identify the event ID and the authoritative local source of truth before any write. - Map local records to Sessionize IDs. For `/Users/swyx/Work/aiewf2026speakerenrichment`, use `External Session ID`. ## Browser Automation Constraints Use the current browser adapter's documented capabilities and restrictions. In prior Codex browser runs, `evaluate(...)` could read DOM state but could not access page globals, `fetch`, `XMLHttpRequest`, or `javascript:` execution. Those are adapter-specific observations, not Sessionize restrictions or limits on other agents. When the current adapter imposes these limits, do not route around them. Use visible page controls, or prepare a dry-run-first page-console runner for the user when appropriate. If the adapter supports authorized same-origin requests, use its documented interface and verify the saved result. ## Discovery Workflow 1. Capture the current URL, title, and visible Sessionize counters. 2. Inspect DOM structure for stable IDs and controls. Hidden controls are usually more stable than visual dropdown text. 3. Inspect inline page scripts for endpoints, payload names, status enums, and modal/bulk action handlers. 4. Prefer no-op probes or read-only extraction before writes. 5. If writes are needed, start with a tiny batch and verify before scaling. 6. Immediately before the first status change or form submission, obtain the browser action confirmation required for the exact Sessionize account, operation, target count, and status. One confirmation can cover the bounded batch; expanding the target set or changing finality requires a new one. Useful DOM reads for session status pages: ```js [...document.querySelectorAll('a[href^="#s="]')] .map(a => (a.innerText || '').replace(/\s+/g, ' ').trim()) [...document.querySelectorAll('select[data-id]')] .filter(s => s.value && s.getAttribute('data-id')) .map(s => ({ id: s.getAttribute('data-id'), value: s.value })) ``` ## Known Session Status Contract On the organizer sessions page, status controls were observed as hidden `select[data-id=""]` elements wrapped by Selectize UI. For event `24196`, individual row status changes posted: ```text POST /app/organizer/session/setStatus id=&status= ``` Observed status codes: | Code | Meaning | |------|---------| | `0` | Accepted | | `2` | Waitlisted | | `5` | Accept Queue | | `10` | Nominated | | `15` | Decline Queue | | `20` | Declined | For rejection staging, prefer `15` (`Decline Queue`) unless the user explicitly asks for final `20` (`Declined`). ## Bulk Status Workflow 1. Identify the local target set and status code. 2. Confirm target IDs are unique and nonblank. 3. Generate per-run artifacts outside commits unless the user asks otherwise. 4. Capture Sessionize baseline counters. 5. Run a small batch first, usually 3 to 21 items. 6. Verify row controls and counters moved by exactly the batch size. 7. Continue with explicit offsets and batch sizes. 8. Keep a short run log: start offset, limit, successes, failures, before/after counters. In `/Users/swyx/Work/aiewf2026speakerenrichment`, generate Sessionize denial targets from `overrides.json` plus source decisions: ```bash python3 scripts/sessionize_rejection_targets.py ``` This writes ignored artifacts: - `sessionize-deny-targets.json` - `sessionize-bulk-status-runner.js` The generated runner defaults to `15` (`Decline Queue`). ## Visible UI Control Path Use this for tiny batches or when page-console execution is unavailable. For each target row, click: ```text select[data-id=""] + .selectize-control .selectize-input ``` then click: ```text select[data-id=""] + .selectize-control .selectize-dropdown .option[data-value="15"] ``` After each click, verify: ```js document.querySelector('select[data-id=""]')?.value === '15' ``` For sparse target rows, use Sessionize's own URL hash search with the exact session title, then update one matching ID: ```text https://sessionize.com/app/organizer/sessions/#q=&o=DateDesc ``` ## Page-Console Runner Path Use this for larger batches when the user can paste JavaScript into the actual Sessionize page console. The runner should: - refuse to run unless it is on `sessionize.com` - refuse to run unless the organizer path includes the expected event ID - print a dry-run table immediately - expose an explicit function such as `window.sessionizeBulkStatusRun()` - accept `{ limit, startAt, delayMs }` options - stop on first failed response - preserve partial results on `window` - log progress every 25 items Example execution: ```js await window.sessionizeBulkStatusRun({ limit: 3 }) await window.sessionizeBulkStatusRun({ startAt: 3, limit: 18 }) await window.sessionizeBulkStatusRun() ``` ## Form Automation Notes For Sessionize session edit/invite forms, prior organizer inspection found that the useful write surface may be a normal authenticated form with: - anti-forgery token fields such as `__RequestVerificationToken` - nested speaker fields such as `NewSpeakers[...]` - invite-related fields such as `InviteEmail` and `HasNoInvite` - speaker link arrays such as `SpeakerLinks[n].Type` and `SpeakerLinks[n].Url` When automating forms: 1. Load the edit form through the authenticated browser. 2. Extract hidden inputs and all named fields before designing the payload. 3. Preserve anti-forgery tokens and unrelated existing fields. 4. Submit one dry-run or harmless test record first where possible. 5. Re-open the affected Sessionize page and verify the rendered result. ## Verification Checklist - Confirm local target count from the source of truth, not a stale export. - Confirm status code and staging/final intent. - Capture before/after Sessionize counters. - Confirm no visible toasts or console errors after the first write. - For UI-driven batches, verify every changed hidden select has the intended value. - For page-console runners, inspect returned results and failed response text. - Keep the Chrome tab as `handoff` if the user will continue from the live page. ## Process Lessons - DOM plus page script snippets beat screenshots for discovering automation contracts. - Hidden `select[data-id]` controls were stable for status changes. - Top counters may lag after one row, but should update after a real batch. - Current filters can hide most target rows; switch to exact title searches when the visible list is exhausted. - Curated/generated CSVs can lag live review state; recompute effective decisions from source export plus overrides before acting.