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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
### Added

- Restored conservative 14-day automatic archiving through one consented hourly Luna heartbeat, deterministic candidate selection, native archive controls, interruption-safe ownership, and ownership-only restore.
- Restored verified automatic updates through the same heartbeat, with bounded official release selection, checksum and candidate validation, same-version repair, and one version-change announcement.

## v2.1.7 - 2026-08-03

Expand Down
8 changes: 5 additions & 3 deletions INSTALL.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ Only after every check and the dry run succeeds, compose one terminal final answ
> - Deterministic classification first; Luna medium only for ambiguity.
> - A small Luna helper checks in hourly, then stays quiet when there is nothing to do.
> - Finished tasks can curl up in the archive after 14 quiet days—and come back whenever you need them.
> - ThreadBear keeps itself fresh from verified releases and tells you when it has a new coat.
>
> Install ThreadBear with this recommended setup?

Expand Down Expand Up @@ -108,7 +109,7 @@ The install result must show `installed:true`, the exact `main_task_id`, and `ph

Inventory and migration cover local Codex Desktop and CLI tasks that the native explicit-target title setter can address. They do not enumerate or rename older signed-in ChatGPT chat-history rows that may also appear in the Desktop sidebar. Disclose that boundary before migration and never describe zero local inventory rows as proof that every visible sidebar row changed.

Create or update one paused hourly heartbeat automation through the native automation control. Its exact ID is `threadbear-maintenance`, its name is “ThreadBear maintenance,” and its target is `MAIN_TASK_ID`. Its prompt is: “Follow the installed ThreadBear skill's Maintenance section. Reconcile archive work first, process eligible archives serially through native controls, and stay quiet when nothing changes.” Reuse only an existing automation whose ID, name, kind, and target all match; an ID collision with anything else stops installation without changing that automation. Do not create a cron job or a second maintenance schedule. A reinstall already at `migration_complete` may keep it active.
Create or update one paused hourly heartbeat automation through the native automation control. Its exact ID is `threadbear-maintenance`, its name is “ThreadBear maintenance,” and its target is `MAIN_TASK_ID`. Its prompt is: “Follow the installed ThreadBear skill's Maintenance section. Reconcile archive work first, process eligible archives serially through native controls, run the verified update check last, and stay quiet when nothing changes.” Reuse only an existing automation whose ID, name, kind, and target all match; an ID collision with anything else stops installation without changing that automation. Do not create a cron job or a second maintenance schedule. A reinstall already at `migration_complete` may keep it active.

If the native automation call fails, say that ThreadBear's local title helper is installed but its housekeeping helper is not, and stop before claiming completion. Do not ask the user to create or repair the automation manually.

Expand Down Expand Up @@ -150,7 +151,7 @@ On complete success, use this shape in natural prose:

> ## ThreadBear is installed
>
> Everything passed: ThreadBear VERSION is installed, its managed guidance, two hooks, and hourly Luna helper are healthy, this task is its persistent home, and the migration controller completed with zero remaining native-addressable local Codex task titles. Older signed-in ChatGPT chat-history rows were outside this migration and may remain unchanged in the sidebar.
> Everything passed: ThreadBear VERSION is installed, its managed guidance, two hooks, verified updates, and hourly Luna helper are healthy, this task is its persistent home, and the migration controller completed with zero remaining native-addressable local Codex task titles. Older signed-in ChatGPT chat-history rows were outside this migration and may remain unchanged in the sidebar.
>
> From here, you can ask “how are you?”, “what tasks do you see?”, or “uninstall ThreadBear.”

Expand All @@ -165,9 +166,10 @@ For later help, lead with a short capability card instead of a command dump. Ver
```sh
~/.local/bin/threadbear status --json
~/.local/bin/threadbear help
~/.local/bin/threadbear update --json
```

The installed binary's help is the authoritative public command list. Cross-check the exact `threadbear-maintenance` heartbeat with the native automation control before describing hourly housekeeping as healthy.
The installed binary's help is the authoritative public command list. Run `update --json` only for an explicit check-now request or from the owned maintenance heartbeat after archive work is reconciled. Cross-check the exact `threadbear-maintenance` heartbeat with the native automation control before describing hourly housekeeping as healthy.

## Uninstall

Expand Down
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ threadbear install
threadbear inventory
threadbear migration
threadbear maintenance
threadbear update
threadbear status
threadbear self-test
threadbear uninstall
Expand All @@ -35,10 +36,10 @@ threadbear version

Every command accepts `--json`. `inventory` is read-only and includes every native-addressable unarchived local Codex Desktop or CLI task, including projectless tasks, excluding the persisted main and controller tasks. Rollout-only internal records and older signed-in ChatGPT chat-history rows that Codex's native title setter cannot enumerate or rename are excluded. Those chat-history rows may remain unchanged in the Desktop sidebar even after local migration completes. `status` reports `ready:true` only after `migration_complete`; the installed binary's `help` output is authoritative.

From the persistent ThreadBear task, ask to “strip title icons” at any time. The control task serially removes all leading ThreadBear status marks through the same native setter and exact Pre/Post verification used by ordinary turns. The same task's hourly Luna helper can archive only deterministically eligible, ThreadBear-owned complete user tasks after 14 quiet days, and restore only archives recorded in its private ownership ledger. Guided uninstall always pauses that helper and completes title cleanup before removing ThreadBear's local state, hooks, and owned automation.
From the persistent ThreadBear task, ask to “strip title icons” or “check for updates now” at any time. The control task serially removes all leading ThreadBear status marks through the same native setter and exact Pre/Post verification used by ordinary turns. The same task's hourly Luna helper can archive only deterministically eligible, ThreadBear-owned complete user tasks after 14 quiet days, restore only archives recorded in its private ownership ledger, and run the deterministic verified update check last. Guided uninstall always pauses that helper and completes title cleanup before removing ThreadBear's local state, hooks, and owned automation.

## Boundaries

ThreadBear installs no daemon or LaunchAgent. One explicitly consented hourly Codex heartbeat runs maintenance from the persistent Luna-medium task and stays quiet on no-op runs. The CLI alone selects archive candidates, stages one operation, and reconciles ownership; Luna calls Codex's supported native archive control and never edits private UI storage or interprets prose to add targets. ThreadBear does not add token counts or update itself in the background yet, and it adds no model call or narration to ordinary turns. Installation uses one serial native-writing controller and adaptive waves of read-only Luna-medium workers only when genuinely ambiguous history cannot be classified deterministically; workers classify and never write titles.
ThreadBear installs no daemon or LaunchAgent. One explicitly consented hourly Codex heartbeat runs maintenance from the persistent Luna-medium task and stays quiet on no-op runs. The CLI alone selects archive candidates, stages one operation, reconciles ownership, and chooses the exact Darwin asset from the official release manifest. Luna calls supported native controls and communicates typed results; it never edits private UI storage, interprets prose to add targets, or chooses/downloads/checksums a release. Updates refuse while archive work is pending, verify repository URLs, SHA-256, embedded version, candidate self-test, candidate install, and installed status, and never downgrade. ThreadBear adds no token counts, model call, or narration to ordinary turns. Installation uses one serial native-writing controller and adaptive waves of read-only Luna-medium workers only when genuinely ambiguous history cannot be classified deterministically; workers classify and never write titles.

See [architecture](docs/architecture.md), [compatibility](docs/compatibility.md), and the [status footer convention](docs/status-convention.md).
1 change: 1 addition & 0 deletions assets/help.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ Commands:
migration Record the single installation migration controller phase
status Check the installed helper, hooks, and migration phase
self-test Validate a release candidate
update Install the latest verified release
uninstall Remove ThreadBear after control-task title cleanup
version Show the installed version

Expand Down
11 changes: 7 additions & 4 deletions assets/skill/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ Be warm, brief, and lightly bear-themed. Explain visible outcomes before command

## Help

For a help-shaped request, start with a short capability card: ThreadBear keeps Codex Desktop titles useful through two native title calls per ordinary turn, while its hooks deterministically preserve each task's subject. One hourly Luna helper can quietly tuck away owned, completed tasks after 14 inactive days. ThreadBear adds no extra model call or narration to ordinary turns.
For a help-shaped request, start with a short capability card: ThreadBear keeps Codex Desktop titles useful through two native title calls per ordinary turn, while its hooks deterministically preserve each task's subject. One hourly Luna helper can quietly tuck away owned, completed tasks after 14 inactive days and install verified ThreadBear releases. ThreadBear adds no extra model call or narration to ordinary turns.

Run `~/.local/bin/threadbear status --json` before saying ThreadBear is installed or healthy. Use `~/.local/bin/threadbear help` as the authoritative public command reference.

Expand All @@ -20,6 +20,7 @@ Show a command before running it. Ask for explicit consent before any lifecycle
| "How are you?" | `~/.local/bin/threadbear status --json` |
| "What tasks do you see?" | `~/.local/bin/threadbear inventory --json` |
| "Run maintenance now" | Follow **Maintenance** below. |
| "Check for updates now" | Run the update-last step in **Maintenance**. |
| "Bring back archived task TASK_ID" | Follow the restore path in **Maintenance**. |
| "Strip title icons" | Follow **Title cleanup** below from the persisted ThreadBear task. |
| "Install ThreadBear" | Follow **Install** below. |
Expand All @@ -29,10 +30,10 @@ Show a command before running it. Ask for explicit consent before any lifecycle

1. Read the current install guide and the candidate's help output. Check macOS, architecture, Codex, HTTPS access, and candidate self-test without changing the machine. Resolve the exact current task ID with supported Codex task tooling.
2. Run the exact dry run with `--control-task-id CURRENT_TASK_ID`. Explain the complete effect: adopting that task as the persistent home, the local binary, one small private state file, one managed AGENTS block, this skill, two hook entries, and one owned hourly Luna heartbeat. Explain that migration covers native-addressable local Codex tasks; older signed-in ChatGPT chat-history rows are outside Codex's current task-title API and stay unchanged.
3. Show the recommended setup and include: “A small Luna helper checks in hourly, then stays quiet when there is nothing to do.” and “Finished tasks can curl up in the archive after 14 quiet days—and come back whenever you need them.” Ask once for consent. A clear yes to the unchanged complete recommendation is installation consent. Ask again only if the recommendation changed, the answer was ambiguous, or this is a reinstall with a different effect.
3. Show the recommended setup and include: “A small Luna helper checks in hourly, then stays quiet when there is nothing to do.”, “Finished tasks can curl up in the archive after 14 quiet days—and come back whenever you need them.”, and “ThreadBear keeps itself fresh from verified releases and tells you when it has a new coat.” Ask once for consent. A clear yes to the unchanged complete recommendation is installation consent. Ask again only if the recommendation changed, the answer was ambiguous, or this is a reinstall with a different effect.
4. Run the confirmed install with the same ID and verify `version`, `self-test`, and `inventory`. A fresh result is `migration_pending`: no controller has started and nothing is running yet. On reinstall, omit the flag only when `status --json` already reports the persisted main task; never replace it with the launching task.
5. Before any migration, use `codex_app__set_thread_title` to set the initiating task to exactly `🧵🐻 ThreadBear 🐻🧵`, use `codex_app__set_thread_pinned` to pin it, and keep that task selected.
6. Create or update one paused hourly heartbeat through the native automation control. Its ID is `threadbear-maintenance`, name is “ThreadBear maintenance,” target is the persisted main task, and prompt is: “Follow the installed ThreadBear skill's Maintenance section. Reconcile archive work first, process eligible archives serially through native controls, and stay quiet when nothing changes.” Reuse only an exact ID/name/kind/target match. A collision stops installation without changing the other automation; never create a cron job or duplicate. If the call fails, report the partial install and stop instead of asking the user to repair it manually. On a reinstall that already reports `migration_complete`, it may be active immediately.
6. Create or update one paused hourly heartbeat through the native automation control. Its ID is `threadbear-maintenance`, name is “ThreadBear maintenance,” target is the persisted main task, and prompt is: “Follow the installed ThreadBear skill's Maintenance section. Reconcile archive work first, process eligible archives serially through native controls, run the verified update check last, and stay quiet when nothing changes.” Reuse only an exact ID/name/kind/target match. A collision stops installation without changing the other automation; never create a cron job or duplicate. If the call fails, report the partial install and stop instead of asking the user to repair it manually. On a reinstall that already reports `migration_complete`, it may be active immediately.
7. For an ordinary guided installation, never use visual inspection, computer control, screenshots, or Codex `/hooks`, and never ask the user to do so. The candidate self-test, installed `self-test --json`, and read-only ThreadBear inventory are the installation gate. Visual hook verification belongs only to **Debug canaries** when the install result explicitly contains `debug_canaries:true`.
8. Create exactly one background migration controller without opening, selecting, or navigating to it, then immediately record it with `migration --phase migration_running`. On a compatible machine with the candidate already downloaded, dispatch it within 60 seconds of consent; do not insert UI inspection, App Server schema generation, native-catalog comparison, or tool-surface discovery before dispatch. If creation fails, leave `migration_pending` unchanged and report that nothing is running plus the exact retry action.
9. Give the controller the **Migration controller** protocol below. Supervise it from the persistent task with compact task waits, reporting only each 25-title milestone or phase change. Do not end the installation turn while durable status is `migration_pending` or `migration_running`.
Expand All @@ -52,10 +53,12 @@ The owned `threadbear-maintenance` heartbeat runs this section from the persiste
2. For each candidate in stable order, run `~/.local/bin/threadbear maintenance --archive TASK_ID --json` immediately before mutation and require that exact ID with `action:"archive"` and `pending:true`. Call native `codex_app__set_thread_archived` once with that task ID and `archived:true`, without opening or selecting the task. Rerun the exact maintenance command and require `reconciled:true` before continuing.
3. If the native result is unknown or the reconciliation still says pending, do not repeat the mutation. Read the task with native task controls, rerun maintenance, and stop with the pending transaction intact unless the CLI authoritatively reconciles it. If the native call returned a definite failure and a native read confirms the original archive state, run `maintenance --cancel TASK_ID --json` to clear that known-unapplied operation. Never cancel an unknown or in-flight result. Title, footer, activity, kind, identity, or archive drift makes the CLI fail closed. If the CLI reports that an applied archive drifted, it remains pending and unowned: do not adopt it. Report the task for manual recovery; only after the user restores it and a native read confirms it is unarchived may the exact guarded cancel clear the operation.
4. Process one native archive operation at a time. Finish with a no-target maintenance pass and require no pending operation. Report only archived task subjects/counts or an error; do not narrate healthy no-op runs.
5. Only after the closing archive pass proves there is no pending native operation, run `~/.local/bin/threadbear update --json` last. The command alone fetches the exact official manifest, chooses the Darwin architecture, verifies HTTPS repository URLs and SHA-256, checks the embedded version and candidate self-test, and invokes the candidate's existing install path. Luna never browses for, chooses, downloads, checksums, or approves an asset.
6. Stay silent for `current:true`. For `updated:true`, report the old and new versions once in the persistent ThreadBear task. For `repaired:true`, say that the same verified version repaired managed files. On failure, report its typed `stage` and error once; never bypass verification, run a remote script, downgrade, or retry an unknown partial install blindly.

For a user-requested restore, verify the request from the persistent ThreadBear task, then run `maintenance --restore TASK_ID --json`. Continue only for `action:"restore"`, call the native archive control once with `archived:false`, and rerun the same command until `reconciled:true`. The CLI accepts only ThreadBear-owned archives and restarts that task's 14-day quiet clock. A user-archived task is never adopted. A task manually restored through Codex is detected on the next pass, removed from the ownership ledger, and receives the same fresh quiet clock.

To change the quiet window, pass the requested positive `--archive-after-days N` consistently to planning and staging and update the owned automation prompt. To disable archival, update the owned heartbeat prompt to omit this archive protocol; do not pause shared maintenance if it also owns other enabled work.
To change the quiet window, pass the requested positive `--archive-after-days N` consistently to planning and staging and update the owned automation prompt. To disable archival, update the owned heartbeat prompt to omit this archive protocol while preserving the update-last step. To pause updates, omit only the update-last step while preserving enabled archival. A check-now request runs the same deterministic update command after proving no pending archive work; there is no separate scheduler or release channel.

## Status and inventory

Expand Down
5 changes: 5 additions & 0 deletions cmd/threadbear/install.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,11 @@ func uninstall(ctx context.Context, confirmed bool) (any, error) {
if !confirmed {
return nil, errors.New("uninstall requires --noninteractive --confirm")
}
operationLock, err := newStore(stateDir()).blockingOperationLock()
if err != nil {
return nil, err
}
defer unlock(operationLock)
value, err := currentStateOrEmpty()
if err != nil {
return nil, err
Expand Down
Loading
Loading