Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/release-smoke.yml
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ jobs:
echo "- Release: \`$RELEASE_TAG\`"
echo "- Result: \`$SMOKE_OUTCOME\`"
echo "- Runner architecture: \`$(uname -m)\`"
echo "- Proved: published bootstrap, manifest, checksum, candidate version and self-test; dry-run collision refusal; exact 2.2.1 reset fingerprint, --reset gate, filesystem reset, and teardown; title-core readiness without the updater; a two-page sorted current-title plan with zero production title writes, installed decoder presence, and a simulated raw JSON-string mounted setter result plus one-attempt failure; complete multi-page onboarding preview with deduplication and failure-before-writes; uncapped complete-snapshot subject preparation with zero per-target production RPCs, followed by simulated serial mounted read-before-set revalidation using raw JSON-string tool results, drift and wrong-task-ID skipping, one-attempt writes, exact accounting, and no retry; no hooks.json mutation or SQLite dependency; real-launchd automatic and direct current updates with restart reporting; an update/uninstall overlap; and complete uninstall JSON with binary-last teardown and unrelated-content preservation."
echo "- Proved: published bootstrap, manifest, checksum, candidate version and Codex compatibility self-test; dry-run collision refusal; exact 2.2.1 reset fingerprint, --reset gate, filesystem reset, and teardown; title-core readiness without the updater; an ordinary stateless title-policy helper with zero App Server or title-state access; installed mounted-response decoder presence plus simulated raw JSON-string read/write results and one-attempt failure; complete multi-page onboarding preview with deduplication and failure-before-writes; uncapped complete-snapshot action preparation with zero per-target production RPCs, followed by simulated serial mounted read-before-set revalidation, drift and wrong-task-ID skipping, one-attempt writes, exact accounting, and no retry; fixed-path Codex resolution, no hooks.json mutation, and no SQLite dependency; real-launchd automatic and direct current updates with restart reporting; an update/uninstall overlap; and complete uninstall JSON with binary-last teardown and unrelated-content preservation."
echo "- Not proved: real Codex auth, native automation deletion or exact-task unpin, rendered Desktop repaint, clean-restart persistence, native timeout behavior, a newer-version partial update, or architectures other than this runner. Those seams require the recorded local Desktop and fault-injection canaries."
echo "- Deployment timing: a Pages/CDN lag can make the live bootstrap older than the release commit; that red result still requires operator investigation."
echo "- A red result marks the published release for operator action; this workflow does not delete, demote, or retry a release."
Expand Down
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,19 @@

## Unreleased

## v3.0.1 - 2026-08-08

### Fixed

- Made ordinary title updates work under Codex's default workspace permissions by moving the current-title read to the mounted app and removing ordinary App Server and filesystem access.
- Stopped resolving Codex from repository-controlled `PATH`; onboarding now uses a fixed Desktop executable and install, self-test, and status require Codex Desktop 0.146.0 or newer.
- Continued through the fixed Desktop locations when an older user-local Codex bundle appears before a compatible system installation.

### Changed

- Removed the per-task subject database. ThreadBear now uses six explicit visible icon prefixes as its small ownership boundary and keeps every other safe title byte intact.
- Made historical onboarding explain and request its one catalog-read permission while keeping the preview, separate consent, immediate native reread, one-write limit, and durable friendly recap; approval-disabled tasks now stop with a plain no-change next step.

## v3.0.0 - 2026-08-08

### Changed
Expand Down
40 changes: 26 additions & 14 deletions INSTALL.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,15 @@ Open with this orientation:

> ## Hi. Let's install ThreadBear.
>
> ThreadBear keeps each Codex task's exact subject and adds one useful status icon at the end of a turn. It prepares one safe title, then Codex applies it once.
> ThreadBear adds one useful status icon while keeping the rest of each safe task title intact. Codex reads and applies the title itself.
>
> I'll check this Mac, show you exactly what will change, and ask before installing anything. Afterward, Codex needs one restart. Then you can say **ThreadBear onboard** in any task to update every safe existing local task—there is no 50-task cap or persistent ThreadBear task.

