Skip to content

v0.8.0

Choose a tag to compare

@github-actions github-actions released this 08 Oct 02:17
· 868 commits to main since this release
v0.8.0

Added

  • Pitboard can switch Claude Code by itself before the account in use runs out, so an agent
    working in a running claude session can go on under another of your accounts. Once any
    limit of the account in use reaches a share you choose, 95% by default, Pitboard
    switches to the enrolled account with the most room in that limit, among those that have
    used less than the share of every limit. With no such account, nothing moves. It
    switches before the limit, not at it, because sessions already running follow a switch
    within about 33 seconds. It is off until you turn it on. It never switches Codex, whose
    running sessions keep their account until restarted, and never switches back by itself.
    Each switch is the one pitboard use makes, and pitboard log records it as
    auto-switch. What was tried for each limit is kept in autoswitch.json in Pitboard's
    directory. See Switch before an account runs
    out
    .
  • pitboard watch switches Claude Code that way for as long as it runs in a terminal,
    whatever the app's setting says. Control-C stops it. Nothing installs it, and it starts
    nothing that outlives it. --at sets the share, from 50 to 99. It reads every account
    every 5 minutes, as the app does, and decides again within 2 seconds of a status line
    recording usage. It prints a line, starting with the local time, for each switch, and
    once for each thing that stops one. --once decides once from the numbers Pitboard last
    read and exits. With --json, each event is an envelope on a line of its own, whose
    data.event is watching, switched, no_room, skipped or idle.
  • The app's Settings > General has a Before an account runs out section, with
    Switch Claude Code automatically, off by default, and the share it switches at, from
    50% to 99%. The app keeps both in app.json, as auto_switch and auto_switch_at, and
    switches only while it is open. After a switch it posts a notification such as
    Switched Claude Code to personal, and the window shows the switch as one made by
    hand. When something stops a switch, or a switch it tries fails, it says why in a
    notification, once until it next switches. The Activity pane names such a switch
    Automatic switch.
  • pitboard doctor has a network check, which the app's This Mac pane shows too. It
    says whether Pitboard's requests go out directly or through a proxy, names the proxy and
    the variable it came from, such as
    through http://***:***@proxy.example.com:3128, from HTTPS_PROXY, and lists the hosts
    Pitboard calls that NO_PROXY exempts. Neither report shows the user name or password
    in a proxy's address, and --json shows a digest in place of its host and port, as it
    does an organisation's name, unless the proxy is on loopback. It warns when the proxy is
    a SOCKS one, which Pitboard does not go through, in the words a request then fails with,
    unless NO_PROXY exempts every host Pitboard calls. It also warns when a variable holds
    no proxy address Pitboard can read, which Pitboard passes over.
  • The daily renewal schedule goes through the proxy of the Pitboard that installs it.
    pitboard schedule install, and turning on Renew parked logins daily in the app,
    write ALL_PROXY, HTTPS_PROXY, HTTP_PROXY and NO_PROXY, in whichever case each is
    set, into the LaunchAgent's EnvironmentVariables or the systemd service's
    Environment= lines, as they write PITBOARD_NO_ARGV. launchd and systemd start the
    schedule without your shell's environment, so its requests went out directly, unless
    they gave every job a proxy. Install it again after your proxy changes. On macOS,
    pitboard schedule install refuses a variable holding a character a LaunchAgent cannot
    hold, such as the control character U+0001, and names it. pitboard doctor has a
    schedule_proxy check, which warns when the schedule's proxy differs from the one the
    run sees, with each user name and password as ***. Where the run's proxy is a SOCKS
    one that fails its requests, it says to change that variable before installing the
    schedule again, which would give the schedule that proxy too. In the app's This Mac
    pane the same line is not a warning: the app runs with the variables macOS gives it, not
    your shell's, and it says that the app's switch would give the schedule the app's.

