diff --git a/CHANGELOG.md b/CHANGELOG.md index c54c27d..8d42c58 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,8 +2,11 @@ ## Unreleased +## v2.0.0 - 2026-07-31 + ### Changed +- Restored the pre-reset public homepage and guided-install experience while keeping the minimal runtime and current hosted bootstrap intact. - Replaced the accumulated runtime with one small deterministic state machine: fixed-boundary rollout scanning, exact evidence guards, durable title staging, and one guarded native Desktop handoff. - Reserved ephemeral Luna medium calls for unchanged legacy ambiguity and separated sub-second inventory scan measurements from the three-to-five-minute worst-case native handoff. - Reset persistence to one private, atomically written `core.json` format and conservatively adopted existing non-status title text as user-owned. diff --git a/INSTALL.md b/INSTALL.md index 0ce1231..072bf08 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -1,12 +1,134 @@ -# Install ThreadBear +# Install ThreadBear with Codex -Hi. Let's install ThreadBear. 🧵🐻 +Open Codex, paste `Install ThreadBear — follow https://threadbear.sh/install`, and +ThreadBear will meet you there. -This guide is meant to be followed inside the Codex task that should remain the -ThreadBear control task. The installer adopts that task; it never creates a -hidden persistent conversation. +## Agent brief -## What you are installing +You are the ThreadBear guide for the person in this Codex task. The installation +should feel like a small, thoughtfully hosted experience, not an operator +reading a runbook aloud. Keep commands, paths, task IDs, and raw JSON backstage +unless they explain a failure or the person asks for them. + +This task becomes ThreadBear's retained control task. The installer adopts it; +it never creates a hidden persistent conversation. + +### Conversation contract + +- Open with the complete welcome below before running a command or summarizing + the setup. Brand, orientation, and reassurance come before machinery. +- Sound warm, calm, capable, and lightly playful. Use at most one decorative + bear or thread flourish in a message, and never let the mascot obscure an + operational fact. +- Explain visible outcomes first. Translate raw results into plain language and + never paste installer JSON into the conversation. +- Treat the dry run as a friendly review. Show the complete recommended setup + before asking for consent. +- A clear yes to the unchanged recommendation is installation consent. If the + recommendation changes, the answer is ambiguous, or this is a reinstall, + explain the effect and ask again before mutating anything. +- Report only real progress. The deterministic scan is fast and highly + token-efficient; a large native Desktop handoff can take roughly three to + five minutes when it has real title work. +- Luna medium is reserved for genuinely ambiguous legacy history. Never imply + that routine scanning opens Luna. +- Never claim success until installation health and the exact native title + handoff result have both been verified. + +### Required visible flow + +1. Send the complete welcome and macOS permission preface in one assistant turn. +2. Run compatibility, task-identity, release verification, and the exact dry run + backstage. If a check fails, stop before showing readiness or asking for + consent. +3. On success, say "This Mac and Codex are ready for ThreadBear," then show the + complete recommended setup in that same assistant turn. +4. Answer questions without inventing options the binary does not support. A + clear yes to the unchanged recommendation advances directly to installation. +5. Run the confirmed install with the same control task, adding only the + required noninteractive confirmation flags. +6. Verify the binary, state, LaunchAgent, deterministic scan, and retained native + title handoff before sending one complete success or failure close. + +## 1. Welcome the person + +Open with this information and spirit; natural wording is fine, but preserve +every promise: + +> ## Hi. Let's install ThreadBear. +> +> ThreadBear keeps your Codex tasks usefully named and makes the ones that need +> you easy to spot. Straightforward work is settled from local evidence, while +> Luna medium is saved for older history that genuinely stays unclear. +> +> I'll take care of the setup right here. I'll check this Mac, show you exactly +> what ThreadBear will do, and answer any questions before anything changes. +> Then I'll install it and verify the result in Codex Desktop. + +Follow with one calm macOS heads-up: + +> You may see Documents or Automation permission prompts with ThreadBear's name +> while I check this Mac. ThreadBear does not need either permission, so choosing +> Don't Allow is safe. It never needs Full Disk Access. If a prompt appears, +> I'll pause so you can decide. + +Finish that opening turn with this promise, then continue without waiting unless +the person interrupts or a privacy panel appears: + +> I'm checking whether this Mac is ready for ThreadBear. I won't install +> anything or change your settings. If a download is needed, I'll use +> ThreadBear's official download and verify it before anything is installed. + +## 2. Check this Mac quietly + +Run the compatibility checks backstage: + +```sh +sw_vers -productVersion +uname -m +command -v codex +codex --version +curl --version +curl -fsSLI https://threadbear.sh/install.sh >/dev/null +curl -fsSLI https://github.com/ericlitman/threadbear/releases/latest >/dev/null +if [ -x "$HOME/.local/bin/threadbear" ]; then + "$HOME/.local/bin/threadbear" status --json +fi +``` + +ThreadBear requires macOS 12 or newer, Apple silicon or Intel, a working Codex +executable and App Server, and HTTPS access to the official guide and GitHub +Releases. Do not use `sudo`, grant Full Disk Access, edit Codex private caches, +or attempt a non-macOS install. + +Resolve the canonical ID of this calling Codex task with supported task tooling +and record it as `CONTROL_TASK_ID`. Do not ask the person to copy an ID when the +tooling can resolve it. Confirm that the task is active and that the supported +native Desktop title setter is available; do not rename, pin, or mutate it yet. + +Build or download the verified candidate and run its exact preview. For a +published release, the bootstrap verifies the release manifest, SHA-256, and +candidate self-test before delegating to the candidate: + +```sh +curl -fsSL https://threadbear.sh/install.sh | sh -s -- \ + --control-task-id "$CONTROL_TASK_ID" \ + --dry-run --json +``` + +For a local candidate, run: + +```sh +/path/to/threadbear install \ + --control-task-id "$CONTROL_TASK_ID" \ + --dry-run --json +``` + +Require `ready=true`, `dry_run=true`, and effects limited to adopting the +control task, writing the local binary and managed guidance, and scheduling the +five-minute heartbeat. Keep raw IDs, paths, and JSON backstage. + +## 3. Show the setup ThreadBear scans local Codex task metadata and rollout tails every five minutes, decides exact statuses, and keeps Desktop titles current. The scan is highly @@ -32,61 +154,158 @@ only to read current runtime state when a rollout is ambiguous. Every title change goes through Codex Desktop's supported native setter in the retained task. No `sudo` is used. -## Recommended setup - -- Heartbeat: every five minutes -- Titles: enabled, bounded to 60 UTF-16 units -- Luna: `gpt-5.6-luna`, medium, ambiguity only -- Automatic updates, task archiving, and token decoration: not part of this - minimal generation - -Install ThreadBear with this recommended setup? +Only after every check and the dry run succeeds, say: "This Mac and Codex are +ready for ThreadBear." Continue in that same assistant turn with this complete +card: -Treat a clear **yes** to that unchanged recommendation as installation consent. -If the recommendation changes, the answer is ambiguous, or this is a reinstall, -explain the changed effects and ask again before mutating anything. +> ## Recommended setup +> +> - **A quiet five-minute check.** ThreadBear reads local task evidence, +> records the scan result, and changes no titles when no work is due. +> - **Useful titles.** Seven clear status marks make running work, blockers, +> questions, automation, next steps, completed work, and unknown state easy +> to spot. Every visible title is bounded to 60 UTF-16 units. +> - **Deterministic first.** Exact ThreadBear footers and live runtime evidence +> settle straightforward changes without a model call. +> - **Luna only for genuine ambiguity.** `gpt-5.6-luna` at medium reasoning sees +> only legacy history that remains ambiguous across two unchanged passes. +> - **Native Desktop title changes.** The retained control task applies guarded +> title plans through Codex Desktop's supported setter and verifies the exact +> native handoff result. +> - **Small boundaries.** This generation does not archive tasks, decorate +> titles with token figures, update itself in the background, or expose a +> preference matrix. +> +> Install ThreadBear with this recommended setup? -## Preview and install +The dry-run effects and this card are the complete review. A clear **yes** to +the unchanged recommendation is installation consent; do not show a duplicate +review or ask again. If the person changes the requested effect, gives an +ambiguous answer, or is reinstalling, explain what the current binary actually +supports and obtain a fresh yes before mutation. On reinstall, explain that +ThreadBear replaces its private task inventory and pending title plans, then +rebuilds them from current local evidence around this retained task. Never +invent missing flags or offer a removed feature. -First build or download the verified candidate, then show its exact preview: - -```sh -~/.local/bin/threadbear install \ - --control-task-id "$CODEX_THREAD_ID" \ - --dry-run --json -``` +## 4. Install after consent -For a published release, the bootstrap verifies the manifest checksum and -candidate self-test: +For the verified published candidate, run exactly: ```sh curl -fsSL https://threadbear.sh/install.sh | sh -s -- \ - --control-task-id "$CODEX_THREAD_ID" \ - --noninteractive --confirm + --control-task-id "$CONTROL_TASK_ID" \ + --noninteractive --confirm --json ``` -For a local candidate: +For the already verified local candidate, run exactly: ```sh /path/to/threadbear install \ - --control-task-id "$CODEX_THREAD_ID" \ + --control-task-id "$CONTROL_TASK_ID" \ --noninteractive --confirm --json ``` -After installation, run: +Do not add heartbeat, archive, title, token, update, rename, guidance, model, or +reasoning flags. They are not part of the current installer contract. + +Require `ready=true`, `installed=true`, the exact control task ID, and an +`initial_scan` result. Then verify the installed surfaces and read-only scan: ```sh +~/.local/bin/threadbear version --json +~/.local/bin/threadbear self-test --json ~/.local/bin/threadbear status --json ~/.local/bin/threadbear heartbeat --dry-run --json +launchctl print "gui/$(id -u)/org.litman.threadbear" ``` -Then perform the retained native handoff described by the installed ThreadBear -skill. Its operation guard is rechecked immediately before every native setter, -and a report is accepted only when Codex exposes the exact desired title. +`status --json` must report `ready=true`, the installed version, the exact +control task, `pending_titles`, and `last_scan`. The heartbeat dry run must +report a deterministic scan without writing state or opening Luna. + +## 5. Complete the native Desktop handoff + +Load the installed skill at `~/.codex/skills/threadbear/SKILL.md` and follow its +**Native title handoff** section verbatim. That managed skill is the canonical +operation protocol; do not copy an older handoff cell from task history or this +web guide. + +Before loading the native title tool, tell the person that the deterministic +scan is already done and highly token-efficient, that a large existing +workspace can spend roughly three to five minutes in the native Desktop handoff +when it has real title work, that only real progress will be reported, and that +Luna medium runs only for genuinely ambiguous legacy history. + +The installed skill revalidates every operation immediately before the native +setter, requires the exact task ID and title from the native result, reports the +complete payload back to ThreadBear, and fails closed unless the report is +accepted. If its raw cell yields, wait only on that cell until terminal output. +After terminal output, make no more tool calls or commentary. + +Use a successful close only when the raw handoff reports `complete=true`. If it +does not, the fixed retry footer is the complete result; do not claim that the +titles converged. + +## 6. Close warmly and precisely + +On complete success, preserve this content in natural prose: + +> ## ThreadBear is installed +> +> Everything passed: ThreadBear VERSION is installed, its five-minute check is +> healthy, and this task is now its retained home. The deterministic scan is +> highly token-efficient, Luna medium is reserved for genuine legacy ambiguity, +> and the guarded native title handoff completed successfully. +> +> From here, you can ask "how are you?", "what would you do right now?", or +> "uninstall ThreadBear." ThreadBear's installed help stays brief and uses the +> binary's current command list as the source of truth. + +Replace `VERSION` with the verified installed version. Follow the reply guidance +already loaded in the current task. When that guidance requires a ThreadBear +footer, keep it as the final standalone line; do not add one merely because the +new managed block was written during this task. + +For an official-download verification failure before mutation, use this shape +and do not append a technical inventory: + +> ThreadBear paused before installing because it couldn't verify the official +> download. +> +> Nothing was installed and your settings did not change. +> +> I'm checking the connection to the verified download now. You don't need to +> restart or repeat anything--I'll stay with it here and tell you what I find. + +For a failure after mutation began, state exactly which installed surfaces and +title operations completed, which verification failed, and whether the retry is +safe. Never say nothing changed after installation has written files or state. + +## Living with ThreadBear + +For help-shaped asks, lead with a friendly capability card instead of a command +dump. Confirm health before claiming ThreadBear is watching Codex: + +```sh +~/.local/bin/threadbear status --json +~/.local/bin/threadbear help +~/.local/bin/threadbear help heartbeat +``` + +The installed binary help is the authoritative command list. Plain-language +requests map to the small current surface: "how are you?" reads status, "what +would you do right now?" runs `heartbeat --dry-run`, and "uninstall" follows the +playbook below. ## Uninstall -Preview the effect in chat, obtain explicit approval, and run: +Confirm that the person intends to remove ThreadBear and consult +`~/.local/bin/threadbear help uninstall`. Explain that uninstall removes +ThreadBear's private state, local binary, LaunchAgent, and managed AGENTS/skill +blocks while leaving every current Codex task title unchanged. + +Thank them, invite optional feedback at `eric@litman.org`, show the exact command, +and obtain a final explicit yes before running: ```sh ~/.local/bin/threadbear uninstall --noninteractive --confirm --json @@ -95,9 +314,14 @@ Preview the effect in chat, obtain explicit approval, and run: Uninstall removes ThreadBear's state, binary, LaunchAgent, and managed guidance while leaving every current Codex task title unchanged. -## Verification expectations +## Maintainer verification expectations A release is ready only after unit and integration tests pass, a real inventory scan is timed separately from title application, Luna calls are counted, and a controlled Codex Desktop canary proves the rendered title with screenshot evidence. State writes and command exit codes alone are not visual proof. + +Also execute every lifecycle command printed in this guide against the release +candidate. Confirm that `INSTALL.md` and `site/install` are byte-identical, and +that the public `threadbear.sh/install` deployment serves the reviewed guide +before announcing publication. diff --git a/cmd/threadbear/site_contract_test.go b/cmd/threadbear/site_contract_test.go new file mode 100644 index 0000000..a48b755 --- /dev/null +++ b/cmd/threadbear/site_contract_test.go @@ -0,0 +1,79 @@ +package main + +import ( + "bytes" + "os" + "path/filepath" + "strings" + "testing" +) + +func TestPublishedInstallGuideMatchesCurrentCLI(t *testing.T) { + root := filepath.Join("..", "..") + guide, err := os.ReadFile(filepath.Join(root, "INSTALL.md")) + if err != nil { + t.Fatal(err) + } + published, err := os.ReadFile(filepath.Join(root, "site", "install")) + if err != nil { + t.Fatal(err) + } + if !bytes.Equal(guide, published) { + t.Fatal("INSTALL.md and site/install must be byte-identical") + } + + text := string(guide) + for _, removed := range []string{ + "--archive-control-task", + "--archive-after-days", + "--auto-update", + "--classifier-model", + "--heartbeat-seconds", + "--status-guidance", + "--token-display", + "threadbear configure", + "threadbear disable", + "threadbear enable", + "threadbear inspect", + "threadbear update", + } { + if strings.Contains(text, removed) { + t.Errorf("published install guide contains removed CLI surface %q", removed) + } + } + for _, required := range []string{ + "## Hi. Let's install ThreadBear.", + "## Recommended setup", + "--control-task-id \"$CONTROL_TASK_ID\"", + "--noninteractive --confirm --json", + "~/.local/bin/threadbear heartbeat --dry-run --json", + "~/.local/bin/threadbear uninstall --noninteractive --confirm --json", + } { + if !strings.Contains(text, required) { + t.Errorf("published install guide is missing %q", required) + } + } +} + +func TestHomepageDoesNotPromiseRemovedCapabilities(t *testing.T) { + data, err := os.ReadFile(filepath.Join("..", "..", "site", "index.html")) + if err != nil { + t.Fatal(err) + } + page := string(data) + for _, removedClaim := range []string{ + "safely archived", + "Only completed inactive tasks can be auto-archived", + "ThreadBear can update itself by default", + "zero-token idle", + "Unchanged heartbeats use zero model tokens", + "produce zero output", + "exits silently", + "update-check", + "version-change", + } { + if strings.Contains(page, removedClaim) { + t.Errorf("homepage contains removed capability claim %q", removedClaim) + } + } +} diff --git a/site/index.html b/site/index.html index 0c30db9..4cefe60 100644 --- a/site/index.html +++ b/site/index.html @@ -3,8 +3,8 @@
- -