Codex collapses commentary after a turn finishes, so the final answer that asks for consent must repeat the orientation, readiness result, complete recommendation, and question. If a check fails, report it plainly and do not ask for install consent.

For every lifecycle action, write the lasting summary after all tool calls. End the final response with **ThreadBear recap 🐻** and include the result, counts or uncertainty, what stayed untouched, and the next action. Never leave that recap only in commentary, progress notices, notifications, or raw tool output; those can disappear when Codex summarizes the turn.

Keep that recap user-facing: do not copy raw fields or list internal files and components. Translate them into helper, title memory, instructions, and automatic updates. Group safe skips as “left unchanged” unless the user needs to act. An unconfirmed title write means “I couldn't confirm whether this title changed,” never “it stayed unchanged.”
Keep that recap user-facing: do not copy raw fields or list internal files. Translate them into helper, instructions, skill, and automatic updates. Group safe skips as “left unchanged” unless the user needs to act. An unconfirmed title write means “I couldn't confirm whether this title changed,” never “it stayed unchanged.”

## 1. Check without changing anything

Expand All @@ -33,8 +33,19 @@ Run:
```sh
sw_vers -productVersion
uname -m
command -v codex
codex --version
codex_found=
for codex_path in \
"$HOME/Applications/ChatGPT.app/Contents/Resources/codex" \
"$HOME/Applications/Codex.app/Contents/Resources/codex" \
/Applications/ChatGPT.app/Contents/Resources/codex \
/Applications/Codex.app/Contents/Resources/codex \
"$HOME/.local/bin/codex"; do
if [ -x "$codex_path" ]; then
printf '%s: ' "$codex_path"
if "$codex_path" --version; then codex_found=1; fi
fi
done
test -n "$codex_found"
curl --version
curl -fsSLI https://threadbear.sh/install.sh >/dev/null
curl -fsSLI https://github.com/ericlitman/threadbear/releases/latest >/dev/null
Expand All @@ -43,7 +54,7 @@ if [ -x "$HOME/.local/bin/threadbear" ]; then
fi
```

ThreadBear requires macOS 12 or newer, Apple silicon or Intel, Codex Desktop, and HTTPS access to the official guide and GitHub Releases. It needs no `sudo` or Full Disk Access. It never opens Codex SQLite or edits Desktop storage.
ThreadBear requires macOS 12 or newer, Apple silicon or Intel, Codex Desktop 0.146.0 or newer, and HTTPS access to the official guide and GitHub Releases. The check prints every fixed Codex Desktop command it finds; ThreadBear uses the first one that actually reports a compatible version. It needs no `sudo` or Full Disk Access. Ordinary title updates work with Codex's default workspace permissions. Historical onboarding asks once for permission to read the complete local task catalog. ThreadBear never opens Codex SQLite.

For an official release, run the verified bootstrap preview:

Expand All @@ -57,7 +68,7 @@ For an already-built local candidate, run:
/path/to/threadbear install --dry-run --json
```

The preview must pass candidate self-test and be limited to the binary, private subject records, managed AGENTS block, installed skill, and one daily update-only LaunchAgent. It must preserve unrelated AGENTS content, skills, settings, files, and LaunchAgents.
The preview must pass candidate self-test and be limited to the binary, private lifecycle/update state, managed AGENTS block, installed skill, and one daily update-only LaunchAgent. It must preserve unrelated AGENTS content, skills, settings, files, and LaunchAgents.

If the preview returns `legacy_reset_required:true`, require `legacy_main_task_id` plus `legacy_automation_id`, `legacy_automation_name`, `legacy_automation_kind`, and `legacy_automation_target_thread_id`. The target must equal the main-task ID. This is a clean 2.2.1 reset, not an in-place migration. Through supported native controls, verify the exact automation and former persistent task before proposing mutation. A collision, missing target, or uncertain owner stops the reset. The reset also removes only exact obsolete ThreadBear Pre/Post title-interception entries and preserves every foreign entry and its order. Import no old state and reinterpret no legacy title.

Expand All @@ -69,7 +80,7 @@ Only after the checks and dry run succeed, present this complete card in the sam
>
> - ThreadBear adds one helpful status icon without rewriting your task's subject or emoji.
> - Existing tasks stay unchanged until you preview onboarding and approve it separately.
> - A small local helper, Codex instructions, and private title memory are added.
> - A small local helper, Codex instructions, and a ThreadBear skill are added.
> - Once a day, ThreadBear checks for and installs only verified official releases. Updates never read tasks or change titles.
> - Unclear or unsafe titles are left alone, and there is no persistent ThreadBear task.
> - Other Codex settings and files stay untouched.
Expand Down Expand Up @@ -108,7 +119,7 @@ Add `--no-onboard` only when the user opted out. Add `--reset` only after the ex
~/.local/bin/threadbear status --json
```