Changed

  • A Windows build of Pitboard, from crates.io or from source, now compiles, for x64 and
    ARM64 with Microsoft's toolchain. It says Pitboard for Windows is not released yet. This build changes nothing., with the code windows_not_released in --json, and reads and
    changes nothing: only --version, --help, completions and manpage answer. Until now
    it stopped compiling with the first of those sentences. On Windows,
    cargo binstall pitboard finds no release to download and stops, rather than build this
    one from source. macOS and Linux are unchanged.
  • The product is called Pitboard, with a capital P, everywhere it names itself: the app
    (its menus, About, Login Items and the Share menu entry, Pitboard and Pitboard
    Debug
    ), the command line's messages and help, and the docs. The command is still
    pitboard, and nothing it reads, writes or prints as a key changes. The home check of
    pitboard doctor --json is now named Pitboard home; its code is still home.
  • pitboard status names a limit whose length is not a whole number of hours by its
    minutes or seconds, as the app does, such as 90m or 45s. It showed the limit's kind
    instead, such as 90_minute, a name Pitboard builds from the length of a Codex limit.
    pitboard status --json still gives the kind.
  • The app says when a limit resets the way pitboard status does: resets in 2h 05m,
    with the minutes in two digits, and resetting now once that time has come. It said
    in 2h 5m, and nothing once the time had passed, so a limit whose reset was due showed
    its old figure with nothing beside it. VoiceOver says resetting now too.
  • The app says how long an account lasts with the spans pitboard status uses, such as
    About 1h 05m left at this rate or Resets in 3d 2h, where it said About 1h 5m
    left at this rate
    or Resets in 3d 2h 5m. Under a minute, pitboard status says
    about to run out or resets any moment, as the app does, where it said
    about <1m left at this rate or resets in <1m.
  • pitboard doctor and the app's This Mac pane sum up the checks in the same words:
    Everything Pitboard checks is in order. when every check holds,
    One thing is worth looking at. or 2 things are worth looking at. when checks only
    warn, and 1 broken: do not switch accounts until fixed. when a check fails. doctor
    said Everything Pitboard relies on holds. or 2 to look at; nothing is broken., and the
    app counted a check that fails as one more thing worth looking at.
  • pitboard renew and the app's Renew Now report a run in the same words:
    No parked login was due., Renewed one., Renewed all 2. or
    Renewed 1 of 2; the rest are tried again next time. renew said Renewed 1. or
    Renewed 2. when it renewed everything due, and the app said Nothing was due. or
    Renewed 1 of 2.
  • The command line runs the claude or codex that PITBOARD_CLAUDE or PITBOARD_CODEX
    names, as the app does, to sign in, and pitboard doctor reports that program's build.
    Before, only the app read them, and the command line always looked on its PATH. Set
    but empty, either names nothing, and the program is looked for on PATH as before.
  • In the app, an empty PITBOARD_CLAUDE or PITBOARD_CODEX names nothing too, and the app
    looks for the program as it does when neither is set. Before, it offered the tool, then
    refused its sign-in because the program was not installed.
  • Pitboard checks a link shared with it, or carried in a pitboard:// link, in its core
    now, which changes three rare answers:
    • A site's host followed by /, ? or # and then a combining mark or a joiner, such
      as chat.com/ and an accent, is a link to that site. Pitboard took the mark and the
      character before it as one, and found no link.
    • A link is too long past 8,192 Unicode scalars, where it was past 8,192 characters as
      they show on screen. A link of letters each followed by a combining accent is too long
      at about half the length it was.
    • A host written partly in full-width letters and partly in escapes, such as
      c%EF%BD%8Caude.ai, is claude.ai, as the WHATWG URL standard maps it. Pitboard said
      it was another site.
  • The app keeps its own preferences, the tools you said Not Now to a second account for
    and whether it has opened its window on a first launch, in app.json in Pitboard's
    directory, so they follow PITBOARD_HOME as your accounts do. It kept one set in its
    macOS preferences for every Pitboard directory, so a Not Now said with one
    PITBOARD_HOME held with another. The first time it opens a directory with no app.json,
    it takes what its macOS preferences held, once.
  • A notification that an account in use has run out is posted once for each reset of that
    limit, however often the app is quit and opened again meanwhile: what was posted is kept
    in told.json in Pitboard's directory. The app posted it again after every relaunch. The
    window still says it after a relaunch, as before.
  • What the app says that depends only on the time, such as used up until 14:05 in the
    menu, is worked out once a minute, so a menu opened between two minutes can say it for up
    to a minute after that time, as its bars already could. It was worked out as the menu
    opened.
  • The Activity list names a switch Switch, from either front end. It named one
    Use, after pitboard use, the command the log records a switch by.
  • The settings learn whether this copy of the app can turn on daily renewal, or link its
    command line, when they read the schedule or look for the command line, which they do
    each time they are shown. They asked the file system again each time they drew, so a copy
    moved meanwhile showed the new answer at once.
  • The app keeps the account windows' records, the data folders it made for each Pitboard
    directory and the page each window was last on, in windows.json in
    ~/Library/Application Support/com.usepitboard.Pitboard, private to you. It kept them in
    its macOS preferences, as webStores and windowPages. The first time it opens without
    windows.json, it takes what those held, once, and removes them once the file is there.
    Where windows.json is there and the app cannot read it, or cannot read records in it, as
    after a hand edit, the app deletes no window's data and never writes over the file. Each
    window then opens at its site's home with the sign-in note at each launch, and keeps its
    data.
  • Building Pitboard on Windows, as cargo install pitboard does there, stops with
    Pitboard for Windows is not released yet. It said that Pitboard runs on macOS and
    Linux, and that another system needs a host of its own. Pitboard still does not build on
    Windows.
  • Pitboard's facts about Claude Code and Codex say, for each of macOS, Linux and Windows,
    whether they were read there and from which build. Read from the Windows builds, x64 and
    ARM64, seven of Claude Code's 17 facts hold on 2.1.289, and nine of Codex's 16 on
    0.160.0. Three of Claude Code's are not read on Windows: two are about the keychain, and
    the Windows build's Credential Manager store calls the Bun.secrets the third rules out.
    Seven of each tool's facts wait on the Windows work. Where Claude Code finds its config
    file was read again, from 2.1.289 on every system. The conformance check reads both
    tools' Windows builds, and Codex's macOS build as well as its Linux one. What Pitboard
    does is unchanged.
  • In pitboard-core, assumptions::Platform gains Windows, and Platform::ALL, which
    meant macOS and Linux, is gone. assumptions::read_on returns a Vec<Platform> in place
    of a &'static [Platform], and assumptions::verified_against takes the system as well
    as the tool. assumptions::OnSystem, PerSystem, per_system, on, verified_on and
    pending are new. These change the crate's public API.
  • The JSON that --json prints is ASCII alone, on every system. Each character outside
    ASCII, in a label or an email address for example, is written as a \u escape: the label
    Đạt is written \u0110\u1ea1t, and a character beyond U+FFFF as its UTF-16 surrogate
    pair. A JSON parser reads the same values as before. Pitboard wrote such characters as
    UTF-8, which a program that decodes the output in another code page read as other
    characters. This reaches every output that carries such a character, whatever your labels
    are: each pitboard doctor --json detail joins its parts with ·, and
    pitboard statusline --json's line does too, so both now carry \u00b7. What Pitboard
    prints without --json, and the JSON files Pitboard keeps, are unchanged.
  • On macOS and Linux, Pitboard refuses every command that changes something when it runs
    as root or with sudo, with the error code elevated:
    Pitboard changes nothing when it runs as root or with sudo. Run it as yourself. That
    includes a container or a WSL distribution whose only user is root. Pitboard tells
    sudo by SUDO_UID being set, as it is under sudo -u another user too, and in a
    shell, tmux or editor server started with sudo -iu and your own name, which passes
    SUDO_UID to everything it starts; env -u SUDO_UID runs a command without it. Pitboard
    changed things as root before: a file it made then was root's, and a keychain item might
    have been, where your own runs might not read or replace it. A refused command writes
    nothing, not even its line in pitboard log. pitboard schedule uninstall and
    pitboard uninstall are refused too, so a schedule or a Pitboard directory made as root
    is removed by hand, as
    Pitboard changes nothing as root
    says.
    • pitboard status still answers, with what pitboard status --offline shows and the
      warning read_only, which says why. It renews no parked login, asks Anthropic and
      OpenAI nothing and writes nothing. The status line still answers too, and writes
      nothing.
    • pitboard doctor fails its elevated check, first, with how Pitboard runs.
    • pitboard renew --json reports a refused run as the error elevated, where it gave
      an empty list, and Renew Now in the app says why, where it said
      No parked login was due.
    • forget and uninstall ask no question whose answer would be refused, and
      enroll --sign-in does not say it is opening a sign-in.
  • In pitboard-core, service::Pitboard::renew returns a Result, and
    service::Permit and Pitboard::permit are new. Everything outside service that
    writes, removes, changes a store of logins or the scheduler, starts a tool's sign-in or
    renews a parked login takes a Permit, which only Pitboard::permit makes: among them
    app::write_file, audit::record, schedule::install, status::gather,
    switch::settle and statusline::read, which takes an Option<Permit> and writes
    nothing without one. host::Elevation is new. These change the crate's public API.
  • pitboard schedule install, and turning on Renew parked logins daily in the app's
    settings, refuse while PITBOARD_HOME names a directory other than ~/.pitboard, with
    the error code schedule_not_default_home, and install nothing. The schedule renews the
    parked logins in ~/.pitboard alone, so it would leave those in that directory to run
    out. Both installed it all the same, and it renewed ~/.pitboard. To turn on daily
    renewal, unset PITBOARD_HOME. To renew the parked logins in another directory, run
    pitboard renew with PITBOARD_HOME set. pitboard schedule uninstall, and turning
    the switch off in the app, still remove the schedule whatever PITBOARD_HOME says.
    Pitboard compares the two as paths, so a PITBOARD_HOME that is a link to
    ~/.pitboard is refused too. While PITBOARD_HOME names another directory,
    pitboard schedule status adds that the schedule renews only the parked logins in
    ~/.pitboard, and with nothing installed it says to unset PITBOARD_HOME before
    pitboard schedule install. It said to run pitboard schedule install, which is
    refused there.
  • Pitboard refuses every SOCKS proxy: socks4://, socks4a://, socks5://, socks5h://
    and socks://, which is socks5://. Pitboard takes its proxy from the first of
    ALL_PROXY, HTTPS_PROXY and HTTP_PROXY, each read before its lower-case spelling,
    that holds an address it can read. While that variable names a SOCKS proxy, each request
    to Anthropic or OpenAI fails before anything is sent, with the cause unreachable and
    the reason Pitboard does not use SOCKS proxies yet, and ALL_PROXY names one, naming
    that variable. Requests with a socks4://, socks5:// or socks:// proxy no longer go
    out directly, around the proxy you set. Requests with a socks4a:// or socks5h:// one
    failed with Connection refused, and fail with that reason instead. A host NO_PROXY
    names is still reached directly, without the proxy's own name being looked up, and a
    redirect from it is not followed while a SOCKS proxy is named. Pitboard does not go
    through SOCKS proxies because ureq 3.4.2, the library it sends requests with, waits past
    the request's 5 seconds on one that never answers, and looks the proxy up before it asks
    NO_PROXY.
  • Pitboard writes the daily renewal schedule's files, the LaunchAgent and the systemd
    service and timer, so that only you can read them, mode 600, whatever mode a file
    already there had. They can hold a proxy's user name and password. A file Pitboard wrote
    where none was there was already 600, and one already there kept its own mode, such as
    644.
  • In pitboard-core, service::Pitboard::check_homes and
    service::Pitboard::schedule_renews_this_home, the errors Error::HomeNotAbsolute and
    Error::ScheduleNotDefaultHome, Context::started_by_the_schedule and
    schedule::SCHEDULED_RUN are new, and Context::with_claude_config_dir keeps an empty
    value, which it dropped as unset.
  • Pitboard's own Codex sign-ins, the ones pitboard enroll codex/<label> --sign-in and the
    app's Add Account and Sign In Again run, start Codex as
    codex -c cli_auth_credentials_store="file" login, so Codex keeps the new login in
    auth.json in the private folder Pitboard reads it back from. /etc/codex/config.toml,
    or a trusted project's .codex/config.toml, could have sent it to the keychain instead.
    Only /etc/codex/managed_config.toml, a managed preference or a requirement is over the
    -c. Where one of them chooses a store other than the file, Pitboard refuses the sign-in
    before it starts, as the first entry under Fixed says; where one chooses the file, the
    sign-in goes ahead.
  • pitboard doctor's Codex login store line, and the app's This Mac pane, say which
    setting chose Codex's store, such as file (Codex's default) or
    keyring (pinned to `keyring` by /etc/codex/requirements.toml). Where the store is the
    file, doctor adds that Pitboard does not read a project's own .codex/config.toml. It
    said file, or, whichever file chose another store,
    keyring (cli_auth_credentials_store in config.toml). pitboard doctor --json gives
    unknown as Codex's backend where nobody can tell, as the second entry under Fixed
    says.
  • In pitboard-core, doctor::CodexFacts has two more fields, backend_setting and
    backend_remedy. This changes the crate's public API.
  • In pitboard-core, holder::Remedy::ReopenApp names its app by app, a
    holder::AppId, in place of bundle_id. AppId::MacBundle holds a Mac app's bundle id,
    and AppId::as_str gives the id as its system writes it. holder::AppId,
    service::Warning::SessionsUnknown and service::Warning::SessionsUnknownAfterSignIn are
    new. These change the crate's public API.
  • Pitboard reads ALL_PROXY, HTTPS_PROXY, HTTP_PROXY and NO_PROXY, and their
    lower-case spellings, from the environment it was started with, by the rules ureq 3.4.2
    reads them with, and gives ureq the proxy. ureq read them itself, from the process, so
    each request goes through the same HTTP or HTTPS proxy as before. In pitboard-core, a
    context made with Context::new names no proxy, where its requests went through the one
    the process's environment named; a context read from an environment, as the command
    line's and the app's are, goes through the one that environment names. doctor::Facts
    has the fields network and in_the_app, doctor::ScheduleFact has the fields
    network and network_differs, and doctor::NetworkFact and doctor::ProxyFact are
    new. These change the crate's public API.
  • In pitboard-core, the autoswitch module, service::Pitboard::auto_switch,
    words::share_of_limit and words::not_switching are new, and error::Error has the
    variant SwitchOvertaken. These change the crate's public API.

