-
Notifications
You must be signed in to change notification settings - Fork 2
docs(updates): design — in-app updates (Sparkle + plugin detect/hand-off) - #74
docs(updates): design — in-app updates (Sparkle + plugin detect/hand-off) #74jordanrburger merged 2 commits into
Conversation
The plugin metadata, installer, and self-update raw URL still referenced the pre-org personal repo (jordanrburger/scout-plugin). Canonical repo is Raven-Scout/scout-plugin. Updates all live references: - .claude-plugin/plugin.json homepage + repository - .claude-plugin/marketplace.json homepage + repository - install.sh curl one-liner + MARKETPLACE slug - engine/scout/scripts/self_update.py RAW_MARKETPLACE_URL - engine/tests/unit/test_versioning.py fixture URL (consistency only; no test asserts these URLs) Historical design docs under docs/plans/ and docs/specs/ are left untouched as dated records (old links redirect via GitHub). Flagged as open item 1 on Raven-Scout/Scout#74 — the in-app updates raw-URL fallback there can now default to the org URL. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The plugin metadata, installer, and self-update raw URL still referenced the pre-org personal repo (jordanrburger/scout-plugin). Canonical repo is Raven-Scout/scout-plugin. Updates all live references: - .claude-plugin/plugin.json homepage + repository - .claude-plugin/marketplace.json homepage + repository - install.sh curl one-liner + MARKETPLACE slug - engine/scout/scripts/self_update.py RAW_MARKETPLACE_URL - engine/tests/unit/test_versioning.py fixture URL (consistency only; no test asserts these URLs) Historical design docs under docs/plans/ and docs/specs/ are left untouched as dated records (old links redirect via GitHub). Flagged as open item 1 on Raven-Scout/Scout#74 — the in-app updates raw-URL fallback there can now default to the org URL. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
...po (#188) The plugin metadata, installer, and self-update raw URL still referenced the pre-org personal repo (jordanrburger/scout-plugin). Canonical repo is Raven-Scout/scout-plugin. Updates all live references: - .claude-plugin/plugin.json homepage + repository - .claude-plugin/marketplace.json homepage + repository - install.sh curl one-liner + MARKETPLACE slug - engine/scout/scripts/self_update.py RAW_MARKETPLACE_URL - engine/tests/unit/test_versioning.py fixture URL (consistency only; no test asserts these URLs) Historical design docs under docs/plans/ and docs/specs/ are left untouched as dated records (old links redirect via GitHub). Flagged as open item 1 on Raven-Scout/Scout#74 — the in-app updates raw-URL fallback there can now default to the org URL. Co-authored-by: Jordan Burger <jordan.burger@keboola.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
...etect/hand-off) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Plan for the full approved scope — Sparkle app track, plugin detect/hand-off, Settings ▸ Updates, sidebar/menu-bar badge, release.sh (inside-out signing, sign_update on the stapled DMG, appcast regenerated and pushed to main). Spec amendments are hardening only; brainstorm decisions stand: exact Sparkle 2.9.6 pin, typed Info.plist + contract test, Debug builds never start the updater, release.sh guards (key preflight, monotonic build number, component existence, clean main), PRERELEASE=1 + SCOUT_APPCAST_URL rc rehearsal before 0.12.0, one-item regenerated feed with an empty initial appcast.xml, release-lib.sh with bash tests in CI, plugin manifest read at the raw HEAD ref. Resolves the two open items (canonical org is Raven-Scout/scout-plugin; sparkle:version = commit count). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
jordanrburger
commented
Sep 3, 2026
Picking this back up (2026年09月02日). Rebased feat/in-app-updates onto current main (16 commits behind, no conflicts — the branch only adds the spec) and pushed two things:
1. Implementation plan — docs/superpowers/plans/2026-09-02-in-app-updates.md, 14 tasks, TDD, one commit each, for the full approved scope: Sparkle app track, plugin detect + /scout-update hand-off, Settings ▸ Updates rows with chips, badge on the Settings sidebar row and menu-bar icon, release.sh (inside-out signing of Sparkle's nested code, sign_update on the stapled DMG, appcast.xml regenerated and pushed to main).
2. Spec amendments — appended as a clearly separated "Amendments (2026年09月02日)" section; none of the brainstorm decisions change. Each is individually rejectable:
- exact Sparkle
2.9.6pin; typedScout/Info.plist+ contract test - Debug builds never start the updater (dev bundle is
com.scout.Scout.dev); all Sparkle code still compiles in Debug so CI catches breakage release.shguards: plist public key must match the keychain key, build number must exceed the last tag's, expected Sparkle components must exist, release only from a cleanmain,sign_updateafter stapling,xmllinton the appcastPRERELEASE=1publishes an rc with the appcast as a release asset only, and aSCOUT_APPCAST_URL(https-only) override lets an installed rc.1 update to rc.2 — the rehearsal that must pass before 0.12.0, since that's the one release a broken updater can't fix through itself- one-item feed regenerated per release + an empty valid
appcast.xmlcommitted up front so the URL never 404s; HTML notes rendered from the same feat/fix/other grouping; helpers inscripts/release-lib.shwith bash tests in CI - plugin "latest" read at the raw
HEADref (no API call for the default branch) - both open items resolved: canonical org is
Raven-Scout/scout-plugin(fixed in scout-plugine0f86f5);sparkle:version= commit count confirmed
Not adopted, listed in the spec for you to opt into: 6-hour interval, SUAutomaticallyUpdate (silent download), Settings toggles, appcast-as-release-asset hosting. Daily checks + dialog-first + checked-in feed stand as decided.
Two tasks are yours unless you delegate: Task 2 (generate_keys + backup — the key is the root of trust for every future update) and Task 14 (rc.1 → rc.2 rehearsal, then scripts/release.sh → 0.12.0).
#97 was my duplicate of the app half of this, written without checking open PRs — closed as superseded.
0e8f15e to
bfa54b8
Compare
What
Design spec for in-app updates — a single update surface in Scout.app that tracks two independent things:
appcast.xmlserved fromraw.githubusercontent.com/Raven-Scout/Scout/main/appcast.xml.installed → latestand copies/scout-updateto the clipboard.Both feed one observable
UpdateService, surfaced as a Settings ▸ Updates section plus a badge on the sidebar and menu-bar icon.Why the two tracks are asymmetric
The app track can self-install (Sparkle owns detection and application). The plugin track can only surface the gap and hand you the command — there is no app→Claude-Code interface to drive a slash command.
Grounded on the real install layout
~/.claude/plugins/installed_plugins.json→plugins["scout@scout-plugin"][0].version(currently0.7.2; repo is0.7.3).known_marketplaces.json: GitHub raw for end users, local directory for dev checkouts (this machine).Grape), so Sparkle is added the same way.The part with teeth:
release.shSparkle adds nested code (
Sparkle.framework,Autoupdate,Updater.app, XPC services) that must be signed inside-out with Developer ID + hardened runtime before the outer.app, then notarized — a required change from today's flat-.appsigning. Per release:sign_updatethe DMG, append anappcast.xmlentry (EdDSA signature + the GitHub release DMG URL as the enclosure), commit + push the feed.Decisions locked in brainstorm
appcast.xmlvia raw GitHub (no Pages)Open items for review
plugin.jsonhomepagesaysjordanrburger/scout-plugin, README saysRaven-Scout/scout-plugin. Runtime detection reads the marketplace source so this isn't blocking, but the fallback default should be right.sparkle:version= commit-countCURRENT_PROJECT_VERSIONas the comparison key,MARKETING_VERSIONas the display string — monotonic, matchesrelease.shtoday, no new state file.Next
Per the review-first flow, the implementation plan lands on this same branch/PR next (spec + plan reviewed together before any code).
Spec:
docs/superpowers/specs/2026-07-07-in-app-updates-design.md🤖 Generated with Claude Code