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
4 changes: 2 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ internal/
diff/ # Pure-logic system-vs-config comparison
dotfiles/ # Clone + stow with .openboot.bak backup
httputil/ # HTTP Do() with rate-limit + Retry-After
installer/ # 7-step wizard orchestrator + snapshot restore
installer/ # Install planning + linear apply + snapshot restore
macos/ # defaults write + app restart
npm/ # Batch install with sequential fallback
permissions/ # macOS screen-recording probe
Expand All @@ -115,7 +115,7 @@ scripts/
|------|----------|-------|
| Add CLI command | `internal/cli/` | Register in `root.go init()`, follow cobra pattern |
| Change install flow | `internal/installer/installer.go` | plan -> apply orchestrator; `PlanFromSelection` builds a plan from TUI picks |
| Change interactive install TUI | `internal/ui/tui/wizard/` | Redesign v5: boot/select/install screens; live install streams `internal/progress` events (brew/npm `SetProgressSink`) |
| Change interactive install TUI | `internal/ui/tui/wizard/` | Redesign v5 planning screens: boot -> select -> git -> review; apply starts only after the wizard exits |
| Change sync behavior | `internal/sync/diff.go`, `internal/sync/plan.go` | Diff -> confirm -> execute |
| Add package category | `openboot.dev/src/lib/package-metadata.ts` | Server is source of truth; CLI fetches `/api/packages` and caches 24h in `~/.openboot/packages-cache.json`. `data/packages.yaml` is fallback only. |
| Modify presets | `internal/config/data/presets.yaml` | 3 presets: minimal, developer, full |
Expand Down
6 changes: 2 additions & 4 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@

## v1.0 (unreleased)

OpenBoot v1.0 narrows the product to two verbs: **`install`** (add things to your Mac) and **`snapshot`** (save your current state somewhere). Everything else is either cloud-config CRUD or independent tooling.
OpenBoot v1.0 narrows the core workflow to two verbs: **`install`** (add things to your Mac) and **`snapshot`** (save your current state somewhere). The supporting **`doctor`** and **`update`** maintenance commands remain available; everything else is either cloud-config CRUD or independent tooling.

### Breaking changes

Six commands are removed outright. Each prints an error with a migration hint when invoked:
Eight commands are removed outright:

| Removed | Use instead |
|---------|-------------|
Expand All @@ -18,8 +18,6 @@ Six commands are removed outright. Each prints an error with a migration hint wh
| `openboot restore` | **no replacement** — version history is dropped |
| `openboot init` | **no replacement** — use your project's own tooling (npm/pip/go/cargo) |
| `openboot setup-agent` | **no replacement** — existed only to service `openboot init` |
| `openboot doctor` | **no replacement** — use `brew doctor` and `git config --list` directly |
| `openboot update` | **no replacement** — use `brew upgrade` directly; OpenBoot self-updates on launch |

Three flat commands are removed with no replacement — manage configs directly at openboot.dev:

Expand Down
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -167,10 +167,12 @@ openboot snapshot --publish # Upload to openboot.dev
openboot snapshot --import FILE # Restore from a snapshot file