Fixed

  • When the account in use runs out of a limit, the app's notification and window offer to
    switch only to an account with room in every limit it reports, counting a limit whose
    reset time has passed as reset. They offered the account with the most room in the limit
    that ran out, even one that had run out of another limit. An account with five-hour room
    and its weekly limit spent stopped at once when switched to.

  • Where Pitboard cannot read the list of running processes, a Codex switch warns, with the
    code sessions_unknown, that Pitboard could not tell whether Codex sessions started
    before it are still running, and says not to sign out in one: that revokes the login
    Pitboard has just parked. It said nothing, as when nothing runs Codex. In the app, the
    switch's notice says it.

  • Where Pitboard cannot read the list of running processes, a sign-in that puts a new login
    of a Codex account in use warns with sessions_unknown too, and says to quit and start
    again any Codex session still on the old login. Otherwise one of them can put the old
    login back when it refreshes. It said nothing. In the app, the notice that the account
    has a new login says it, and says it once where the last switch already could not tell.

  • On macOS and Linux, Pitboard reads which store Codex keeps its login in from every layer
    Codex 0.160.0 reads outside a project, in Codex's order: its default, the file store;
    /etc/codex/config.toml; config.toml in Codex's home; /etc/codex/managed_config.toml;
    and on macOS the managed preference config_toml_base64 of com.openai.codex, which a
    configuration profile forces. /etc/codex/requirements.toml, and on macOS the managed
    preference requirements_toml_base64, pin the store over all of them. Pitboard read only
    config.toml in Codex's home, so on a machine where another of them put Codex's login in
    the keychain or in memory, it looked for the login in an auth.json Codex does not keep:
    it said nothing was signed in to Codex, and a sign-in it ran left its login where Pitboard
    could not read it back. A Codex switch, enrolment or sign-in on such a machine is refused
    with live_store_unsupported, as one already was where Codex's own config.toml named
    such a store, and the message names the setting and what would choose the file store: the line
    cli_auth_credentials_store = "file" in your own config.toml, or, where a requirement,
    managed_config.toml or a managed preference chose it, that only an administrator can
    change it.

  • Where one of those layers is there and Codex cannot read it, because it cannot be read,
    is not TOML or names a store Codex does not have, Pitboard refuses with
    live_store_unsupported and says which, and pitboard doctor says it cannot tell where
    the login is: Codex 0.160.0 does not start then. Pitboard took such a store to be the
    file. cli_auth_credentials_store = "nonsense" in your own config.toml was read as the
    file store, and is refused the same way.

  • Where one of those layers has a profile = "<name>" line, Pitboard reads it as choosing
    the profile that a [profiles.<name>] table in any of them defines, as an older Codex,
    such as 0.99.0, does. A profile's table does not change the store, since neither Codex
    0.99.0 nor 0.160.0 reads cli_auth_credentials_store there, so the store is still the
    one the layers choose. A line that names a profile no table defines is refused with
    live_store_unsupported, as 0.99.0 refuses it; Pitboard ignored the line. Codex 0.160.0
    does not start with such a line at all, whatever it names: it chooses a profile only
    with --profile.

  • On macOS and Linux, Pitboard no longer refuses a Codex account where your own
    config.toml sets cli_auth_credentials_store to keyring, auto or ephemeral and
    /etc/codex/managed_config.toml, /etc/codex/requirements.toml or, on macOS, a managed
    preference sets it to file. Codex 0.160.0 takes their setting over yours and keeps its
    login in auth.json, so Pitboard parks and switches it there. Pitboard read only your
    own config.toml, and refused such an account with live_store_unsupported.

  • An account's window no longer opens again on a sign-in page of its site, such as
    chatgpt.com's /api/auth, which would repeat a sign-in that has ended: it opens on the
    page it showed before, or at the site's home. A window closed, or Pitboard quit, while it
    was on such a page opened there again.

  • After Claude Code refuses a code you paste, the sign-in sheet says Claude Code didn’t
    take that code. Copy the whole code your browser shows, and paste it again.
    and offers
    the code field again, and the same sign-in takes the next code. The sheet asked for a code
    once, so a refused one left a sign-in that only Cancel could end.

  • In the app, Sign In pressed right after Cancel no longer says another
    pitboard enroll --sign-in is already waiting: the new sign-in starts once the one
    cancelled has stopped. 0.7.0 stopped the cancelled sign-in in the background and started
    the new one at once, which could find the old one still holding the lock that allows one
    sign-in at a time.

  • After you add, name, rename or forget an account, give up on an interrupted switch or
    press Renew Now, the app shows what it reads of your accounts right after. When its
    look for changes made in a terminal found the account index just as the app had written
    it, the app took its own change for one made elsewhere and threw that read away: in an
    open window, an account just added showed no numbers, and nothing said when the accounts
    were read, until you opened the menu or went back to Accounts, either of which reads
    at once, or the read the app makes every five minutes came. 0.7.0 did the same. A look
    that found a rename before the rename had finished also put away what that account's
    last switch said, and could notify you again that the account had run out.

  • A sign-in that fails after its sheet has closed is said in the window. It was said
    nowhere.

  • A name the Name or Rename sheet could not save, once the sheet had closed or
    another had taken its place, is said in the window. It was said nowhere.

  • Quitting Pitboard stops a sign-in under way, and the tool's own sign-in with it, before the
    app goes. Pitboard left a claude or codex that was signing in running after it quit.

  • Two warnings with the same words are two notices in the window. They shared one id, which
    a SwiftUI list does not allow, so what the window drew of them was not defined.

  • This Mac lists the check of each account's parked login as a row of its own. Every
    Claude Code account's check shared one id, and so did every Codex account's, which a
    SwiftUI list does not allow, so what the pane drew of them was not defined.

  • This Mac says it is checking until the last check asked for is in. With two under
    way, after you left This Mac and came back while a check ran, it showed Checked
    at
    once the first was in, beside checks still to come.

  • After an interrupted switch Pitboard cannot finish, the app shows An interrupted switch
    is waiting
    , with its Give Up button, each time it reads your accounts, and
    pitboard status warns with recovery_undetermined in the words a change stops with.
    Neither said so before. The app looks for it in what a read says, and no read said it, so
    the notice never appeared and a switch failed with nothing offered but OK. Reading
    finishes and changes nothing. pitboard status --offline and pitboard doctor give the
    same message unless only Anthropic can tell whose login Claude Code is using. The app
    puts the notice away once the switch is given up on or finished in a terminal.

  • pitboard abandon, and the app's Give Up, give up on an interrupted Codex switch
    while CLAUDE_CODE_CUSTOM_OAUTH_URL is set, as a change to a Codex account goes ahead
    with it set. Both refused, so a Codex switch nothing could finish could not be given up
    on either.

  • pitboard doctor, and the app's checks, no longer warn that a parked login "could not be
    read this time" after a switch, a sign-in that enrolled an account, or a renewal of a
    parked login. Pitboard writes a park's name down before it writes the login, and the name
    stayed on that list until the next change, so doctor counted a login it had already
    recorded.

  • pitboard doctor's advice about a login others on the machine can read is one
    sentence again, without a run of spaces in the middle of it.

  • The note under the sign-in sheet's Code field says what Claude Code does: it takes
    the code shown after you sign in, whether or not your browser came back to it. The note
    said Claude Code asked for the code only when your browser could not reach it.

  • The sign-in sheet links the right address when Claude Code prints it as a terminal
    hyperlink. Claude Code does that when the app runs with FORCE_HYPERLINK set, or with
    the variables a terminal such as iTerm2 sets. The link's address picked up a stray
    control character, and sometimes a second copy of the address.

  • With HOME unset, the command line used the folder it was run from as your home: it kept
    its files in a .pitboard there, and looked for Claude Code's and Codex's beside it. It
    now uses your account's home directory, as the app does.

  • With HOME or PITBOARD_HOME empty, or with it, CLAUDE_CONFIG_DIR,
    CLAUDE_SECURESTORAGE_CONFIG_DIR or CODEX_HOME set to a path that does not start at
    the root, such as pitboard, Pitboard refuses every command but completions,
    manpage and doctor with the error code home_not_absolute, which names the variable:
    HOME is empty, so the folder it names would depend on where each program runs. Set it to a full path, or unset it; Pitboard reads and changes nothing until then. Pitboard
    took such a path to be under the folder it was run from: an empty HOME or
    PITBOARD_HOME kept its files there, and an empty HOME had
    pitboard schedule install write the schedule's file there. Claude Code and Codex take a
    relative one from the folder each of them runs in, so the login Pitboard read and
    switched was the one under the folder Pitboard ran in. pitboard doctor fails a homes
    check instead, and checks nothing else; the app's This Mac pane shows the same. The
    app reads nothing under such a home either. It shows the error where it shows your
    accounts, finds no tool and no pitboard, and leaves its own files there unread and
    unwritten.

  • A run of the daily renewal schedule renews the parked logins in ~/.pitboard whatever
    PITBOARD_HOME says, empty or relative included, which other commands refuse as above.
    The schedule renews ~/.pitboard alone, and its job is written without
    PITBOARD_HOME, but a PITBOARD_HOME set with launchctl setenv on macOS, or in
    systemd's user environment on Linux, through systemctl --user set-environment or
    environment.d, reached the job. The job then renewed that directory's parked logins
    and left those in ~/.pitboard to run out. On macOS Pitboard tells a run of the
    schedule by the job's name, which launchd gives it in XPC_SERVICE_NAME. On Linux it
    does not rely on what systemd passes a job, which systemd itself calls best effort, so
    the timer's service runs pitboard renew --scheduled. A timer installed by an earlier
    Pitboard runs pitboard renew, which renews the directory PITBOARD_HOME names as
    before, until you run pitboard schedule install again, which rewrites it. If you go
    back to an earlier Pitboard on Linux, run its pitboard schedule install as well: an
    earlier Pitboard does not take --scheduled, so until then the timer fails every day.

  • With CLAUDE_CONFIG_DIR set but empty, Pitboard refuses as above, with
    home_not_absolute: CLAUDE_CONFIG_DIR is empty, so the folder it names would depend on where each program runs. Claude Code reads an empty one as unset for ~/.claude.json
    and for which keychain item holds its login, but as the folder it runs in for its
    config directory, so its settings, the lock around its login and, on Linux, the login in
    .credentials.json are in whatever folder each session started in. Pitboard read an
    empty one as unset, so it took that lock, and on Linux switched that login, in
    ~/.claude, where no Claude Code started with it looks. On macOS such a switch did move
    the login, which is in the default keychain item, and took only the lock in the wrong
    folder; it is refused all the same. To keep switching, unset CLAUDE_CONFIG_DIR.

  • The command line takes a claude or codex to be the tool's program only when it is a
    regular file you may run, as the system judges it when it starts one, and otherwise looks
    further on PATH. It took a file only others may run, and then failed to start it.

  • The app reads the environment it was started with as the command line reads its own, by
    the same code. It read only the variables that move where things are kept, and ignored
    these, which it now reads as the command line does:

    • CLAUDE_CODE_CUSTOM_OAUTH_URL: while it is set, the app refuses changes to Claude Code
      accounts.
    • The variables that make Claude Code sign in another way, such as ANTHROPIC_API_KEY:
      the app's checks and changes warn about one that is set.
    • CLAUDE_CODE_HOVER_REST, which switches on Claude Code's successor credential
      backend: the app's checks say it is on, and warn when the login is in the plaintext
      file, where what Pitboard reads may not be what Claude Code reads.
    • PITBOARD_API_BASE, a hook for Pitboard's tests: the app sends the requests meant for
      Anthropic and OpenAI to the loopback address it names, and ignores any other address.

    An app opened from Finder has these only when they are set with launchctl setenv. Both
    front ends already read the first two from Claude Code's settings files.

  • The app takes a claude or codex it finds to be the tool's program only when it is a
    regular file you may run, as the command line does, and otherwise looks further. It took
    a directory with that name, offered the tool, and failed to start its sign-in.

  • The Open Link window and the Share sheet name claude.ai and chatgpt.com in English, as
    the rest of what they say is. On a Mac set to another language they joined the two names
    in that language, as in There is no claude.aiまたはchatgpt.com link in what was shared.

