docs: mark browser inbox domain binding complete

This commit is contained in:
mirivlad 2026-06-29 09:30:43 +08:00
parent eac67d9417
commit 2564f6ca9d
4 changed files with 202 additions and 7 deletions

View File

@ -249,8 +249,10 @@ search.provider
sidebar view and a workspace item. Workspace tabs keep their own pending queue;
the global sidebar view aggregates queues from all workspaces plus unscoped
global captures. The local receiver now has an opt-in paired mode that requires
`X-Verstak-Receiver-Token` before publishing browser capture events. Domain
binding and conversion into notes/links/files/activity are still future work.
`X-Verstak-Receiver-Token` before publishing browser capture events. Browser
Inbox stores plugin-owned `domainBindings` and routes unscoped captures with an
exact domain match into the bound workspace queue. Conversion into
notes/links/files/activity is still future work.
## 9. `official.search`

View File

@ -55,8 +55,9 @@ Known remaining gaps:
- File/image preview exists as a basic provider with bounded inline image
rendering through the public Files API.
- Browser extension repository has protocol, queue, and Chromium/Firefox build
scaffold; desktop has a local receiver and mounted-view inbox plugin, but no
pairing model, domain binding, or conversion workflow yet.
scaffold; desktop has a local receiver and mounted-view inbox plugin; receiver
pairing and basic Browser Inbox domain binding are implemented, but no
conversion workflow yet.
- Packaging/update/release workflow is not product-grade yet.
## 4. Implementation Phases
@ -163,7 +164,7 @@ Tasks:
- [x] implement browser extension capture scaffold for URL, selected text,
page title, and link captures;
- [x] define local receiver permission/pairing model;
- add domain-to-workspace binding;
- [x] add domain-to-workspace binding;
- convert inbox entries into notes/links/files/activity events through public
plugin APIs.
@ -235,8 +236,7 @@ Verification:
4. [x] Notes trash/delete UX in `verstak-official-plugins`.
5. [x] Sync hardening pass with expanded real two-vault smoke.
6. [~] Browser inbox protocol design, extension scaffold, local receiver, and
minimal inbox plugin are implemented; pairing, domain binding, and conversion
workflows remain.
minimal inbox plugin are implemented; conversion workflows remain.
This order finishes generic platform surfaces before building product features
that depend on them.

View File

@ -0,0 +1,119 @@
# Browser Inbox Domain Binding Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** Route unscoped Browser Inbox captures into workspace queues through plugin-owned domain bindings.
**Architecture:** Keep domain binding in `verstak.browser-inbox`. The plugin reads `domainBindings` from its own settings namespace, annotates unscoped incoming captures before storage, and preserves desktop core as a capture-event publisher only.
**Tech Stack:** Plain JavaScript official plugin bundle, Node smoke test harness, Markdown docs.
## Global Constraints
- Do not move Browser Inbox queues or conversion workflows into desktop core.
- Do not import Notes, Files, Activity, or Journal from Browser Inbox.
- Route only captures that do not already include `workspaceRootPath`.
- Domain matching is exact and case-insensitive for this slice.
- Use TDD: write the failing smoke test first, run it red, then implement.
---
### Task 1: Browser Inbox Domain Routing
**Files:**
- Modify: `/home/mirivlad/git/verstak2/verstak-official-plugins/scripts/smoke-browser-inbox-plugin.js`
- Modify: `/home/mirivlad/git/verstak2/verstak-official-plugins/plugins/browser-inbox/frontend/src/index.js`
**Interfaces:**
- Consumes: Browser Inbox plugin settings key `domainBindings`.
- Produces: unscoped captures annotated with `workspaceRootPath` and `workspaceName` when an exact domain binding exists.
- [ ] **Step 1: Write the failing smoke test**
Add a scenario to `scripts/smoke-browser-inbox-plugin.js`:
```js
const bindingApi = makeApi({
domainBindings: {
'client.example.com': 'ClientA',
'project.example.com': 'Project'
}
});
const bindingGlobal = await mountWithApi(bindingApi, {});
await bindingApi.handlers['browser.capture.page']({
name: 'browser.capture.page',
timestamp: '2026-06-29T00:00:00Z',
payload: {
captureId: 'bound-client-capture',
capturedAt: '2026-06-29T00:00:00.000Z',
kind: 'page',
url: 'https://client.example.com/page',
title: 'Bound Client Page',
domain: 'client.example.com'
}
});
await flush();
if (bindingApi.getStoredCaptures('captures:workspace:ClientA').length !== 1) {
throw new Error('domain-bound capture was not stored under ClientA workspace key');
}
```
Also prove explicit `workspaceRootPath: "Project"` wins over a binding for
`client.example.com`.
- [ ] **Step 2: Run RED**
Run:
```bash
cd /home/mirivlad/git/verstak2/verstak-official-plugins
PATH=/tmp/verstak2-tools:/home/mirivlad/.lmstudio/.internal/utils:$PATH node scripts/smoke-browser-inbox-plugin.js
```
Expected: fails because domain-bound captures are still stored in the receiving
view scope.
- [ ] **Step 3: Implement minimal routing**
In `plugins/browser-inbox/frontend/src/index.js`, add helper functions to
normalize binding keys, derive a domain from capture fields, and annotate
captures without `workspaceRootPath` before storage.
- [ ] **Step 4: Run GREEN**
Run:
```bash
cd /home/mirivlad/git/verstak2/verstak-official-plugins
PATH=/tmp/verstak2-tools:/home/mirivlad/.lmstudio/.internal/utils:$PATH node scripts/smoke-browser-inbox-plugin.js
PATH=/tmp/verstak2-tools/venv/bin:/tmp/verstak2-tools:/home/mirivlad/.lmstudio/.internal/utils:$PATH ./scripts/check.sh
```
Expected: both commands exit 0.
### Task 2: Roadmap Documentation
**Files:**
- Modify: `/home/mirivlad/git/verstak2/verstak-docs/05_Official_Plugins.md`
- Modify: `/home/mirivlad/git/verstak2/verstak-docs/07_Full_Implementation_Roadmap.md`
**Interfaces:**
- Consumes verified plugin routing behavior.
- Produces docs that distinguish implemented domain binding from future conversion workflows.
- [ ] **Step 1: Update docs**
Mark domain binding as implemented in the Browser Inbox status text and roadmap
while leaving conversion workflow as future work.
- [ ] **Step 2: Verify docs**
Run:
```bash
cd /home/mirivlad/git/verstak2/verstak-docs
git diff --check
rg -n "domain-to-workspace binding|Domain binding|conversion" 05_Official_Plugins.md 07_Full_Implementation_Roadmap.md
```
Expected: `git diff --check` exits 0 and `rg` shows the updated status lines.

