superbee 0.0.1 → 0.1.1-pre.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. package/LICENSE +202 -0
  2. package/NOTICE +14 -0
  3. package/README.md +94 -7
  4. package/SKILL.md +453 -0
  5. package/dist/superbee.mjs +101701 -0
  6. package/package.json +38 -7
  7. package/references/recipes/claims/conventions/claim.md +39 -0
  8. package/references/recipes/claims/recipe.md +34 -0
  9. package/references/recipes/review-workflow/conventions/review-request.md +44 -0
  10. package/references/recipes/review-workflow/conventions/view.md +71 -0
  11. package/references/recipes/review-workflow/recipe.md +20 -0
  12. package/references/recipes/review-workflow/references/view-authoring-v0.md +324 -0
  13. package/references/recipes/review-workflow/views/review-workflow/reviews.html +27 -0
  14. package/references/recipes/review-workflow/views-registry/review-workflow-reviews.md +8 -0
  15. package/references/sample-bundle/concepts/index.md +4 -0
  16. package/references/sample-bundle/concepts/link-graph.md +25 -0
  17. package/references/sample-bundle/concepts/okf-alignment.md +26 -0
  18. package/references/sample-bundle/context-notes/cycle-okf-lite-vision.md +33 -0
  19. package/references/sample-bundle/context-notes/index.md +3 -0
  20. package/references/sample-bundle/index.md +8 -0
  21. package/references/sample-bundle/log.md +7 -0
  22. package/references/sample-bundle/references/index.md +3 -0
  23. package/references/sample-bundle/references/okf-spec.md +27 -0
  24. package/references/views/about.html +102 -0
  25. package/references/views/conventions/view.md +71 -0
  26. package/references/views/pulse.html +439 -0
  27. package/references/views/references/view-authoring-v0.md +324 -0
  28. package/references/views/roadmap.html +226 -0
  29. package/references/views/views-registry/about.md +12 -0
  30. package/references/views/views-registry/pulse.md +13 -0
  31. package/references/views/views-registry/roadmap.md +12 -0
package/package.json CHANGED
@@ -1,10 +1,41 @@
1
1
  {
2
2
  "name": "superbee",
3
- "version": "0.0.1",
4
- "description": "Shared agent memory. A versioned markdown knowledge bundle that lives in your repo, plus a CLI for humans and agents.",
5
- "keywords": ["agents", "agent-memory", "context", "knowledge-base", "markdown", "cli", "coordination"],
6
- "homepage": "https://getsuperbee.com",
7
- "license": "MIT",
8
- "author": "Holaxis",
9
- "files": ["README.md"]
3
+ "version": "0.1.1-pre.2",
4
+ "type": "module",
5
+ "description": "Agent-facing Superbee CLI for reading and writing local OKF knowledge bundles: context notes, docs, cross-links, and live bundle Views.",
6
+ "keywords": [
7
+ "okf",
8
+ "knowledge",
9
+ "agent",
10
+ "axi",
11
+ "cli",
12
+ "markdown",
13
+ "context-notes"
14
+ ],
15
+ "license": "Apache-2.0",
16
+ "homepage": "https://github.com/Holaxis-ai/superbee#readme",
17
+ "repository": {
18
+ "type": "git",
19
+ "url": "git+https://github.com/Holaxis-ai/superbee.git"
20
+ },
21
+ "bugs": {
22
+ "url": "https://github.com/Holaxis-ai/superbee/issues"
23
+ },
24
+ "bin": {
25
+ "superbee": "dist/superbee.mjs",
26
+ "aslite": "dist/superbee.mjs",
27
+ "agentstate-lite": "dist/superbee.mjs"
28
+ },
29
+ "files": [
30
+ "dist",
31
+ "SKILL.md",
32
+ "references",
33
+ "NOTICE"
34
+ ],
35
+ "publishConfig": {
36
+ "access": "public"
37
+ },
38
+ "engines": {
39
+ "node": ">=20"
40
+ }
10
41
  }