Security

  • Pitboard refuses more folders that sync to other machines as its directory, with
    state_on_synced_drive: any folder inside ~/Library/CloudStorage, where macOS keeps
    the folders of cloud storage apps such as Box and Google Drive, or inside
    ~/Library/Mobile Documents, where iCloud Drive keeps each app's folder; and any folder
    named Dropbox, Dropbox (<team>), Google Drive, GoogleDrive-<account>,
    OneDrive, OneDrive - <organisation>, OneDrive-<kind>, com~apple~CloudDocs or
    Sync. A name counts in any case, so ~/dropbox is refused as ~/Dropbox was, and
    wherever it is in the path, your home's own folders included: with a home of
    /home/sync or /home/dropbox, ~/.pitboard is refused though nothing syncs it, and
    PITBOARD_HOME can name a folder outside it. On Linux the parked logins are files in
    that directory, which such a folder would copy to every machine it syncs to. Every
    folder Pitboard refused before is still refused, SyncAdmin and OneDriveTools among
    them, though they sync nothing.
  • Pitboard refuses a site's sign-in link from outside however its path is written. It took
    claude.ai/magic-link/%FF and chatgpt.com/api/auth/%C3, whose paths hold a byte that is
    not text, and claude.ai/magic-link/%CC%81, whose path has a combining mark after a
    slash, for pages of the site, and offered to open them in an account's window. Somebody
    else's sign-in link opened there would sign the window in as them, under your label. A
    link that names a port or a user is refused whatever they hold: one whose port is too
    long for a number, or whose user name is not text, was opened without them.
  • Pitboard refuses a link whose path has a . or .. segment whatever else the path
    holds. It opened claude.ai/x/../%FF, whose path holds a byte that is not text, though
    WebKit drops the dot segments and loads another page than the link reads.
  • A pitboard:// link that names a user or a port, or has a path, is one Pitboard cannot
    read whatever they hold. One whose user, port or path is not text, such as
    pitboard://open/%FF?url=claude.ai, was read as if it had none.