Core `ready` is healthy when the installed binary, private subject state, managed guidance, and skill match the candidate. Report the daily updater separately; missing automatic updates do not make title handling globally unready. Core readiness does not depend on historical title counts.
Core `ready` is healthy when the installed binary, private lifecycle state, compatible Codex Desktop, managed guidance, and skill match the candidate. Report the daily updater separately; missing automatic updates do not make title handling globally unready. Core readiness does not depend on historical title counts.

No controller, worker, migration phase, persistent task, or hidden onboarding job should exist after installation. If installation fails after mutation starts, report `partial:true`, the failed stage, whether restart is required, and the one safe rerun action. `planned_changes` is a plan, not a claim that every item ran.

Expand All @@ -132,7 +143,8 @@ After a successful install say:

When that request arrives, read the installed skill and follow this protocol:

1. Run `~/.local/bin/threadbear status --json`, then `~/.local/bin/threadbear onboard --dry-run --json`.
1. Run `~/.local/bin/threadbear status --json`. Explain that Codex will ask once so ThreadBear can read the complete task list and that the preview changes nothing. Then run `~/.local/bin/threadbear onboard --dry-run --json` with `sandbox_permissions:"require_escalated"` and that plain-language justification.
If the host says approval requests are disabled, stop without running around that policy. End with **ThreadBear recap 🐻**: “No tasks changed. This task cannot ask for onboarding permission. Next: use a task where Codex can ask, then say **ThreadBear onboard**.” Do not change the user's permission settings.
2. Require `ready:true`, `plan_complete:true`, and `read_only:true`. The preview enumerates and deduplicates the entire unarchived App Server catalog before any preparation or title write. If enumeration fails, make zero changes.
3. Explain `total`, `safe`, and `needs_update` with this card:

Expand All @@ -154,7 +166,7 @@ The active caller, null or blank names, unsafe or overlong subjects, and ambiguo
~/.local/bin/threadbear onboard --noninteractive --confirm --json
```

The confirmed command takes a fresh complete catalog snapshot, stores each safe subject, and returns one `prepared` action containing the snapshot title and desired title. It makes no Codex title writes. If preparation yields, the same JavaScript cell resumes that exact process through `tools.write_stdin`; it never starts another command. For every prepared item, call `tools.codex_app__read_thread({threadId:item.task_id,includeOutputs:false,turnLimit:1,maxOutputCharsPerItem:1})` immediately before a possible write. A missing, unreadable, wrong-ID, or changed-title response is skipped. Only an exact task ID and snapshot title may receive one serial `tools.codex_app__set_thread_title({threadId:item.task_id,title:item.desired_title})` call. Lightweight progress appears during preparation and every 25 outcomes. There is no item cap, wave, worker task, or resume state. Count only an exact returned task ID/title as `updated`; a throw, malformed response, or mismatch is `unconfirmed` and is never retried.
The confirmed command asks for the same one-time permission, takes a fresh complete catalog snapshot, and returns one `prepared` action containing the snapshot title and desired title. It stores no titles and makes no Codex title writes. If preparation yields, the same JavaScript cell resumes that exact process through `tools.write_stdin`; it never starts another command. For every prepared item, call `tools.codex_app__read_thread({threadId:item.task_id,includeOutputs:false,turnLimit:1,maxOutputCharsPerItem:1})` immediately before a possible write. A missing, unreadable, wrong-ID, or changed-title response is skipped. Only an exact task ID and snapshot title may receive one serial `tools.codex_app__set_thread_title({threadId:item.task_id,title:item.desired_title})` call. Lightweight progress appears during preparation and every 25 outcomes. There is no item cap, wave, worker task, or resume state. Count only an exact returned task ID/title as `updated`; a throw, malformed response, or mismatch is `unconfirmed` and is never retried.

Codex can keep an already-mounted historical row cached after an exact native write. Do not retry or add refresh machinery. The persisted title appears when its project is reopened or Codex restarts; say this plainly in the onboarding summary.

Expand All @@ -178,7 +190,7 @@ End with:
~/.local/bin/threadbear update --json
```

