You are working locally on my Mac. Start with no assumptions about how the current ChatGPT desktop client is built or how its model picker and send path work. Your job is to inspect the current installed client, understand it, and then build and validate a completely programmatic, disposable ChatGPT client patch that reaches the target state below. Do not stop at a plan, mock-up, architectural proposal, proof-of-concept UI, or partial patch. Carry the work through discovery, implementation, automated tests, bundle construction, strict verification, a clean smoke test, and a normal launch. Keep me informed with concise progress updates while working. ## Objective Create a separate macOS application named **ChatGPT Auto Router** that retains the current ChatGPT/Codex desktop experience but adds a native, persistent **Auto** choice to the Codex/local-task model picker and makes Auto perform per-request semantic model and reasoning-effort selection using only models available through the signed-in ChatGPT account. The same policy should also govern the ordinary Chat surface if that surface already exposes a native Auto lane. Manual model choices must continue to work normally and must never be silently overridden. Every dispatched task turn must visibly and persistently identify the concrete model and reasoning effort that the client actually requested, preferably through the client's existing native timeline/status component. For example: > Your request was routed to GPT-5.6 Sol · High. This must be derived from the concrete values placed in the real request or stored turn parameters—not merely from the classifier's prediction or a label painted optimistically before dispatch. Be precise in documentation: the client can prove what it requested and recorded; it cannot independently prove whether the service performed any opaque server-side substitution afterward. ## Zero-knowledge and evidence rule Treat all details of the installed application as unknown until verified in this run. In particular, do not assume: - that it is Electron, Swift, AppKit, or any other framework; - its bundle version, build number, architecture, signing identity, or paths; - that an ASAR exists; - any renderer chunk name, symbol name, minified anchor, API endpoint, schema, model identifier, effort vocabulary, or picker state representation; - that ordinary Chat and Codex/local tasks share a picker or transport; - that a plugin can modify the host model picker; - that the currently advertised models match documentation or an earlier run. Establish each relevant fact with current local evidence. Keep a compact research record that separates: 1. verified bundle/static evidence; 2. verified runtime evidence; 3. conclusions inferred from those facts; and 4. anything that remains unverified. Use official OpenAI documentation for current public model guidance, but treat the live client catalog/app-server model list as the authority for what this signed-in account can actually select. Never invent or send a model slug or effort value merely because documentation mentions it. ## Safety and authorization boundaries You are authorized to inspect local application bundles, create source files under the project directory, build a disposable application copy, ad-hoc sign that copy, launch it with an isolated profile, and run bounded non-destructive tests. You are not authorized to: - modify, replace, re-sign, delete, or quarantine the installed original `/Applications/ChatGPT.app`; - weaken SIP, Gatekeeper, Keychain, sandboxing, TLS, model authorization, billing, or server-side access controls; - extract, print, copy, migrate, or patch around credentials, cookies, OAuth tokens, Keychain items, app-group data, or secrets; - hide credential-storage warnings with CSS or bypass authentication; - overwrite any unmarked application or delete user data; - register the disposable copy as the handler for ChatGPT/Codex, HTTP, HTTPS, document, or OAuth URL schemes by default; - claim a live signed-in or backend routing test passed when only static/unit checks or an unauthenticated renderer smoke test ran. If an action might be destructive, resolve and validate its exact target first. Move prior marked disposable builds to Trash rather than deleting them. Keep the isolated profile unless explicitly asked to trash it. Do not quit the original ChatGPT client merely to build the copy. If a test instance must be stopped, capture its PID, verify that the process executable is inside the disposable bundle, and stop only that PID and its known helpers. ## Target user experience ### Native picker behavior - The Codex/local-task picker contains a visible **Auto** row alongside the account's real model choices. - Auto is enabled by default for a new disposable profile and persists locally across restarts. - The composer trigger initially says `Auto`, then may show the most recent decision in a compact native label such as `Auto → Terra · Medium`. - Selecting a concrete model disables local Auto and restores the client's normal effort controls and behavior. - Selecting Auto again re-enables routing. - Do not implement this as an unrelated settings window, web overlay, plugin iframe, separate chat, or proxy application. Integrate with the existing native-looking picker/state path in the current client. - A synthetic UI sentinel is allowed internally, but it must be resolved to a real advertised model before persistence into task settings or dispatch to a backend. The sentinel must never appear in a real network/app-server request. Independently implement these principles: - A fast/cheap model is a hidden semantic **classifier only**. It must never be the worker for an Auto-routed user request. - Classification rates what the assistant must **do next**, not how long the current prompt is. - New prompts are classified semantically even when very short. Do not use a prompt-length fast path that routes short requests directly to the cheapest worker. - In the Codex/local-task surface, rate direct imperatives to build, create, implement, make, develop, code, write, or design a software artifact by the complete implementation they obligate—not by their terse wording. A complete small game, app, or website has at least a complex floor; production-scale, multiplayer, full-stack, or cross-cutting builds have at least a hard floor. Do not apply this floor to explicit concept-only questions, plans, reviews, explanations, or brainstorming. - Retain a difficulty level per task/conversation. - Isolate state by stable task/conversation identity, including unsaved/new tasks. Concurrent tasks must not share a prior level, and a stale classifier result must not overwrite a newer turn's decision. - Give the classifier bounded recent context: at least the prior user and assistant turns, with a conservative character cap, plus the previous difficulty level when known. - Obvious continuations such as `continue`, `keep going`, `go on`, `proceed`, `resume`, `retry`, `try again`, `next`, `finish it`, `yes`, `okay`, `do it`, and `carry on` inherit the previous route without another classifier call. - Explicit escalation must be evaluated **before** continuation inheritance, so a phrase such as `Continue to look harder` cannot be mistaken for a plain continuation. - `think harder`, `look harder`, `reason deeply`, `strongest`, `hardest`, `maximum quality`, and equivalent unambiguous requests select the extreme route deterministically. - Requests such as `make it more comprehensive`, `be more thorough`, or `go deeper` have at least a complex floor and must preserve a prior harder level. - Handle negation: `do not think harder` must not trigger escalation. - Only classify a new user turn. Tool results, steering continuations, and internal follow-on events should reuse the conversation's concrete decision or retain native behavior rather than creating visible classifier traffic. - The classifier has a four-second timeout. On malformed output, timeout, or failure, keep the previous difficulty when available; otherwise use the simple route. Routing failure must never strand the task. - The classifier returns only one strict level name and does not answer the user's request, use tools, store a visible conversation, or mutate files. - Keep model selection and reasoning effort as separate decisions. - Document that semantic Auto adds one hidden classifier request, latency, and account usage for each non-continuation user turn that is not handled by a deterministic directive/escalation rule. Use this five-level policy when the corresponding current model families are available: | Level | Worker family | Codex effort | Ordinary Chat preferred effort | |---|---|---|---| | `simple` | Terra | `low` | the catalog's low/min equivalent | | `moderate` | Terra | `low` | the catalog's low/min equivalent | | `complex` | Terra | `medium` | the catalog's standard/medium equivalent | | `hard` | Sol | `medium` | standard/medium, or nearest advertised value | | `extreme` | Sol | `high` | extended/high, or nearest advertised value | Use Luna at low effort as the hidden classifier when it is currently advertised and callable. Luna must not be selected as an Auto worker. Terra is the worker for simple through complex; Sol is reserved for hard and extreme. Other available ChatGPT models remain manually selectable and untouched. Do not hardcode assumptions that cross client surfaces. Discover the exact effort vocabulary each transport expects. For example, if ordinary Chat uses catalog values such as `min`, `standard`, or `extended` while Codex uses values such as `low`, `medium`, or `high`, resolve each level against that surface's actual advertised options and send its exact native value. If a preferred family/effort is unavailable, choose only a defensible concrete fallback from the current advertised list. Prefer the nearest supported effort within the intended worker family. If there is no safe real option, retain the underlying native selection and expose a diagnostic error; never fabricate an identifier. The classifier vocabulary is exactly: - `simple`: a question, small edit, extraction, formatting task, or one command; - `moderate`: a focused feature or fix with a clear objective and a few steps; - `complex`: bounded multi-file work, debugging, architecture, or several careful steps; - `hard`: a complete nontrivial app, subsystem rewrite, deep surgery, migration, or gnarly diagnosis; - `extreme`: only the hardest long-horizon, ambiguous, or tangled cross-cutting work. Specific requests with clear objectives should generally rate lower than vague or sprawling work, except when explicit escalation language applies. Support deterministic diagnostic overrides in user text: ```text [route:simple] [route:moderate] [route:complex] [route:hard] [route:extreme] ``` The directive may remain in the user message, but the resulting concrete route must still obey the live model list. ### Per-turn attribution - Render an attribution row for every local task turn for which a concrete model is recorded, including manual choices. - Prefer the client's existing model-rerouted/timeline component and native wording rather than a custom floating badge. - Read the model and effort from the actual dispatched/stored turn parameters, including any collaboration-mode settings envelope the client uses. - Preserve collaboration mode and all unrelated settings while replacing only Auto's model and effort fields. - The attribution survives task reload because it derives from saved turn state, not a transient global variable. - Historical turns retain their historical model labels. Never rewrite an old Luna attribution after the router changes. ## Phase 1: current-state discovery, read-only Before changing anything: 1. Locate the installed ChatGPT app and any currently running instance. 2. Record the bundle ID, display name, version, build, architecture, executable type, framework/runtime, package metadata, and update mechanism. 3. Verify and record its code signature, Team ID, hardened-runtime state, entitlements relevant to debugging/Keychain/app groups, notarization, and Gatekeeper status. 4. Inventory its resource packaging and integrity mechanisms. If an ASAR or equivalent archive exists, identify how the runtime validates it. Determine experimentally or from the packaging library whether any integrity plist value hashes the whole archive, a header string, or something else. 5. Compute a build manifest containing hashes of the files that will establish compatibility: at minimum Info.plist, the main executable, the application archive, relevant archive header, package metadata, web entrypoint, and every renderer chunk that will be patched. 6. Extract or read only the files needed for analysis into a unique temporary directory. Do not unpack or broadly scan more than is useful. 7. Determine whether official extension/plugin APIs can add a native model option and intercept ordinary host turns. If they cannot, record why and proceed with a disposable client patch instead of pretending a plugin meets the requirement. 8. Locate both model-selection surfaces, their live model sources, selection persistence, label rendering, effort controls, and model-change actions. 9. Trace both send paths from composer submission to the final request/task dispatch. Identify the last safe semantic point at which an Auto sentinel can be resolved before request construction. 10. Locate an existing authenticated, hidden, no-tools model transport suitable for classification if the client has one. Determine required initialization, timeout/abort behavior, output parsing, and whether it creates visible conversation state. Do not introduce an API key requirement when the signed- in client already has a suitable internal route. 11. Determine the real model and effort schemas separately for ordinary Chat and Codex/local tasks. 12. Locate the native per-turn component and the stored turn fields from which actual model attribution can be reconstructed. Use parallel read-only investigation where useful, but personally verify every fact that will control a write. LLDB, Ghidra, disassembly, runtime inspection, or Chrome DevTools are available if needed, but do not attach to or alter the signed original. Prefer static renderer inspection when it provides the needed evidence. If runtime instrumentation becomes necessary, use only a disposable copy made for that purpose. At the end of discovery, state the exact patch seams and why they are the smallest reliable ones. Then continue directly into implementation unless a genuine safety blocker exists. ## Phase 2: create a reproducible patcher project Create the project at: ```text /Users//Downloads/chatgpt-auto-router ``` If that path already exists, inspect it and preserve unrelated/user changes; do not erase or replace the directory wholesale. The project must be understandable and runnable by another person. Include at least: ```text README.md .gitignore package.json package-lock.json manifests/.json src/patcher.mjs src/router-injection.mjs test/*.test.mjs ``` Expose these commands, or clearly equivalent ones: ```sh npm ci npm test npm run probe npm run build npm run verify npm run smoke npm run launch npm run patch -- uninstall ``` The default output and profile should be distinct and obvious: ```text dist/ChatGPT Auto Router.app dist/profile ``` ### Patcher requirements - Pin supported Node/npm versions with `engines` and `packageManager`. Use a pinned archive-packaging dependency and lockfile; prefer `npm ci --ignore-scripts` after confirming that no required dependency needs an install lifecycle script. Do not let a normal build rewrite the lockfile. - Use a private `umask` such as `077`. Canonicalize and validate source, output, staging, profile, and Trash paths; reject root/home-wide targets, source/output equality or nesting, paths that escape through symlinks, and builds performed through an existing output symlink. Refuse root execution unless a verified current requirement makes it unavoidable. - Preflight sufficient free space. Create staging with `mkdtemp` on the same filesystem as the final output so a fully verified build can be published by atomic rename. - `probe` performs read-only inspection, reports support, fingerprints, signing evidence, and exact anchor counts. It may emit a **candidate** manifest, but `build` must never auto-admit or rewrite support for a new version. Supporting a new build requires an intentional, reviewed manifest change. - Pin the exact discovered source build in a manifest. Subsequent build/verify operations fail closed if the version, hashes, relevant metadata, or anchor cardinality changes. - Every patch anchor must occur exactly once. Refuse zero or multiple matches. - Embed a versioned patch marker and refuse accidental double-patching. - Copy the original to a private staging path with metadata preserved. Recheck source hashes before and after copying so an in-progress updater cannot create a mixed build. - Patch archive contents programmatically. Preserve entry order, file modes, symlinks, unpacked flags, and every unchanged byte where the format permits. - Diff the rebuilt archive against the original. Allow only the explicitly documented package/renderer/timeline paths to change. Reject unexpected additions, removals, changed unpacked state, external symlinks, or native binary changes. - Also enforce a whole-bundle change allowlist. Expected outer changes may include `Info.plist`, the application archive, the added patch manifest, the removed provisioning profile, the main executable signature, and outer `_CodeSignature` resources. Require every other framework, helper, resource, native file, and nested signed object to remain byte-identical. - Define an extended-attribute policy instead of blindly cloning or stripping everything. Preserve resource forks and required metadata, do not transplant inappropriate source provenance/MACL attributes into the lab copy, and never alter the original's attributes. If quarantine prevents the ad-hoc copy from launching, report it explicitly rather than silently weakening Gatekeeper. - Recompute every packaging-integrity value the current runtime requires. If Electron ASAR integrity is present, verify whether the plist value is the SHA-256 of `getRawHeader(app.asar).headerString`; do not confuse it with the whole-file hash. - Give the disposable copy a different bundle identifier, display name, and name. Disable automatic updates and remove copied provisioning data. - Remove URL schemes and document handlers by default. If a scheme is needed for login, make retaining it an explicit documented option; do not silently compete with the installed client. - Add an outer signed manifest recording patch version, source version/build, source fingerprints, patched hashes, permitted changed paths, safety choices, and build time. Here “reproducible” means a repeatable, verified process; if claiming byte-identical output, use stable archive ordering and an explicit `SOURCE_DATE_EPOCH` instead of an uncontrolled wall-clock field. - Ad-hoc sign the outer disposable app without claiming OpenAI's Team ID. Sign without `--deep`, without metadata preservation, and with `--timestamp=none`; do not blindly replace nested signatures. If Chromium requires hardened runtime or JIT-related entitlements, retain only the independently justified minimal set—never OpenAI keychain, app-group, push, or other restricted Team-ID entitlements. Compare every nested signed object's CDHash before and after. - Verify the final bundle recursively. Gatekeeper rejection of an ad-hoc lab copy is expected; it must not falsely appear to retain OpenAI notarization. - Never overwrite an existing app unless it contains this patcher's own marker and the caller passes an explicit replace flag. For replacement, fully verify staging, uniquely move only the marker-validated prior build to Trash, atomically rename staging into place, and restore the prior copy if publication fails. - `uninstall` verifies the marker and exact paths, moves only the disposable app to Trash, retains the profile by default, and re-verifies the original. - Do not use broad destructive paths, unresolved globs, `$HOME`, or `~` as removal targets. - Add `dist/`, profiles, and logs to `.gitignore`. Create application/profile/log directories with private permissions (profile at least `0700`), never record auth headers or cookies in logs/reports, and reject a profile path that escapes its approved location through a symlink. ### Runtime injection requirements - Integrate at semantic state/dispatch seams discovered in the current build; do not depend solely on DOM click simulation or `window.fetch` if transport uses an internal bridge. - Reuse a native Auto lane where one already exists. Add a synthetic Auto model only to the surface that lacks one, and update every version-specific list the picker actually reads. - Resolve Auto immediately before final request/task construction so the concrete model and effort reach prepare/cache logic and dispatch consistently. - Ensure cached prepare requests cannot cause a route/model mismatch. - Gate ordinary Chat routing to the proper conversation origin and leave Work/TPP or other unsupported host modes untouched unless their exact semantics have been verified. - For Codex collaboration-mode turns, preserve the mode envelope and update only its model and reasoning-effort settings. - Do not append routing metadata to the user prompt or assistant answer. - Persist only the Auto toggle and normal client state; keep transient routing diagnostics bounded. - Expose a non-secret diagnostic object such as `globalThis.__CHATGPT_AUTO_ROUTER__` with patch version, enabled flags, classifier model, timeout, most recent Chat/Codex decisions, last error, available Codex model list, policy helpers, and a bounded history (for example 50 decisions). Never place credentials or response bodies in it. - A renderer-session kill switch such as `enabled = false` should bypass both Auto resolvers cleanly. ## Phase 3: tests and validation Build a test suite before declaring success. ### Pure policy tests Use synthetic catalogs/model lists and prove: - every level resolves only to an advertised concrete model and supported effort; - the Auto sentinel is never returned as a worker; - Luna is never returned as an Auto worker; - simple/moderate/complex prefer Terra with the intended effort; - hard/extreme prefer Sol with the intended effort; - missing preferred effort selects a real nearest advertised fallback; - malformed classifier output returns null and triggers the documented fallback; - fenced JSON, compact JSON, and one-word classifier results parse safely if the implementation permits them; - explicit route directives are deterministic; - continuation detection and previous-level inference are deterministic; - classifier context includes bounded recent turns and the previous level; - negated escalation does not escalate. Include exact regression cases for: ```text THINK HARDER. Continue to look harder Make it more comprehensive, think harder. Please don't think harder. Design a snake-copy game How would you design a snake-copy game? ``` The first three must resolve to `extreme`, which maps to current Sol + High on the Codex surface when that pair is advertised. The negated case must not be forced to extreme. `Design a snake-copy game` on the Codex surface must have a `complex` floor and therefore route to current Terra + Medium when advertised. The conceptual `How would you design...` variant must not be forced into implementation scope. ### Static injection tests - Confirm all discovered anchors occur exactly once in the pinned original. - Generate the complete patched renderer/timeline source and run a JavaScript syntax check over the full result. - Assert the patch marker, Auto picker integration, classifier, context, escalation, Codex dispatch, collaboration-mode handling, diagnostic policy, and timeline attribution are present. - Assert obsolete prompt-length routing is absent from the actual runtime path. ### Bundle verification After building: - verify patched bundle ID/display name and patch version; - verify archive whole-file and header-integrity hashes; - verify the plist integrity value equals the rebuilt archive's required value; - enumerate exact changed archive paths; - compare all unchanged archived files and all unpacked native files, including modes and unpacked state; - verify recursive code signatures; - prove the outer signature is ad hoc and has no OpenAI Team ID; - confirm provisioning profile, updater, URL handlers, and document handlers follow the declared safety policy; - re-hash and re-verify the installed original, including its OpenAI signature and Gatekeeper acceptance. ### Clean smoke test Launch the disposable app with a brand-new temporary profile and loopback-only Chrome DevTools using an ephemeral port. Do not expose remote debugging beyond `127.0.0.1`, and do not enable it for normal launch. Verify: - the main process remains alive; - the main `app://` or equivalent renderer loads; - the expected patch version is exposed in diagnostic state; - there are no ASAR-integrity, signature, missing-library, or fatal startup errors; - the test terminates only its captured disposable PID; - the temporary profile is removed afterward; - a machine-readable smoke report is saved under `dist/`. An unauthenticated smoke test may legitimately lack the signed-in model picker. Report that distinction instead of treating it as proof of visible UI behavior. ### Signed-in live validation Launch normally using only: ```text dist/profile/browser dist/profile/codex-home dist/profile/logs ``` Never reuse or copy the original client's profile. The ad-hoc copy may not have OpenAI Team-ID Keychain/app-group privileges, so an interactive sign-in may be required. If login is the only blocker, leave the app open and ask me to sign in; do not patch around it. Normal launch must execute the exact disposable Mach-O rather than relying on bundle-name resolution such as `open -a`. Pass an explicit isolated `--user-data-dir`, set `CODEX_HOME` to the isolated profile, direct stdout and stderr to its private log directory, and defensively disable the updater in the launch environment. Verify the effective runtime paths; a changed bundle ID by itself is not proof of profile isolation. After sign-in, validate visually and through bounded runtime diagnostics: - Auto appears natively in the Codex model picker; - Auto selection survives a restart; - selecting a manual model disables Auto and restores effort controls; - selecting Auto re-enables it; - the diagnostic model list contains real advertised options; - policy evaluation maps the four regression phrases correctly; - existing historical attribution remains unchanged; - a newly dispatched harmless test turn stores a concrete model/effort, never the Auto sentinel, and displays the matching native attribution row. You may send only these harmless non-mutating messages in the disposable client for live validation, and only if the account is already signed in: ```text [route:simple] Reply with exactly: route test complete [route:extreme] Reply with exactly: route test complete ``` Do not run a filesystem-changing task merely to test routing. If live sending is unavailable, perform the strongest local transport/policy verification possible and label live backend acceptance as pending. ## Acceptance criteria Do not call the task complete unless all applicable items below are true: 1. The installed original is byte-for-byte unchanged in all pinned artifacts and still verifies as OpenAI-signed and Gatekeeper-accepted. 2. A reproducible, version-pinned, fail-closed patcher project exists. 3. A distinct ad-hoc signed disposable app builds and verifies. 4. Auto is a native choice in the Codex/local-task picker and persists. 5. Manual model selection remains untouched and disables Auto. 6. The Auto sentinel cannot reach the backend/app server. 7. Luna classifies but never processes Auto-routed user work. 8. Terra handles simple through complex; Sol handles hard/extreme, using only currently advertised exact models/efforts. 9. Recent context, previous difficulty, continuation inheritance, escalation precedence, negation, four-second timeout, and safe fallback are implemented. 10. Every concrete local turn can show durable native model/effort attribution from actual stored dispatch data. 11. Unit, injection, bundle, signature, and clean smoke checks pass. 12. Signed-in live validation is either passed with evidence or explicitly marked pending because authentication/user interaction is required. 13. The normal disposable app is left launched without remote debugging, and logs/profile paths are reported. 14. README documents usage, routing policy, security limitations, update brittleness, verification, replacement, and uninstall/recovery. ## Failure and update behavior - A future ChatGPT update must fail as an unsupported fingerprint instead of patching guessed symbols. - If the app changes mid-copy, discard only the private staging copy and retry from a fresh verified source. - If no unique semantic patch seam can be established, stop before producing a runnable app and report the evidence and exact blocker. - If classification transport is unavailable, do not add a secret API-key dependency silently. Report the limitation and implement only a clearly labeled deterministic fallback if it still satisfies the target. - If the preferred current model family is unavailable for this account, do not bypass authorization; use only documented advertised fallbacks and report the difference. - If the app launches but sign-in is unavailable because of ad-hoc entitlement limitations, do not weaken credential storage. Preserve the build and report live validation as blocked on interactive authentication. ## Final handoff Lead with the achieved state, not a diary of commands. Report: - discovered source app version/build/architecture and signing identity; - the exact disposable app, project, profile, log, manifest, and smoke-report paths as clickable absolute links; - the implemented routing table and classifier/worker separation; - how native Auto, persistence, manual override, context, continuation, escalation, fallback, collaboration mode, and attribution work; - exact test counts and commands run; - exact archive paths changed and confirmation that native files were preserved; - code-signing, ASAR/package integrity, and original-app verification results; - signed-in live validation evidence, clearly separated from static/smoke tests; - limitations: unsupported patch, update brittleness, classifier latency/usage, ad-hoc Keychain/app-group constraints, server-side authorization and opaque server substitutions, and historical attribution behavior; - how to rebuild, launch, disable for a renderer session, replace safely, and uninstall/recover from Trash. Do not claim success from screenshots alone, from a static picker mock-up, from unit tests alone, or from the classifier choosing a label. The finish line is a verified disposable client whose actual outgoing/stored concrete model and effort match the visible per-turn attribution while the original client remains untouched. Describe that as the requested/dispatched route, not unverifiable proof of the service's internal physical execution path.