eleven / messenger Public

Apps live in places — the unified placement model, space-wide installs, and the pullback (#853)

Squashed history of PR #853. The through-line: replace the app framework's
tangle of placement flags with ONE vocabulary — `places` — carry it through
routing, the UI, and the member-app pipeline, then deliberately pull the
member-facing surface back behind a flag while the machinery stays live.

The places model
- Every app declares `places`: any non-empty subset of ["you","chat","space"].
  Legacy flags (personal/groupCapable/spaceCapable) and the older `scope` enum
  are accepted as INPUT at one funnel (normalizePlaces) then deleted, so nothing
  downstream reads a capability flag and a stale one can't disagree with places.
- Space-wide apps: one shared instance in a hidden all-members space thread,
  its commands usable from every chat; a sidebar "space band" home; install via
  the Apps sheet or `/cmd @everyone`. The Vault is space-capable (a team
  credential shelf); /gif asks for one Giphy key per space, asked once.
- The place-flag family — @me / @here / @everyone — with the "/" typeahead
  teaching the flags as selectable rows, in flowing per-row sentences.
- The Apps sheet is focus-and-explain: one directory, each app naming its
  places, the verb on a footer action.

The pullback (member-facing UI hidden, machinery live)
- Shipped apps park at ["you","chat"]; the multi-place UI hides behind the
  PLACES_UI device flag (default off) so two same-named rows can't appear in
  the thread list. Space band, install sheets, and space routing stay live and
  tested via synthetic fixtures.

The hook split
- chatMessage now means only "companion Chat view"; groupFeedMessage is the
  feed-only projection into group chats (fallback: chatMessage(_, {group:true})).
  Replaces the mis-named groupChatOnly flag — the hook shape is the declaration.

Member apps
- May declare any non-empty places subset (space-only + listed:false, the /gif
  shape, included). A documented compat policy (docs/member-apps.md § Legacy
  formats) + App Studio "older format" flag with one-tap in-place Upgrade.

Security + review fixes
- Member commands that collide with an existing verb (built-in or another app)
  are rejected at the load-time gate — a crafted blob could otherwise shadow
  /todo or /help on every client and route typed args into a hijacker-readable
  log. Plus confirmed lows: @here stripped on chat-only apps, spaceAppInfo
  honours the admins-only policy, installs registry declares places:[].

Docs: apps/README, space-apps.md, building-apps.md, building-member-apps.md,
member-apps.md, app-credentials.md, CLAUDE.md all speak places; the bot's
served guide teaches it. Tests + event vectors move with the contract.
Co-authored-by: Claude <noreply@anthropic.com>
James Adam 78989463bd3dac5fb2493fb90d0c08a1b07ec64b parent befd217
63 files changed, +4727 −629
  • .claude/skills/verify/SKILL.md +31 −3
  • CLAUDE.md +25 −6
  • bot_appbuild_test.go +11 −9
  • changelog/2026-07-14-poll-whiteboard-widget-bar.md +7 −0
  • changelog/2026-07-15-app-studio-upgrade.md +6 −0
  • changelog/2026-07-15-command-place-flags.md +7 −0
  • changelog/2026-07-15-gif-space-key.md +9 −0
  • changelog/2026-07-15-typeahead-install-offers.md +9 −0
  • checkpoint_test.go +1 −1
  • db.go +153 −4
  • docs/app-credentials.md +275 −0
  • docs/building-apps.md +42 −39
  • docs/building-member-apps.md +73 −11
  • docs/member-apps.md +40 −2
  • docs/space-apps.md +258 −0
  • internal/bot/appbuild.go +12 −6
  • ios/LChatCore/Sources/LChatCore/GroupApps.swift +2 −2
  • ios/LChatCore/Tests/LChatCoreTests/GroupAppsVectorTests.swift +1 −1
  • main.go +45 −15
  • miniapps.go +3 −0
  • push.go +7 −0
  • spacethread.go +135 −0
  • spacethread_test.go +357 −0
  • test/apps.test.mjs +701 −58
  • test/check-app.test.mjs +25 −5
  • test/event-vectors.test.mjs +17 −0
  • test/event-vectors/groupapps.json +11 −3
  • test/event-vectors/index.js +2 −0
  • test/event-vectors/spaceapps.json +166 −0
  • test/sandbox-bridge.test.mjs +1 −1
  • test/sandbox.test.mjs +88 −11
  • tools/check-app.mjs +20 −7
  • web/pages/index.html +7 −1
  • web/static/apps.js +635 −134
  • web/static/apps/README.md +169 −82
  • web/static/apps/admin.js +11 −1
  • web/static/apps/appstudio.js +31 −4
  • web/static/apps/board.js +4 −1
  • web/static/apps/bookmarks.js +17 −11
  • web/static/apps/chat.js +4 −12
  • web/static/apps/connect.js +2 −1
  • web/static/apps/dnsimple.js +1 −1
  • web/static/apps/emoji.js +1 −2
  • web/static/apps/gif.js +65 −23
  • web/static/apps/github.js +1 −1
  • web/static/apps/hubspot.js +1 −1
  • web/static/apps/linear.js +1 −1
  • web/static/apps/memory.js +1 −1
  • web/static/apps/poll.js +21 −1
  • web/static/apps/remind.js +11 −4
  • web/static/apps/setup.js +1 −1
  • web/static/apps/stripe.js +1 −1
  • web/static/apps/todo.js +13 −4
  • web/static/apps/vault.js +26 −21
  • web/static/apps/views.js +1 −1
  • web/static/apps/welcome.js +1 −1
  • web/static/groupactions.js +378 −29
  • web/static/newchat.js +64 −11
  • web/static/sandbox-frame.js +18 −8
  • web/static/sandbox.js +70 −9
  • web/static/shell.js +536 −77
  • web/static/shellstate.js +16 −0
  • web/static/style.css +79 −0
.claude/skills/verify/SKILL.md

Loading diff…

CLAUDE.md

Loading diff…

bot_appbuild_test.go

Loading diff…

changelog/2026-07-14-poll-whiteboard-widget-bar.md

Loading diff…

changelog/2026-07-15-app-studio-upgrade.md

Loading diff…

changelog/2026-07-15-command-place-flags.md

Loading diff…

changelog/2026-07-15-gif-space-key.md

Loading diff…

changelog/2026-07-15-typeahead-install-offers.md

Loading diff…

checkpoint_test.go

Loading diff…

db.go

Loading diff…

docs/app-credentials.md

Loading diff…

docs/building-apps.md

Loading diff…

docs/building-member-apps.md

Loading diff…

docs/member-apps.md

Loading diff…

docs/space-apps.md

Loading diff…

internal/bot/appbuild.go

Loading diff…

ios/LChatCore/Sources/LChatCore/GroupApps.swift

Loading diff…

ios/LChatCore/Tests/LChatCoreTests/GroupAppsVectorTests.swift

Loading diff…

main.go

Loading diff…

miniapps.go

Loading diff…

push.go

Loading diff…

spacethread.go

Loading diff…

spacethread_test.go

Loading diff…

test/apps.test.mjs

Loading diff…

test/check-app.test.mjs

Loading diff…

test/event-vectors.test.mjs

Loading diff…

test/event-vectors/groupapps.json

Loading diff…

test/event-vectors/index.js

Loading diff…

test/event-vectors/spaceapps.json

Loading diff…

test/sandbox-bridge.test.mjs

Loading diff…

test/sandbox.test.mjs

Loading diff…

tools/check-app.mjs

Loading diff…

web/pages/index.html

Loading diff…

web/static/apps.js

Loading diff…

web/static/apps/README.md

Loading diff…

web/static/apps/admin.js

Loading diff…

web/static/apps/appstudio.js

Loading diff…

web/static/apps/board.js

Loading diff…

web/static/apps/bookmarks.js

Loading diff…

web/static/apps/chat.js

Loading diff…

web/static/apps/connect.js

Loading diff…

web/static/apps/dnsimple.js

Loading diff…

web/static/apps/emoji.js

Loading diff…

web/static/apps/gif.js

Loading diff…

web/static/apps/github.js

Loading diff…

web/static/apps/hubspot.js

Loading diff…

web/static/apps/linear.js

Loading diff…

web/static/apps/memory.js

Loading diff…

web/static/apps/poll.js

Loading diff…

web/static/apps/remind.js

Loading diff…

web/static/apps/setup.js

Loading diff…

web/static/apps/stripe.js

Loading diff…

web/static/apps/todo.js

Loading diff…

web/static/apps/vault.js

Loading diff…

web/static/apps/views.js

Loading diff…

web/static/apps/welcome.js

Loading diff…

web/static/groupactions.js

Loading diff…

web/static/newchat.js

Loading diff…

web/static/sandbox-frame.js

Loading diff…

web/static/sandbox.js

Loading diff…

web/static/shell.js

Loading diff…

web/static/shellstate.js

Loading diff…

web/static/style.css

Loading diff…