The managed guidance runs one injection-safe terminal JavaScript cell immediately before an ordinary final response. Replace only the status enum; the parsed `plan.desired_title` variable passes directly to the native tool and is never re-embedded by the model. The cell runs `title --status <complete|next_steps|needs_input|blocked|automation> --json` exactly once. The binary reads the exact current title through the App Server, preserves the safe subject, and returns a plan without writing a title. When `write_required` is true, the cell calls `tools.codex_app__set_thread_title({title:plan.desired_title})` exactly once with `threadId` omitted, and accepts only the exact returned planned task ID and title. If the outer cell yields after 30 seconds, wait only for that same cell; the yield does not cancel a slow native call, which may delay the final response. Never retry, start another cell, poll the title, or reconcile.
The managed guidance runs one injection-safe terminal JavaScript cell immediately before an ordinary final response. Replace only the status enum. The cell runs `title --status <complete|next_steps|needs_input|blocked|automation> --json` exactly once; the stateless helper returns the calling task ID and fixed title policy without starting App Server or writing state. The mounted app then reads that exact task, derives one safe desired title, and—only when it differs—calls `tools.codex_app__set_thread_title({title:desired})` once with `threadId` omitted. Exact returned task ID/title is required. If the outer cell yields after 30 seconds, wait only for that same cell; the yield does not cancel a slow native call. Never retry, start another cell, poll the title, or reconcile.

`update` verifies the official manifest, release URLs, architecture, checksum, embedded version, and candidate self-test before replacement. Network or verification failure leaves the old installation untouched. A later managed-surface write can truthfully leave a rerunnable partial; the binary is written last. Every successful update reports `restart_required`. The daily LaunchAgent runs only this command and never reads tasks or changes titles.

Expand Down Expand Up @@ -212,7 +224,7 @@ End the consent turn with:

> ## Here's what will happen
>
> - I'll remove ThreadBear's local helper, private title memory, Codex instructions, skill, and automatic updates.
> - I'll remove ThreadBear's local helper, Codex instructions, skill, and automatic updates.
> - Your tasks, other Codex settings, and unrelated files stay untouched.
> - Existing title icons may remain until those tasks are renamed.
> - After removal, you'll restart Codex once.
Expand Down Expand Up @@ -241,8 +253,8 @@ Before release, run unit and integration tests, race tests, both Darwin builds,

Release acceptance additionally requires one reviewed candidate live-tested end to end in Codex Desktop:

- the terminal planner changes no Codex title, preserves the exact subject, and prepares only the status icon change;
- the mounted app-native setter receives no explicit current-task ID and returns the exact planned task ID and title;
- the stateless terminal helper works under Codex's default workspace permissions and starts no App Server or title-state write;
- the mounted app-native reader supplies the exact current title, and the setter receives no explicit current-task ID and returns the exact task ID/title;
- the rendered sidebar shows the expected title before and after a clean restart;
- a full onboarding preview enumerates every local task, confirmed preparation writes no title, and the consented serial app-native pass accounts for every prepared target while skipping title drift before any write;
- failures and unconfirmed results are reported locally without retries or global failure state;
Expand Down
Loading
Loading