View File

@ -0,0 +1,74 @@
# Browser Inbox Domain Binding Design
## Purpose
Browser captures often arrive while the user is not focused on the workspace
that should receive them. Domain binding routes unscoped browser captures into a
workspace queue by matching the capture domain to a plugin-owned binding table.
This keeps Browser Inbox behavior in the official plugin. Desktop core still
only publishes browser capture events and may annotate the current workspace
when it knows one.
## Scope
This slice implements domain-to-workspace routing inside `verstak.browser-inbox`:
- store bindings in the Browser Inbox plugin settings namespace;
- match capture domains case-insensitively;
- route only captures that do not already include `workspaceRootPath`;
- preserve the global aggregate view;
- keep the binding model independent from Notes, Files, Activity, and Journal.
It does not implement conversion into notes/links/files/activity, browser UI for
editing bindings, or a desktop core domain binding API.
## Settings Contract
The plugin reads `domainBindings` from its settings object:
```json
{
"domainBindings": {
"example.com": "Project",
"client.example.com": "ClientA"
}
}
```
Keys are hostnames. Values are top-level `workspaceRootPath` strings.
Normalization rules:
- trim whitespace around keys and values;
- lowercase domains for matching;
- strip one or more leading dots from binding keys;
- ignore empty domains and empty workspace roots.
## Routing Rules
For each incoming `browser.capture.page`, `browser.capture.selection`, or
`browser.capture.link` event:
1. If payload already has `workspaceRootPath`, keep it unchanged.
2. Otherwise, derive a domain from `payload.domain` or `payload.url`.
3. If an exact normalized domain binding exists, set `workspaceRootPath` and
`workspaceName` to the bound workspace root before storage.
4. If no binding exists, keep current behavior and store the capture in the
receiving view scope.
Subdomain fallback is intentionally out of scope for this first slice. A
binding for `example.com` does not match `docs.example.com` until a later design
adds explicit wildcard or suffix semantics.
## Testing
`scripts/smoke-browser-inbox-plugin.js` must prove:
- an unscoped capture with `domain: "client.example.com"` and a
`domainBindings` entry for that domain is stored under
`captures:workspace:ClientA`;
- the routed capture appears in the ClientA workspace view;
- the global view still aggregates the routed capture;
- an event that already includes `workspaceRootPath: "Project"` is not
overwritten by a domain binding for another workspace.