@@ -0,0 +1,39 @@
1
+ ---
2
+ type: Convention
3
+ title: Claim
4
+ governs: Claim
5
+ path: claims/
6
+ links:
7
+ supersedes: Claim
8
+ fields:
9
+ required:
10
+ - title
11
+ - progress_status
12
+ - reason
13
+ optional:
14
+ - artifacts
15
+ - evidence_command
16
+ - evidence_commit
17
+ values:
18
+ progress_status:
19
+ - active
20
+ - challenged
21
+ - locked
22
+ - deprecated
23
+ ---
24
+
25
+ # Claim
26
+
27
+ A data-derived finding with provenance and a verification lifecycle. The `title` IS the
28
+ claim: one sentence carrying the specific numbers. `progress_status` is the lifecycle —
29
+ `active` (filed) → `challenged` (under independent check) → `locked` (verified
30
+ end-to-end; citable in final outputs) — or `deprecated` (superseded; never deleted).
31
+ `reason` says how the number was derived or why it changed. `evidence_command` is the
32
+ exact command that reproduces the number; `evidence_commit` the commit it ran against;
33
+ `artifacts` points at supporting files.
34
+
35
+ Transitions are CAS writes: capture the head with `doc read <id> --field head_version`,
36
+ pass it as `--expected-version` — a racing writer is refused, never silently overwritten
37
+ (the ported `--parent-event-id` semantics). Locked claims change only by deprecation plus
38
+ a linked successor. Documents that cite a claim link to it; backlinks enumerate every
39
+ citation when the claim changes.
@@ -0,0 +1,34 @@
1
+ ---
2
+ type: Recipe
3
+ id: claims
4
+ title: Claims
5
+ version: 1.0.0
6
+ summary: "Declares the Claim kind: event-lifecycle claims for data-derived numbers (progress_status enum active/challenged/locked/deprecated, required provenance), composed entirely from lite primitives"
7
+ ---
8
+
9
+ # Claims
10
+
11
+ Installs the `Claim` kind convention: a single source of truth for data-derived findings,
12
+ ported from the holaxis-claims event-sourced JSONL system onto lite's native primitives.
13
+
14
+ A claim is a `type: Claim` doc under `claims/<section>/`. Its **title carries the claim
15
+ text with its specific numbers** ("759 tests pass across six workspaces" — not "the suite
16
+ is green"). Its lifecycle is the validated `progress_status` enum: `active` (filed, working
17
+ hypothesis) → `challenged` (under independent verification) → `locked` (provenance chain
18
+ verified end-to-end; only locked claims belong in final outputs) — or `deprecated` (facts
19
+ changed; never delete, file a successor and link it). `reason` records how the number was
20
+ derived or why it changed; `evidence_command` + `evidence_commit` pin the exact
21
+ reproduction.
22
+
23
+ Everything composes from existing primitives — no claims engine:
24
+ - the optimistic lock is `doc update --expected-version` (capture the token with
25
+ `doc read <id> --field head_version`);
26
+ - the event trail is doc history (native on enforced backends; git log on file bundles);
27
+ - actor attribution is the engine's own `--actor` on every write;
28
+ - **citations are links**: a document that cites a claim links to it, so `link show
29
+ <claim>` lists every citing document — the blast radius of a deprecation, precomputed.
30
+
31
+ Rules of use (the discipline the schema cannot enforce): claims are the source of truth
32
+ for numbers — a number in a document without a locked claim is unverified; weight by
33
+ progress status; verify by re-running `evidence_command`, not by re-reading the claim; a locked
34
+ claim is never edited — deprecate it and file a successor.
@@ -0,0 +1,44 @@
1
+ ---
2
+ type: Convention
3
+ title: Review Request
4
+ governs: Review Request
5
+ path: review-requests/
6
+ description: A durable request for a named human to judge a defined question against linked evidence.
7
+ links:
8
+ reviews design: Design
9
+ reviews task: Task
10
+ reviews roadmap item: Roadmap Item
11
+ link_descriptions:
12
+ reviews design: A design whose reasoning or contract is under review.
13
+ reviews task: Implementation work whose scope or result informs the judgment.
14
+ reviews roadmap item: The strategic commitment affected by the judgment.
15
+ fields:
16
+ required: [title, progress_status, reviewer, requested_by, question]
17
+ optional: [decision_summary, decided_at]
18
+ values:
19
+ progress_status: [requested, in_review, changes_requested, approved, canceled]
20
+ value_descriptions:
21
+ progress_status:
22
+ requested: Ready for the reviewer; no review is yet in progress.
23
+ in_review: The reviewer is actively considering the request.
24
+ changes_requested: Work must be addressed before another decision.
25
+ approved: The reviewer accepted the proposal; terminal for this request.
26
+ canceled: The requester withdrew the request; terminal for this request.
27
+ terminal:
28
+ progress_status: [approved, canceled]
29
+ descriptions:
30
+ title: A concise label for the review outcome being sought.
31
+ progress_status: The persisted review lifecycle state, not an activity update.
32
+ reviewer: The human expected to make the judgment; coordination metadata, not authorization.
33
+ requested_by: The human accountable for the scope and evidence supplied.
34
+ question: The exact judgment the reviewer is being asked to make.
35
+ decision_summary: A concise persisted outcome recorded after the review.
36
+ decided_at: The ISO 8601 timestamp at which the persisted decision was made.
37
+ sections: [Context, Requested decision, Acceptance criteria, Reviewer response]
38
+ ---
39
+ # Review Request
40
+
41
+ Create a Review Request when a decision must survive chat history and remain visible to humans and
42
+ agents. The named reviewer owns the response; the requester owns scope, evidence, resubmission, and
43
+ cancellation. Record responses through version-guarded, attributed updates. The View is a
44
+ projection and never becomes the decision authority.
@@ -0,0 +1,71 @@
1
+ ---
2
+ type: Convention
3
+ title: View
4
+ governs: View
5
+ path: views-registry/
6
+ fields:
7
+ required:
8
+ - title
9
+ - entry
10
+ - access
11
+ optional:
12
+ - description
13
+ - entry_version
14
+ - presentation
15
+ values:
16
+ access:
17
+ - none
18
+ - bundle-read
19
+ - bundle-propose
20
+ presentation:
21
+ - workspace
22
+ - inline
23
+ - adaptive
24
+ terminal: {}
25
+ timestamp: "2026-07-24T00:00:00.000Z"
26
+ ---
27
+ # View
28
+
29
+ A bundle-hosted UI view. A `type: View` doc is a **registry entry**: it names a self-contained
30
+ HTML blob (`entry`, a bundle-relative blob key under `views/…`) that the `superbee ui`
31
+ launcher renders in a sandboxed, opaque-origin iframe. The view reaches bundle data only through
32
+ the narrow postMessage bridge documented in the bundle's
33
+ [View authoring reference](../references/view-authoring-v0.md) — it never holds a credential.
34
+ V0 is read-only; `bundle-propose` adds only a trusted-shell-confirmed v1 scalar action.
35
+
36
+ `Page` is the legacy name for this kind, and `bridge` the legacy spelling of `access` — and
37
+ neither is read any longer: a legacy `type: Page` doc does not register, and a doc declaring
38
+ only the legacy `bridge` field resolves to `access: none`. Docs under the legacy
39
+ `pages-registry/`/`pages/` prefixes stay recognized where they are once typed `View`; the
40
+ repo's `migrate-legacy-view-names` script renames leftover legacy names in place
41
+ (`superbee status` lists them under `legacy_naming`). Author views as `type: View` with `access`.
42
+
43
+ - `title` (required) — the launcher card's heading.
44
+ - `entry` (required) — the HTML blob key, e.g. `views/roadmap.html`.
45
+ - `entry_version` (optional) — an exact content-version pin. When present, every host refuses to
46
+ launch the View if the current entry bytes no longer match it. Ordinary mutable Views may omit it.
47
+ - `description` (optional) — one line shown on the launcher card.
48
+ - `presentation` (optional) — an advisory host-layout hint: `workspace | inline | adaptive`.
49
+ It never controls whether a host may run the View and never grants authority. A host may ignore
50
+ it when its available surface differs from the author's preference.
51
+ - `access` (required) — `none | bundle-read | bundle-propose`. Required so every View is an INTENTIONAL
52
+ classification, not a silent default — an author who forgets to declare it gets a clear
53
+ authoring-time lint, not a view that quietly renders empty against a full bundle. ENFORCED by
54
+ the shell too, not just linted: absent, malformed, or any value other than exactly
55
+ `bundle-read` or `bundle-propose` is treated as `none` at runtime — fail-closed defense for a doc this convention
56
+ didn't govern (an external bundle, a hand-edited file that skipped the lint).
57
+ - `bundle-read` — a **data view**: the shell answers its bridge requests (`hello`/`query`/
58
+ `read`/`render-document`/`edges`/`subscribe`) with live bundle data. Groups under the launcher's "Dashboards".
59
+ - `bundle-propose` — an **interactive view**: includes the read surface and may propose one
60
+ declared scalar field update. Every proposal is independently validated and shown in trusted
61
+ shell chrome; only the human's Apply action authorizes a hard-CAS local write.
62
+ - `none` — a **content view**: the shell DENIES every bundle-data request. Arbitrary
63
+ self-contained HTML with zero bundle-data access — a report, a rendered design doc, a diagram.
64
+ It may still ask the shell to open another registered View. Groups under "Documents".
65
+
66
+ Both capabilities may use `open-page` (the bridge's wire verb, kept stable across the rename) to
67
+ navigate to another valid registered View. This shell action returns no target content or
68
+ metadata and grants no bundle-data capability.
69
+
70
+ Views sync, version, and attribute like any doc; the HTML bytes travel as an opaque blob via
71
+ `promote`/`pull`, never through the model context window.
@@ -0,0 +1,20 @@
1
+ ---
2
+ type: Recipe
3
+ id: review-workflow
4
+ title: Review Workflow
5
+ version: "1"
6
+ summary: Durable human review requests with a live, generic evidence View.
7
+ content_policy: definitions-only
8
+ references:
9
+ - references/view-authoring-v0.md
10
+ pages:
11
+ - registry: views-registry/review-workflow-reviews.md
12
+ entry: views/review-workflow/reviews.html
13
+ ---
14
+ # Review Workflow
15
+
16
+ Install a durable human-review operating model without installing any review instances or project
17
+ content. The `Review Request` Kind is the decision authority; the View is a live, read-only
18
+ projection. Its versioned View-authoring reference travels into the target bundle with the
19
+ definitions, so agents can inspect and extend the UI without an agent-harness skill. Agents create
20
+ and update requests through the generic Kind-aware CLI.
@@ -0,0 +1,324 @@
1
+ ---
2
+ type: Reference
3
+ title: Bundle View authoring — shared web and MCP contract
4
+ protocol: v0+v1
5
+ timestamp: "2026-07-22T00:00:00.000Z"
6
+ ---
7
+
8
+ # Bundle View authoring — shared web and MCP contract
9
+
10
+ Author **one durable View** for both local web and MCP hosts: a self-contained, responsive HTML
11
+ blob under `views/…` plus a `type: View` registry doc under `views-registry/…`. Both hosts launch
12
+ the same registry id, exact HTML bytes, access declaration, and bridge contract. The host chooses
13
+ the available size and may offer expansion; do not create separate inline, expanded, web, or MCP
14
+ implementations.
15
+
16
+ Use the bridge for bundle data and `render-document` for canonical Markdown presentation. Style
17
+ the returned inert fragment inside the View; do not ship another Markdown parser. The sections
18
+ below are the exact protocol contract and copy-paste client. This contract travels with portable
19
+ View-bearing recipes, so authoring does not depend on an agent-harness skill.
20
+
21
+ Legacy `Page` and `bridge` are retired authoring names. Use `type: View` and `access`; `superbee status`
22
+ reports legacy content that needs migration. Legacy wire names such as `open-page` remain stable.
23
+
24
+ ## Trust model (exact-byte approval + no credential)
25
+
26
+ The `ui` server serves two privilege tiers on one loopback origin:
27
+
28
+ - **Data API** (`/v0/*`): reachable ONLY with the shell's per-run session token/cookie.
29
+ - **View bytes** (`/__page/<nonce>`): a view's static HTML, served for a short-lived **nonce**
30
+ the session-authed shell mints (`POST /__page/mint`) for that view's one blob key. The nonce
31
+ is not the session token, so it is rejected by every data route; the session token does not
32
+ open the page route to arbitrary keys.
33
+
34
+ The iframe is `sandbox="allow-scripts"` with **no** `allow-same-origin`, so the view runs at an
35
+ **opaque origin**. A strict per-view CSP (`connect-src 'none'`) blocks ordinary direct network APIs
36
+ such as fetch, XHR, WebSocket, and EventSource, while the View never receives the shell's credential
37
+ or a data endpoint. Those controls are defense-in-depth: a data-bearing View is executable code,
38
+ and approving its exact bytes and declared access is the decision to trust that code. Approve only
39
+ a View whose source or author you trust; changed bytes or expanded access ask again. The supported
40
+ bundle-data channel is `postMessage` to the shell, whose stable v0 bridge is read-only. A View that
41
+ declares `bundle-propose` may additionally ask trusted shell chrome to prepare one v1 scalar-field
42
+ action; only the human's shell-native Apply choice authorizes the CAS write.
43
+
44
+ ## Message shapes
45
+
46
+ Every message carries `bridge: "v0"`. A request carries an `id`; the reply echoes it as
47
+ `"<type>:result"` (or `"error"`). The shell drops any message whose `event.source` is not the
48
+ view's own iframe; the view drops any message whose `event.source` is not `window.parent`.
49
+
50
+ ### View → shell (requests)
51
+
52
+ | type | payload | reply `result` |
53
+ | ----------- | ---------------------------------------------------------- | ---------------------------------------------------------- |
54
+ | `hello` | — | `{ bundle: { root, name }, mode, protocol: "v0", grant }` |
55
+ | `query` | `{ params: { type?, prefix?, field?, open?, limit? } }` | `{ rows: DocHead[], count }` |
56
+ | `read` | `{ docId }` | `{ id, frontmatter, body }` |
57
+ | `render-document` | `{ docId }` | `{ document: { id, version }, html, bounded }` |
58
+ | `edges` | `{ params: { from?, to?, text? } }` | `{ edges: { from, to, text }[], count }` |
59
+ | `subscribe` | — | `{ ok: true }`, then a stream of `change` events |
60
+ | `open-page` | `{ pageId: "views-registry/…" }` | none; fire-and-forget shell navigation |
61
+
62
+ `hello.result.grant` is `"read"` for `bundle-read` and `"propose"` for `bundle-propose`.
63
+
64
+ `open-page` is the sole capability-independent action: `access: none`, `access: bundle-read`, and
65
+ `access: bundle-propose` Views may ask the shell to open another usable registered View. The shell
66
+ accepts only a conservative `views-registry/…` (or legacy-location `pages-registry/…`) concept
67
+ id, validates that it resolves to a `type: View` doc with a safe `views/…`
68
+ (or legacy-location `pages/…`) entry, and mounts the target normally with its own sandbox, nonce, and
69
+ bridge capability. It returns no target body, frontmatter, entry, HTML, or nonce. A failed
70
+ attempt can reveal that one caller-supplied registry id is not usable; this bounded existence
71
+ oracle is the only information exposed by navigation.
72
+
73
+ `DocHead` is `{ id, version, frontmatter }` — the same **head projection** `list` uses (full
74
+ frontmatter, never a body). `query` params:
75
+
76
+ `render-document` reads the canonical document and serializes its body with the shell's shared,
77
+ bounded Markdown renderer. The returned `html` is inert semantic markup: it contains no scripts,
78
+ event handlers, forms, controls, images, or navigable anchors. Internal concept links become
79
+ passive elements carrying `data-aslite-doc-id`; the View may delegate clicks on those markers to
80
+ its own selection logic and issue another `render-document` request. Insert only the unmodified
81
+ `html` returned by this request. The accompanying document `version` is the exact version rendered;
82
+ after a matching `change` event, refetch instead of treating old HTML as current. `bounded: true`
83
+ means renderer safety limits truncated or collapsed part of the input.
84
+
85
+ - `type` / `prefix` — server-side facets (a bundle-relative id prefix, a frontmatter `type`).
86
+ - `field` — a client-side `key=value` filter; comma-separated values are OR (`progress_status=todo,blocked`).
87
+ Scalar and array-valued fields use the same string-coerced membership rule as CLI `list`.
88
+ - `open` — drop terminal rows, derived from the BUNDLE'S OWN kind conventions exactly like
89
+ `list --open`: a row is dropped iff the convention governing its `type` declares the row's
90
+ current field value(s) terminal (`fields.terminal`, e.g. the Task kind's `done`/`canceled`).
91
+ A row with no governing kind is kept; a bundle where no kind declares a terminal set filters
92
+ nothing. (The shell loads the registry once per change from the server, which builds it with
93
+ core's `loadKinds` — one registry, no bridge-side schema.)
94
+ - `limit` — a positive number caps `rows`; `0` or absence is unlimited. `count` remains the total
95
+ matched after `field`/`open` filtering and before the cap, matching CLI `list`.
96
+
97
+ `edges` is the general graph query — the ONE primitive every edge-shaped question reduces to
98
+ (the same `queryEdges` atom `link list` is a CLI face over). `params`:
99
+
100
+ - `from` / `to` — each one exact nonblank concept id, a bundle-relative `prefix/` (trailing
101
+ slash), or an array of 1–32 such strings (union within the facet; giving both ANDs them). Omit
102
+ the property for "no restriction". Supplied empty/all-whitespace strings, empty arrays,
103
+ blank/non-string array entries, and arrays above 32 are invalid. Every string is preserved
104
+ byte-for-byte and may be at most 1,024 UTF-8 bytes; duplicate entries still count toward 32.
105
+ - `text` — one exact nonblank link-display string (never substring/regex), preserved byte-for-byte
106
+ and at most 1,024 UTF-8 bytes; an empty/all-whitespace value is invalid.
107
+
108
+ Backlinks are `edges({ to: docId })`; a container's contents are `edges({ from: itemId, text:
109
+ "contains" })` (or whatever link text a bundle's convention uses) — there is no separate
110
+ backlinks-only bridge call. A source linking to the same target twice with different text yields
111
+ two rows (no dedup), matching `queryEdges`'s own granularity.
112
+
113
+ ### Shell → view (server-initiated)
114
+
115
+ | type | payload |
116
+ | -------- | --------------------------------------------------- |
117
+ | `change` | `{ event: { changes: [{ id, version }], removed: [id] } }` |
118
+
119
+ `change` is pushed only to a view that has `subscribe`d. It is a **delta signal** — refetch with
120
+ `query` against it rather than trusting it as a full state. Removed ids have been deleted.
121
+
122
+ For live data views, prefer `Bridge.watch(refresh)` over assembling the startup sequence yourself.
123
+ It subscribes before the first snapshot, passes an ordered batch of raw `change` event payloads to
124
+ each refresh, and never overlaps refresh calls. Events arriving during a refresh are coalesced into
125
+ one follow-up batch. A failed refresh does not poison later event-driven refreshes; `watch` does not
126
+ retry on a timer. Its returned Promise covers subscription plus the first refresh, so handle that
127
+ Promise to surface startup failures. Raw `subscribe` remains available when a view needs the
128
+ lower-level event stream.
129
+
130
+ **There are no mutation messages in v0.** Read-only is enforced *by construction*: the shell
131
+ defines no write/delete/update handler, so any such request returns an `error` reply.
132
+
133
+ ### Trusted action bridge v1
134
+
135
+ `access: bundle-propose` includes the v0 read surface and adds two exact v1 requests:
136
+
137
+ - `{ bridge: "v1", type: "read-versioned", id, docId }` returns one canonical document and the
138
+ version from the same read.
139
+ - `{ bridge: "v1", type: "action.propose", requestId, action: { kind:
140
+ "document.set-field", docId, field, value, expectedVersion } }` proposes changing one declared
141
+ scalar field on an existing governed document.
142
+
143
+ The shell independently re-reads the View registry, exact HTML version, target document, and Kind;
144
+ shows canonical before/after values outside the iframe; and commits only after the human chooses
145
+ Apply. The approval token and immutable launch identity never enter the iframe. A stale target is a
146
+ visible conflict and is never retried behind the human's back. V1 is local `--dir` only and excludes
147
+ body writes, links, creation, deletion, remote writes, and persistent grants. Start the shell with
148
+ `superbee ui --actor <name>` (or set `SUPERBEE_ACTOR`; `AGENTSTATE_LITE_ACTOR` remains a
149
+ supported compatibility input) to enable proposals.
150
+
151
+ ## Live updates
152
+
153
+ The shell (only) holds one `EventSource('/events')`. The server watches the bundle — `fs.watch`
154
+ in `--dir` mode, a poll in `--remote` mode — diffs content-addressed **version tokens**, and pushes
155
+ a change delta. The shell fans doc changes into subscribed views as `change` events, and
156
+ **hot-reloads** a view's iframe (with a fresh nonce) when the view's own HTML blob changes. (Remote
157
+ view-blob hot-reload is a labeled follow-up; live doc updates work in both modes.)
158
+
159
+ ## `access` — the enforced data/content split
160
+
161
+ The registry doc's `access` field decides whether the shell will answer THIS view's bridge
162
+ requests at all — and the shell, not the view, is what enforces it:
163
+
164
+ - `access: bundle-read` — a **data view**. The shell answers `hello`/`query`/`read`/
165
+ `render-document`/`edges`/`subscribe` as described above.
166
+ - `access: bundle-propose` — an **interactive view**. It receives the same read surface and may
167
+ submit the narrow v1 proposal above. Each proposal still requires trusted-shell confirmation.
168
+ - `access: none` — a **content view**. The shell replies to every bundle-data request with a
169
+ `FORBIDDEN` error, before touching any bundle data. It may still use `open-page` navigation.
170
+ - `bridge` is the legacy spelling of this field, and it is no longer read: a doc declaring only
171
+ the legacy `bridge` field resolves to `access: none` (every bundle-data request is denied).
172
+ The repo's `migrate-legacy-view-names` script renames leftover legacy `bridge` fields to
173
+ `access` in place, and `superbee status` lists them under its `legacy_naming` finding.
174
+ Authoring uses `access`.
175
+ - The `View` convention declares `access` REQUIRED — every view is an intentional
176
+ classification, not a silent default. At runtime the shell still fails closed for a doc this
177
+ convention didn't govern (an external bundle, a hand-edited file that skipped the lint): absent,
178
+ malformed, or any other value is treated as `access: none`. A view only gets bundle access by
179
+ declaring exactly `bundle-read` or `bundle-propose`.
180
+
181
+ The launcher groups views by this same field: "Dashboards" for `bundle-read`, "Interactive" for
182
+ `bundle-propose`, and "Documents" for `none`.
183
+
184
+ ## Authoring a view
185
+
186
+ Start from a working installed View when possible, then adapt it responsively for the space the
187
+ host provides. Keep data selection bounded and show empty, partial, over-limit, and unavailable
188
+ states.
189
+
190
+ ```sh
191
+ superbee blobs --prefix views/
192
+ superbee pull --doc-key views/review-workflow/reviews.html --out my-view.html
193
+ ```
194
+
195
+ Keep HTML, CSS, and JavaScript self-contained with no external hosts. A data View embeds the bridge
196
+ client below. A content View (`access: none`) may use only `openPage`; bundle-data calls return
197
+ `FORBIDDEN`.
198
+
199
+ Install the HTML blob and its registry entry:
200
+
201
+ ```sh
202
+ superbee promote my-view.html --doc-key views/my-view.html
203
+ superbee new "View" my-view \
204
+ --title "My view" \
205
+ --entry views/my-view.html \
206
+ --access bundle-read \
207
+ --description "A live view of this bundle."
208
+ superbee ui --open
209
+ ```
210
+
211
+ `new "View" my-view` applies the View Kind's declared `views-registry/` path. Use `access: none`
212
+ for a static report or diagram. Re-promoting the HTML updates the open View; the shell reloads it
213
+ with a fresh nonce. If the bundle does not yet declare the View Kind, install its View-bearing
214
+ recipe or promote the supplied `conventions/view.md` once before creating the registry entry.
215
+
216
+ Verify the same registered id in both surfaces: open it with `superbee ui`, then have an MCP-capable
217
+ desktop list and show that View. Confirm narrow and expanded layouts without changing the source.
218
+
219
+ The seed views here are working examples: `pulse.html`/`roadmap.html` are `access: bundle-read`
220
+ data views — `roadmap.html` is the one that exercises the `edges` request end-to-end (a live graph
221
+ view of Roadmap Items and the tasks each one `contains`) — and `about.html` is an `access: none`
222
+ content view (no bridge calls at all). `demo.sh` (repo only) wires all of this over a scratch copy
223
+ of this repo's own board.
224
+
225
+ ## The bridge client (embedded copy)
226
+
227
+ ```js
228
+ (function () {
229
+ var PROTO = "v0", seq = 0, pending = {}, subs = [];
230
+ function send(type, extra) {
231
+ return new Promise(function (resolve, reject) {
232
+ var id = String(++seq);
233
+ pending[id] = { resolve: resolve, reject: reject };
234
+ var msg = { bridge: PROTO, id: id, type: type };
235
+ if (extra) for (var k in extra) msg[k] = extra[k];
236
+ parent.postMessage(msg, "*"); // parent origin is opaque to us; the shell validates by source
237
+ });
238
+ }
239
+ // A shell action, deliberately separate from send(): the source frame may unload immediately,
240
+ // so openPage is void/fire-and-forget and must not be awaited.
241
+ function openPage(pageId) {
242
+ parent.postMessage({ bridge: PROTO, type: "open-page", pageId: pageId }, "*");
243
+ }
244
+ function watch(refresh) {
245
+ if (typeof refresh !== "function") return Promise.reject(new TypeError("Bridge.watch requires a refresh function"));
246
+ var active = true, ready = false, running = false, queued = [];
247
+ function schedule(initial) {
248
+ running = true;
249
+ var batch = queued.splice(0);
250
+ return Promise.resolve().then(function () { return refresh(batch); }).then(function (value) {
251
+ running = false;
252
+ if (queued.length) void schedule(false);
253
+ return value;
254
+ }, function (err) {
255
+ running = false;
256
+ if (queued.length) void schedule(false);
257
+ if (initial) throw err;
258
+ console.error("Bridge.watch refresh failed", err);
259
+ });
260
+ }
261
+ function onChange(event) {
262
+ if (!active) return;
263
+ queued.push(event);
264
+ if (ready && !running) void schedule(false);
265
+ }
266
+ return window.Bridge.subscribe(onChange).then(function () {
267
+ ready = true;
268
+ return schedule(true);
269
+ }, function (err) {
270
+ active = false;
271
+ throw err;
272
+ });
273
+ }
274
+ window.addEventListener("message", function (e) {
275
+ if (e.source !== window.parent) return; // only trust the shell
276
+ var m = e.data;
277
+ if (!m || m.bridge !== PROTO) return;
278
+ if (m.type === "change") { subs.forEach(function (cb) { cb(m.event); }); return; }
279
+ var p = pending[m.id];
280
+ if (!p) return;
281
+ delete pending[m.id];
282
+ if (m.type === "error") p.reject(new Error((m.error && m.error.message) || "bridge error"));
283
+ else p.resolve(m.result);
284
+ });
285
+ window.Bridge = {
286
+ hello: function () { return send("hello"); },
287
+ query: function (params) { return send("query", { params: params }); },
288
+ read: function (docId) { return send("read", { docId: docId }); },
289
+ renderDocument: function (docId) { return send("render-document", { docId: docId }); },
290
+ edges: function (params) { return send("edges", { params: params }); },
291
+ openPage: openPage,
292
+ subscribe: function (cb) { subs.push(cb); return send("subscribe"); },
293
+ watch: watch
294
+ };
295
+ })();
296
+ ```
297
+
298
+ Compose and style the trusted fragment inside the View; do not rewrite or concatenate its HTML:
299
+
300
+ ```js
301
+ async function showDocument(docId) {
302
+ var rendered = await Bridge.renderDocument(docId);
303
+ documentPanel.innerHTML = rendered.html;
304
+ }
305
+ documentPanel.addEventListener("click", function (event) {
306
+ var target = event.target instanceof Element ? event.target.closest("[data-aslite-doc-id]") : null;
307
+ if (target) void showDocument(target.getAttribute("data-aslite-doc-id"));
308
+ });
309
+ ```
310
+
311
+ ```css
312
+ .document-panel [data-aslite-rendered-document] { line-height: 1.6; }
313
+ .document-panel h1 { font: 600 1.5rem/1.2 system-ui; }
314
+ .document-panel [data-aslite-doc-id] { cursor: pointer; text-decoration: underline; }
315
+ ```
316
+
317
+ A live view supplies only its domain snapshot and render work:
318
+
319
+ ```js
320
+ Bridge.watch(async function (events) {
321
+ var result = await Bridge.query({ type: "Task" });
322
+ render(result.rows, events);
323
+ }).catch(showStartupError);
324
+ ```
@@ -0,0 +1,27 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta name="viewport" content="width=device-width,initial-scale=1">
6
+ <title>Review requests</title>
7
+ <style>
8
+ :root{color-scheme:light dark;--paper:#fffefa;--ink:#19212b;--muted:#68727d;--line:#d9d6ce;--navy:#173b57;--soft:#e5f0f5}*{box-sizing:border-box}body{margin:0;background:#f4f2ed;color:var(--ink);font:15px/1.5 system-ui,sans-serif}header{padding:24px max(20px,5vw);background:var(--navy);color:white}h1{margin:0;font:500 32px Georgia,serif}header p{margin:5px 0 0;opacity:.75}main{max-width:1000px;margin:auto;padding:28px max(20px,5vw)}.card{background:var(--paper);border:1px solid var(--line);border-radius:9px;padding:20px;margin:0 0 14px}.top{display:flex;justify-content:space-between;gap:16px;align-items:flex-start}.title{font:500 23px Georgia,serif}.status{font:700 11px ui-monospace,monospace;text-transform:uppercase;background:var(--soft);padding:5px 9px;border-radius:999px}.meta{color:var(--muted);font-size:13px;margin-top:8px}.question{font:500 18px Georgia,serif;margin-top:16px}.empty,.error{text-align:center;color:var(--muted);padding:15vh 10px}.error{color:#a8463d}@media(prefers-color-scheme:dark){:root{--paper:#17212a;--ink:#edf1f4;--muted:#a3afb9;--line:#34414c;--soft:#203846}body{background:#111820}}
9
+ </style>
10
+ </head>
11
+ <body>
12
+ <header><h1>Review requests</h1><p id="state">Connecting to this bundle…</p></header>
13
+ <main id="app"><div class="empty">Subscribing before reading…</div></main>
14
+ <script>
15
+ (function(){
16
+ "use strict";
17
+ var seq=0,pending=Object.create(null),listeners=[],app=document.getElementById("app"),state=document.getElementById("state");
18
+ function request(type,extra){return new Promise(function(resolve,reject){var id=String(++seq),message={bridge:"v0",id:id,type:type};pending[id]={resolve:resolve,reject:reject};Object.assign(message,extra||{});parent.postMessage(message,"*")})}
19
+ window.addEventListener("message",function(event){if(event.source!==parent)return;var message=event.data;if(!message||message.bridge!=="v0")return;if(message.type==="change"){listeners.slice().forEach(function(listener){listener()});return}var item=pending[message.id];if(!item)return;delete pending[message.id];if(message.type==="error")item.reject(new Error(message.error&&message.error.message||"Bridge error"));else item.resolve(message.result)});
20
+ function esc(value){return String(value==null?"":value).replace(/[&<>"']/g,function(char){return{"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;"}[char]})}
21
+ function reload(){state.textContent="Refreshing…";return request("query",{params:{type:"Review Request"}}).then(function(result){var rows=(result.rows||[]).slice().sort(function(a,b){return String(b.frontmatter.timestamp||"").localeCompare(String(a.frontmatter.timestamp||""))});state.textContent=rows.length+" durable request"+(rows.length===1?"":"s")+" · live";if(!rows.length){app.innerHTML='<div class="empty">No Review Requests yet. This view will update when one is created.</div>';return}app.innerHTML=rows.map(function(row){var value=row.frontmatter||{},progress=value.progress_status;return '<article class="card"><div class="top"><div><div class="title">'+esc(value.title||row.id)+'</div><div class="meta">Reviewer: '+esc(value.reviewer||"not named")+' · Requested by: '+esc(value.requested_by||"not named")+'</div></div><span class="status">'+esc(progress||"unknown")+'</span></div><div class="question">'+esc(value.question||"No review question supplied")+'</div></article>'}).join("")}).catch(function(error){state.textContent="Read failed";app.innerHTML='<div class="error">'+esc(error.message)+'</div>'})}
22
+ listeners.push(reload);
23
+ request("subscribe").then(reload).catch(function(error){state.textContent="Subscription failed";app.innerHTML='<div class="error">'+esc(error.message)+'</div>'});
24
+ })();
25
+ </script>
26
+ </body>
27
+ </html>
@@ -0,0 +1,8 @@
1
+ ---
2
+ type: View
3
+ title: Review requests
4
+ entry: views/review-workflow/reviews.html
5
+ description: Live, generic review handoffs with their dynamically linked evidence.
6
+ access: bundle-read
7
+ ---
8
+ A read-only projection of every Review Request in this bundle.
@@ -0,0 +1,4 @@
1
+ # Concept
2
+
3
+ * [OKF Alignment](okf-alignment.md) - How Superbee maps agent knowledge onto Open Knowledge Format primitives.
4
+ * [The Link Graph](link-graph.md) - How standard markdown links become an untyped directed graph with derived backlinks.
@@ -0,0 +1,25 @@
1
+ ---
2
+ type: Concept
3
+ title: The Link Graph
4
+ description: How standard markdown links become an untyped directed graph with derived backlinks.
5
+ tags: [okf, links, graph]
6
+ timestamp: 2026-07-01T12:05:00Z
7
+ ---
8
+ # Summary
9
+
10
+ OKF cross-links are ordinary markdown links `[text](target)` — never wikilinks.
11
+ A link `A -> B` is an *untyped* directed edge; the relationship kind lives in the
12
+ surrounding prose, not in a typed-edge slot. Backlinks are **derived**, not
13
+ stored: a consumer reverses the resolved edge set to compute "Cited by".
14
+
15
+ # Example
16
+
17
+ This concept explains the model that [OKF Alignment](./okf-alignment.md) depends
18
+ on, so the alignment note links here and this note links back — a two-way edge
19
+ the visualizer draws once and the backlink index reflects on both ends. The
20
+ governing rules are defined in the
21
+ [Open Knowledge Format v0.1 (Draft)](../references/okf-spec.md), §5.
22
+
23
+ # Citations
24
+
25
+ 1. [Open Knowledge Format v0.1 (Draft)](../references/okf-spec.md)