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.
- package/LICENSE +202 -0
- package/NOTICE +14 -0
- package/README.md +94 -7
- package/SKILL.md +453 -0
- package/dist/superbee.mjs +101701 -0
- package/package.json +38 -7
- package/references/recipes/claims/conventions/claim.md +39 -0
- package/references/recipes/claims/recipe.md +34 -0
- package/references/recipes/review-workflow/conventions/review-request.md +44 -0
- package/references/recipes/review-workflow/conventions/view.md +71 -0
- package/references/recipes/review-workflow/recipe.md +20 -0
- package/references/recipes/review-workflow/references/view-authoring-v0.md +324 -0
- package/references/recipes/review-workflow/views/review-workflow/reviews.html +27 -0
- package/references/recipes/review-workflow/views-registry/review-workflow-reviews.md +8 -0
- package/references/sample-bundle/concepts/index.md +4 -0
- package/references/sample-bundle/concepts/link-graph.md +25 -0
- package/references/sample-bundle/concepts/okf-alignment.md +26 -0
- package/references/sample-bundle/context-notes/cycle-okf-lite-vision.md +33 -0
- package/references/sample-bundle/context-notes/index.md +3 -0
- package/references/sample-bundle/index.md +8 -0
- package/references/sample-bundle/log.md +7 -0
- package/references/sample-bundle/references/index.md +3 -0
- package/references/sample-bundle/references/okf-spec.md +27 -0
- package/references/views/about.html +102 -0
- package/references/views/conventions/view.md +71 -0
- package/references/views/pulse.html +439 -0
- package/references/views/references/view-authoring-v0.md +324 -0
- package/references/views/roadmap.html +226 -0
- package/references/views/views-registry/about.md +12 -0
- package/references/views/views-registry/pulse.md +13 -0
- 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.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
"
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
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{"&":"&","<":"<",">":">",'"':""","'":"'"}[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,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)
|