openboot login / logout # openboot.dev auth
openboot doctor # Check system health and diagnose issues
openboot update # Update, pin, or roll back OpenBoot
openboot version # Print version
```

Removed in v1.0: `pull`, `push`, `diff`, `clean`, `log`, `restore`, `init`, `setup-agent`, `doctor`, `update`. See [CHANGELOG.md](CHANGELOG.md) for migration.
Removed in v1.0: `pull`, `push`, `diff`, `clean`, `log`, `restore`, `init`, `setup-agent`. See [CHANGELOG.md](CHANGELOG.md) for migration.

</details>

Expand Down
6 changes: 1 addition & 5 deletions docs/HARNESS.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ Three regulation categories:
| Arch. | `no-raw-http` | L1 | `internal/archtest/http_test.go` |
| Arch. | `no-os-getenv-home` | L1 | `internal/archtest/envhome_test.go` |
| Arch. | `dryrun` — destructive ops must check `DryRun` | L1 | `internal/archtest/dryrun_test.go` |
| Arch. | `no-raw-fmtprint` — UI output via `ui.*` helpers, not raw `fmt.Print*` | L1 | `internal/archtest/fmtprint_test.go` |
| Arch. | `fmtprint` — UI output via `ui.*` helpers, not raw `fmt.Print*` | L1 | `internal/archtest/fmtprint_test.go` |
| Arch. | `install.sh` must not prompt on stdin — under `curl \| bash` stdin is the script | L1 | `internal/archtest/installsh_test.go` |
| Behav. | L1 unit + integration + contract (faked runners *and* real brew/git/npm in temp dirs) | pre-push, CI | `make test-unit` |
| Behav. | L2 contract schema (against openboot-contract repo) | CI | `.github/workflows/test.yml` `contract` job |
Expand Down Expand Up @@ -93,10 +93,6 @@ it survives doc rot.
- **No coverage gate that fails PRs.** Coverage is informational
(`codecov.yml` `informational: true`). Hard coverage gates push toward
test-shaped code without raising actual quality.
- **No fmt.Print/Println archtest rule yet.** The convention exists in
AGENTS.md but the codebase has ~150 existing call sites and the rule
would be mostly noise. Reconsider after the UI helpers cover all the
cases currently using raw stdout.
- **No agent-driven changes to `main` without human review.** All AI
changes go through PR review and the existing CI matrix.
- **No retroactive refactors triggered by new archtest rules.** New rules
Expand Down
2 changes: 2 additions & 0 deletions internal/archtest/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ from the baseline) are logged but do not fail the test.
| `no-raw-http` | `http_test.go` | yes | "Use `httputil.Do()` — handles 429 + Retry-After" |
| `no-os-getenv-home` | `envhome_test.go` | no (hard rule) | "Use `os.UserHomeDir()` — never hardcode `~` or `/Users/...`" |
| `dryrun` | `dryrun_test.go` | yes | "Destructive ops: check `cfg.DryRun` first. Always." |
| `fmtprint` | `fmtprint_test.go` | yes | "UI output must go through `ui.*` helpers" |
| `installsh` | `installsh_test.go` | no (hard rule) | "`install.sh` must not prompt on stdin" |
| `agent-config` | `agentconfig_test.go` | no (hard rule) | "Keep `AGENTS.md` and shared skills canonical across agent tools" |

## Workflow
Expand Down
2 changes: 1 addition & 1 deletion internal/cli/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ func parseConflictError(body []byte) error {
msg = errResp.Error
}
if msg != "" && strings.Contains(strings.ToLower(msg), "maximum") {
return errors.New("config limit reached (max 20): delete an existing config with 'openboot delete <slug>' first")
return errors.New("config limit reached (max 20): delete an existing config at https://openboot.dev/dashboard first")
}
if msg != "" {
return errors.New(msg)
Expand Down
30 changes: 21 additions & 9 deletions internal/cli/helpers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,25 @@ import (

func TestParseConflictError_MaxConfigsMessage(t *testing.T) {
tests := []struct {
name string
body []byte
wantContain string
name string
body []byte
wantContain string
wantAlsoContain string
wantNotContain string
}{
{
name: "message field contains maximum",
body: mustMarshalJSON(t, map[string]string{"message": "You have reached the maximum number of configs"}),
wantContain: "config limit reached",
name: "message field contains maximum",
body: mustMarshalJSON(t, map[string]string{"message": "You have reached the maximum number of configs"}),
wantContain: "config limit reached",
wantAlsoContain: "https://openboot.dev/dashboard",
wantNotContain: "openboot delete",
},
{
name: "error field contains maximum",
body: mustMarshalJSON(t, map[string]string{"error": "Maximum configs exceeded"}),
wantContain: "config limit reached",
name: "error field contains maximum",
body: mustMarshalJSON(t, map[string]string{"error": "Maximum configs exceeded"}),
wantContain: "config limit reached",
wantAlsoContain: "https://openboot.dev/dashboard",
wantNotContain: "openboot delete",
},
{
name: "plain message field returned as-is",
Expand Down Expand Up @@ -51,6 +57,12 @@ func TestParseConflictError_MaxConfigsMessage(t *testing.T) {
err := parseConflictError(tt.body)
require.Error(t, err)
assert.Contains(t, err.Error(), tt.wantContain)
if tt.wantAlsoContain != "" {
assert.Contains(t, err.Error(), tt.wantAlsoContain)
}
if tt.wantNotContain != "" {
assert.NotContains(t, err.Error(), tt.wantNotContain)
}
})
}
}
Expand Down
Loading