RRamnique Singhfix(harbor): name initial space general
| 文件 | 最后提交记录 | 最后更新时间 |
|---|---|---|
fix(harbor): name initial space general | 5 天前 | |
feat(harbor): managed apex — self-serve orgs, deployment entry, Dockerfile Apex face + Dockerfile; "Create an org" becomes the third way into spaces; consent page served on the apex; host-independent /healthz; contract next-list updated (parked /internal recorded). Co-Authored-By: Claude <noreply@anthropic.com> | 30 天前 | |
feat(spaces): protocol contract, Harbor server, and the apps/x client chain — v0 end to end - @rowboat/spaces-protocol: the wire contract package (v0) - Harbor: one core, three faces (HTTP/WS/MCP), the §11 gate as a test suite; in-memory and Postgres (PgStore) stores; content-addressed blob store with disk and S3 drivers (spec §6 shape) - apps/x: core client + live socket, IPC, org registry; adding an org registers its MCP agent face; agent discovery via list_spaces + the spaces skill (MCP entries derived from the registry, never mcp.json) - @rowboat in topics: per-topic sessions, receipts, presence; thread context is pull-don't-push (read_topic); working chips render at the typing-indicator position; first-message invocations trigger sessions - docs: CONTRACT.md progress; tiered auth amendment (DCR + refresh are AS policy, the org stays a pure resource server) - ci: apps/harbor test job; harbor builds before every apps/x install Co-Authored-By: Claude <noreply@anthropic.com> | 30 天前 | |
feat(spaces): add copy links for files, people, and discussions | 5 天前 | |
fix(harbor): terminate stalled live sockets instead of buffering, pool size knob A live socket whose peer stops draining (lid shut, tunnel) kept receiving whiteboard relay at ~30 frames/s per editor plus full-scene syncs, and `ws` held every byte in process memory until the heartbeat reaped the socket 25-50s later. send() now terminates a socket holding more than 2 MiB of unsent bytes; the client's existing reconnect-and-replay restores durable state and the board self-heals on its next full sync. postgresDb() takes a pool ceiling, read from DATABASE_POOL_MAX in main.ts (default unchanged at pg's 10). One pool serves every org on a deployment and each space-locked write holds a connection for its transaction, so the ceiling should track the database plan's connection limit. Test: a subscriber pauses its TCP socket while another member pushes 16 MB of whiteboard frames; the stalled socket closes with 1006 and the sender stays open. Fails with the cap raised to 1 GiB. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014XyaXEeVYZGC52ecLexFZT | 6 天前 | |
feat(spaces): mentions as link tokens, stamped by the org — layer 2 Layer 2 of the unread / Activity / notifications arc (spec §7 "Mentions", CONTRACT.md's mentions bullet, apps/harbor/MENTIONS_PLAN.md with the outcome). The wire form of a mention is now a link token whose href carries the key and whose label is only a hint: `[@Name](#member:<id>)`, and the fixed addresses `[@here](#here)` and `[@rowboat](#rowboat)`. One grammar in the protocol package is the only parser; every regex that turned names into ids or ids into names is gone (encodeMentions, mentionEndingAtCaret, the shared scanner's address checks and walker, the renderer chip's reverse lookup, push.ts's classifier regex). Harbor (migration 017): - The org STAMPS `Message.mentions` / `mentionsHere` / `mentionsRowboat` at post and edit from tokens alone — id only, no name fallback, only space members; a tombstone addresses nobody; `message_edited` carries the stamp. - A mention follows you into its thread (with reply and root-author rules). - `GET /v1/unread` gains `unreadMentions` per space (unread roots + unread replies in followed threads) and per followed thread. - Push classifies off the stamp; the agent face re-resolves every token's label from the roster before a body reaches a model; post/edit tool descriptions state the grammar. - Search text is app-computed (`search_text`, NOT NULL): tokens collapse to their key, so labels and the word "member" never index. - `service.migrateMentions`: the pre-token spelling (`@<id>`, bare @here / @rowboat) is rewritten once through the ordinary edit path, attributed to the author (dogfood decision), titles through retitle; ledger-gated so it runs once per org. - test/mentions.test.ts on both stores; push tests on the stamp. App: - The composer holds a mention as ONE inline atom (MentionNode): a pill with kind + id + label, one backspace, serialized to the token and parsed back for drafts and the edit box; the autocomplete inserts nodes for people, @here, and @rowboat; nothing rewrites typed text on send. The voice path produces no mentions. - Chips key on the id (app://space-member/<id> pre-parse); the name is the roster's, so same-name members no longer collide. @x/shared's faces (decorateMentions / resolveMentions / containsRowboatAddress) sit over the protocol grammar; the phone renders through the same code. - Read state folds mention counts; the sidebar and dock take the Slack posture — bold for unread, a number for mentions (every message in a DM). - Thread pane: Follow / Unfollow. - Skill: how an agent writes a mention. Not live-verified in the running app; typecheck, lint, and all suites pass. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WDVdZu6LnYEjWZReiHCQoE | 10 天前 | |
feat(spaces): the org decides notifications once; desktops get a notify frame and a dot-and-count badge The unread arc's delivery slice (layer 4, first cut). After a message commits, Harbor decides for every member of the space whether and why it should reach them — mention > here > dm > reply-in-a-followed-thread, off the stamp and the follow rows, never the text — and the one decision goes out twice: an ephemeral `notify` frame on each recipient's member channel (Slack's desktop_notification shape: the server decides, the client shows) and, level-gated, as push to their phones. Gagan's PushSender now only delivers (`send(rows)`); the classifier and the text builder moved into notify.ts so a toast and a banner can never disagree. Who is excluded: the author of their own direct post. An agent's post is the agent's act — the symmetry read state already keeps — so your own agent addressing you reaches you. Plain messages send no frame; only the phone's `all` level wants them. Never replayed: a closed desktop catches up from GET /v1/unread, a phone from push. Harbor: `NotifyReason` + the `notify` member frame (protocol events), `Store.listThreadFollowers` on both stores, `notify.ts` (decide + Notifier), push.ts reduced to level gate + Expo delivery (replies in followed threads now pass every level but off — Slack's default-on threads toggle), service hooks the Notifier outside the lock. Tests: notify.test.ts (matrix, decide, Notifier, wire on memory + pglite), push.test.ts around `send`. Desktop: live.ts routes `notify` to member handlers; `core/spaces/notify.ts` shows the org's title/body through the app notifier (background-only, deep link via the shared `spaceLink`), started by both hosts; the settings category is relabelled "Space mentions & DMs". No client-side policy — levels, DND and presence suppression return org-side in a later layer. The sidebar badge is redesigned in the same slice (Ramnique's design, replacing the solid gold pill that read brown on light and mustard on dark): every row with anything unread shows a dot and a figure. A grey dot means nothing here is for you and the figure is the unread count; a red dot means the figure is how many are for you — mentions, or every message in a DM. Hover gives both. New token `--stream-alert`, the one red in Spaces. Discussions badge the same way: expanded, a space row carries only its stream and each followed discussion its own replies; collapsed, the row sums them. The store keeps the roots' mentions apart from the threads' (`unreadRootMentions`) so the halves show apart with no server change; `SpaceBadge` is `{unread, forYou}` served by `streamBadge`, `threadBadge` and `spaceBadge`. Rail and dock sidebar use the same component. Tests: the badge arithmetic over a snapshot, and the component's states. Docs: CONTRACT.md notifications bullet + member-frame amendment + push amendment; PUSH_PLAN.md amendment. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WDVdZu6LnYEjWZReiHCQoE | 10 天前 | |
feat(harbor): managed apex — self-serve orgs, deployment entry, Dockerfile Apex face + Dockerfile; "Create an org" becomes the third way into spaces; consent page served on the apex; host-independent /healthz; contract next-list updated (parked /internal recorded). Co-Authored-By: Claude <noreply@anthropic.com> | 30 天前 | |
Spaces: file uploads — images in messages, files in the tree (#891) * feat(harbor): file uploads — blob routes + binary propose variant (spec §6; was §12-deferred) Two-phase uploads on the render face: PUT /v1/spaces/:id/blobs (raw bytes, mandatory x-blob-sha256 the org recomputes, sniff-first mime, no type restrictions, 100MB default cap) and GET /v1/spaces/:id/blobs/:hash, which streams on the disk driver and 302s to a presigned URL on S3 (new optional BlobStore.downloadUrl). proposeChange gains the blob variant (exactly one of newContent/blob); version rows, change-sets, listAssets entries, and ReadAssetResult carry {hash, size, mime}. Binary staleness never merges — conflict-or-replace with regions: [] + currentBlob; diff degrades to a readable binary stub. Migration 005: nullable content + blob jsonb columns, plus space_blobs — the space-scoped read gate over per-org-deduped bytes. Serving: inline only for sniffed images, everything else forced attachment + nosniff. Link grammar gains /b/<hash>. Blob storage wires from env (BLOBS_S3_BUCKET/… or BLOBS_DIR); unconfigured deployment-mode orgs refuse uploads loudly and everything else is untouched. New blobs-api.test.ts runs the flow end to end on both stores (memory + PGlite), same dual gate as §11; presigned URL shape unit-tested offline. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(spaces): file uploads in the app — composer attachments, files-tree uploads, app://space-blob rendering Composer: paperclip / paste / drag-drop attachments that upload at attach time (progress chips keep drop order; send is instant) and land on the wire as canonical /b/<hash> links — images as markdown images, other files as links. Rendering: new SpaceMarkdown is the one renderer for message bodies (same fix-it-once rule as MemberText) — rewrites org blob links to app://space-blob/…, which main serves through a content-addressed cache at ~/.rowboat/cache/blobs with nativeImage thumbnails; non-image blob links render as download cards backed by the save dialog. The renderer holds no org tokens, so all blob bytes resolve in main. Files: rail Upload button + drop-onto-tree open a destination-folder dialog (folders are key prefixes — typing design/screens creates the "folder"); binary entries get type icons; FileColumn shows an image preview or file card with Download / Replace / History instead of the text editor. Replace is a binary propose at the current head; a stale one surfaces the conflict-or-replace contract as a reload-and-retry toast. Core client gains uploadBlob/fetchBlob (sha256 header, 401-refresh retry, redirect-following); new spaces:uploadBlob / spaces:saveBlob IPC — pastes cross as bytes, picked/dropped files cross as paths so 100MB never rides IPC. Round-trip covered in client.test.ts against the real stub; blob-link rewrite helpers tested in spaces-presentation.test.ts. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(harbor): blob variant on MCP propose_change — agents file uploaded attachments by hash The agent face gains the same one-of newContent/blob the render face has, so "put that chat attachment in the space files" is a pure reference: the agent reads the /b/<hash> link out of the message and proposes it at a path — no re-upload, no byte movement. One-of is enforced in dispatch (schema stays a plain JSON-schema projection); unregistered hashes are refused as before. Exercised through a real MCP client in mcp.test.ts. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> | 27 天前 | |
feat(spaces): protocol contract, Harbor server, and the apps/x client chain — v0 end to end - @rowboat/spaces-protocol: the wire contract package (v0) - Harbor: one core, three faces (HTTP/WS/MCP), the §11 gate as a test suite; in-memory and Postgres (PgStore) stores; content-addressed blob store with disk and S3 drivers (spec §6 shape) - apps/x: core client + live socket, IPC, org registry; adding an org registers its MCP agent face; agent discovery via list_spaces + the spaces skill (MCP entries derived from the registry, never mcp.json) - @rowboat in topics: per-topic sessions, receipts, presence; thread context is pull-don't-push (read_topic); working chips render at the typing-indicator position; first-message invocations trigger sessions - docs: CONTRACT.md progress; tiered auth amendment (DCR + refresh are AS policy, the org stays a pure resource server) - ci: apps/harbor test job; harbor builds before every apps/x install Co-Authored-By: Claude <noreply@anthropic.com> | 30 天前 | |
feat(spaces): protocol contract, Harbor server, and the apps/x client chain — v0 end to end - @rowboat/spaces-protocol: the wire contract package (v0) - Harbor: one core, three faces (HTTP/WS/MCP), the §11 gate as a test suite; in-memory and Postgres (PgStore) stores; content-addressed blob store with disk and S3 drivers (spec §6 shape) - apps/x: core client + live socket, IPC, org registry; adding an org registers its MCP agent face; agent discovery via list_spaces + the spaces skill (MCP entries derived from the registry, never mcp.json) - @rowboat in topics: per-topic sessions, receipts, presence; thread context is pull-don't-push (read_topic); working chips render at the typing-indicator position; first-message invocations trigger sessions - docs: CONTRACT.md progress; tiered auth amendment (DCR + refresh are AS policy, the org stays a pure resource server) - ci: apps/harbor test job; harbor builds before every apps/x install Co-Authored-By: Claude <noreply@anthropic.com> | 30 天前 |