能够回应的 .bl.org 档案
一份档案就是一个 .bl.org 文件。它既是源代码,也是页面、可执行规范和答案收集表。一个轻量级的 Beam Lisp 服务器(即位于本文件旁边的工具包)会使用真正的 bl.doc.spans 分区对其进行渲染,发布在 tailnet 上,并将读者给出的每一个答案写回到它对应的块中。无需数据库,无需第二份副本:git diff 会显示 Othman 说过什么。
示例文件,在编写新档案前请先阅读:
~/projects/tiny-tools/mobile-app/dossier/pocket-harness.bl.org(2026-10-31 从 baton.bl.org 重命名而来)。
一个包含桌面端布局和可执行生命周期的桌面端示例:
~/projects/knowledger-interface-review/docs/reports/interface-review-20261031/knowledger-interface.bl.org。
它继承的家风品味源头:~/code/undefine/omega/spikes/mobile/design-review/design-review.bl.org。
工作循环
- 先调研。将每一个论断都锚定在一个文件路径、一条你运行过的命令或一个引用来源上。
- 编写
NAME.bl.org(结构见下文)。代码单元不要手动编写#+RESULTS——让bl doc run来写。 - 依次执行
bl doc run FILE.bl.org和bl doc run FILE.bl.org --check(退出码 0 表示所有单元都是绿色且结果是最新的)。 - 在手机宽度下查看结果后再下结论(见查看它)。
- 发布:
环境文件保存work serve NAME --http auto --cwd ~/.claude/skills/bl-org-dossier/kit \ --env-file ~/.config/baton-dossier/service.env --ready-timeout 120 \ -- /etc/profiles/per-user/othman/bin/bl serve serve.bl -- /abs/path/NAME.bl.orgDOSSIER_OWNER=(所有者的 Tailscale 登录名);只有该身份可以回答问题(Serve 会设置Tailscale-User-Login;已验证于 2026-10-31)。DOSSIER_HANDLE=timinou是答案在:by中携带的名称;登录名(电子邮件)永远不会写入文件。 服务会根据每个请求重新读取文件:编辑.bl.org无需重启。 编辑工具包的.bl代码则需要重启:work restart NAME。 - 读取答案:
答案位于块头部:bl run ~/.claude/skills/bl-org-dossier/kit/answers.bl -- /abs/path/NAME.bl.org:answer B :at "2026-10-31 17:14" :note "…"。 根据答案采取行动,更新档案(将某个决策的:status改为decided,将某个任务标题移至 DONE),并说明更改了什么。
结构(Othman 的品味)
开头提出论点,结尾给出他必须做出的决策。中间部分,赢得每一节。
#+title:命名事物及其承诺。#+subtitle:一句话。#+kicker:说明所处位置。#+status:诚实描述(提案 · 尚未构建)。- 一句话概括 ——
lede+stats。两段简短的段落,四个数字。 - 定义词语 —— 为你将要倚重的每个名词定义
terms。如果像“无缝”这样的短语承载了请求,请将其拆分为它隐藏的各个独立承诺,并说明它绝不意味着什么(honest)。 - 任务 —— 一个
job对应一个使用时刻,每个都包含参考产品的方式和一个决策。 - 屏幕 ——
screen块,通过文本绘制。使用真实名称、真实仓库、来自实际环境的真实机器——永远不要使用 lorem ipsum。 - 实际工作方式 —— 任何跨越线路连接的内容使用
sequence;拓扑结构使用graph;跨多个系统的一个概念使用rosetta。 - 可执行模型 —— 包含
assert的 Beam Lisp 单元,用于固定设计的不变量。每个单元下打印的结果就是证明。 - 现有技术 ——
matrix,列出来源,标注来自文档的第三方声明。 - 决策(
decision,显示被拒绝的选项)→ 问题(ask)→ 任务列表(包含:PROPERTIES::JOBS: :AREA: :PROTO:的* TODO/DONE标题)。 - 文案简短。位置和权重承载状态。没有营销形容词。说明哪些尚未验证。
组件
头部属性::key value 或 :key "quoted value"。主体是行语言;除非特别说明,否则为 a | b | c 行。
| 块 | 主体 | 渲染效果 |
|---|---|---|
lede | 散文 | 大号衬线体开场 |
stats | value | label | note | 统计图块 |
terms | term | meaning | 定义列表 |
principles | title | text | 编号卡片 |
note / warning / honest(:title) | 散文 | 标注框(石板色/陶土色/赭色) |
rosetta | 首行为表头 | 对齐表格;在手机上显示为卡片 |
matrix | 首行为表头;单元格 y、~、n 加可选文字 | 带图例的点阵图 |
job :id :title :codex :claude :decision :screen | JTBD 句子 | 任务卡片 + 答案 |
decision :id :title :status proposed|decided | - [x] chosen、- [ ] rejected,然后是依据 | 决策卡片 + 同意/重做/不同意 |
ask :id :title :options "A|B|C" | 背景信息 | 问题卡片 + 选项 |
screen :id :title :job :caption [:theme dark] | 屏幕语言(见下文) | 手机模型 + 反应;连续的屏幕形成可滑动的条带 |
desk :id :title :job :caption [:url] | 桌面语言(见下文) | 桌面应用框架 + 反应;与屏幕相同的答案按钮 |
sequence :title | lanes a · sub | b · sub、a -> b : label(调用)、a --> b : label(回复)、a => b : label(关键时刻)、note a, b : text、== divider | 宽屏上为 SVG,手机上为编号步骤 |
graph :title | node id | label | sub | col,row | tone(you door station builder foreign)、edge a -> b | label | style(baton solid dash none;也可使用 <->、--) | SVG 拓扑图 |
schema :id :title | 未缩进的 entity @icon | gloss;缩进的 attr type | note。类型:text date、-> kind(引用)、->> kind(多对多)、a > b / c(状态流)、a, b, c(枚举) | 实体卡片,引用链接到 #entity-NAME,显示实体·属性·状态的数量;可回答 |
procedure :id :title :makes | step @icon Title | who | name · kind, …(产出物;第三个 · draft 表示工具包在任务开始时草拟它)、check @icon Title | who | name, …(验证者的建议,可以是代理或人)、gate @icon Title | who | name, …(等待交接);who 指定角色;其他行为散文 | 步骤轨道,检查项为虚线,门控项为菱形;可回答 |
一个 schema 或 procedure 块也是源代码,可作为单元的编译输入:bl doc run
将文件传递给 (BeamLisp/argv),因此单元可以读取自己的块并将其转换为
datom 属性或任务(示例:team-machine/design/work/WORK.bl.org,单元 spine、procedure)。
屏幕语言,每个动词一行:bar Title | right(< Title 添加返回按钮)、sub、chips a* | b(* = 选中)、
section Label | count、card tone | title | meta | body(色调 turn approve run done fail idle)、
ask、actions Primary! | Plain | Quiet? | Danger^、msg you|agent | text、tool label | detail、
diff +12 −3 | path、code text、strip tone | text | button、row label | value、banner warn|ok | text、
dest icon | title | body、note、spacer、input placeholder | ↑、tabs A* | B、
lock time | date + notif app | title | body | Action, Action(配合 :theme dark)、
sheet Title——其后所有内容都将置于一个覆盖在屏幕上的底部表单中。
card tone | title | meta | body | @check! @x @next?——第五列将纯图标操作放在卡片上
(在卡片中说明需要什么,将操作保留为图标;Othman 觉得按钮行难以解析)、
machine tone | @server name | role | detail | right(色调 up busy asleep you)、
search placeholder + pick @folder name* | detail | right(项目选择器,* = 已选)、
listen hint(麦克风 + 波形)、heard transcript、intent @send Verb | target | text(语音)、
choices A | B* | C(多项选择问题,* = 已选)、reply placeholder(带麦克风和发送按钮的自由文本回答)。
pick 和 machine 上的侧边事实会流入文本;卡片操作位于文字下方。
桌面语言——所有屏幕动词,加上桌面区域和一个文档:
nav @home Accueil* | @matter Affaires(侧边栏,* = 当前位置)、crumb a › b、
head Title | tone · Status | Action! | Other?(右上角的操作槽位)、meta a | b、tabs、
col doc|rail|main(列;doc 绘制纸张;重复 col main 形成一行卡片)、
page Title、para text 配合 {+inserted+} {-deleted-} {~proposed~} {!to verify!} ‹P-01›(来源标签)^1(评论锚点)、
point tone | @icon kind | title | body | Option* | Option、comment 1 | who | text(与 ^1 相同的数字)、opts A* | B。
在第一个 col 之前的行会横跨整个宽度。桌面端使用 Knowledger 的 Warm Signal 调色板(暗色模式下为灯盏)。
图标:屏幕或桌面行中任何位置的 @name 都会绘制一个线条图标——home matter library playbook people audit settings search plus send file word check x alert clock user bot chat comment link seal eye edit version diff filter more back next down up bell wait calendar scale flag play undo lock attach grid list board split spark。对于事物的种类,优先使用图标而非文字。
可回答的块:ask decision screen desk job schema procedure。每个都需要一个唯一的 :id。
纯 Org 模式同样有效:带有 TODO 关键字和标签的标题、:PROPERTIES: 抽屉(标签块)、
表格、列表、[ ]/[X] 复选框、=code=、*bold*、/italic/、[[url][label]]、[[#screen-ID][label]]。
可执行单元
#+begin_src beam-lisp :id lifecycle
(ns project.model) ; 第一个单元命名命名空间;所有单元共享它
(defn advance [s e] …)
(assert (= … …)) ; 失败的断言会成为结果,并且 --check 会退出码为 1
{:what "最后一个值是打印出的证明"}
#+end_src
仅使用小写 #+begin_src beam-lisp(大写永远不会运行)。对设计的
不变量进行建模(如唯一写入者、效果顺序、映射的覆盖率),而非琐碎的细节。
编写这些时遇到的 Beam Lisp 陷阱(已安装 bl,2026-10-31):
不在核心中的——ffirst、reductions、spit、replace、format、parse-long、prn。
请使用 File/read!、File/write!、File/rename!、String/replace、Regex/run|scan|split|replace、
erlang/binary_to_integer、(println (pr-str x))。参数从右向左求值。
切勿将公共函数命名为核心函数的名字。Hiccup 子元素必须是序列(map/for),而不是向量:
(map identity v)。
查看它
chromium --headless=new --no-sandbox --hide-scrollbars --window-size=412,16000 \
--virtual-time-budget=5000 --screenshot=/tmp/d.png http://127.0.0.1:PORT/
nix shell nixpkgs#imagemagick --command magick /tmp/d.png -crop 412x1000+0+0 +repage /tmp/d0.png
对于高于 16000 像素的页面:通过之前的 section.parts 隐藏来保存 HTML 并截取该部分。
检查:首先是手机宽度,每个图表可读,两种主题,没有 ✗ failed 单元格。
作为应用阅读它
在宽屏上,页面是一个两列的应用,无需更改文件:每个一级标题都会成为侧边栏中的一个页面(展开时会显示其下的二级标题)。一个部分会显示它还在等待多少个答案,已回答的显示 ✓,以及一个显示 DONE/总计的任务列表。一次只显示一个部分,底部有上一步/下一步按钮,←/→ 用于移动。#all(« 整个文档 »)显示所有内容;任何 #anchor 都会打开包含它的部分。在手机上,标签块以相同方式切换部分。将一级标题写为报告的页面,二级标题写为其组成部分。
设计语言
原型可以穿戴上产品的真实设计语言。档案顶部的 #+design: forest 会将其中的每个 screen 和 desk 都绘制为该设计语言(单个块可以用 :design NAME 覆盖,或者用 :design none 选择退出);原型行保持不变。只要存在,优先使用产品自己的语言,而非工具包的默认调色板。
一种设计语言是一个 literate 模块:kit/dossier/design/NAME.bl.org,其中的单元就是命名空间 dossier.design.NAME:令牌(亮色和暗色)、字号比例、圆角、阴影、运动令牌,以及重新绑定工具包原型变量的 CSS。在 kit/dossier/design.bl 中注册它。同一个文件就是它的样本页面,通过四个从模块中绘制自身的块来实现:#+begin_palette、#+begin_type、#+begin_shape、#+begin_motion(每个都有 :design NAME,主体为空)。保留一个 parity 单元,用于检查令牌与产品发布的 CSS 是否一致;工具包测试会断言没有偏差。
已发布:forest——Nurturing Forest,Ora 团队的机器(design/forest.bl.org,作为 forest-language 服务发布)。
工具包
kit/serve.bl(路由 / /doc /rev /src /answers POST /answer)、kit/answers.bl、
kit/dossier/{org,attrs,ui,page,answer,design}.bl(纯函数)、kit/dossier/design/*.bl.org、
kit/style.css、kit/live.js。
测试:bl test -p ~/.claude/skills/bl-org-dossier/kit kit/test/kit_test.bl。
一个新组件 = ui.bl 中的一个 -h 函数、block->h 中的一个分支、CSS、测试中的一个匹配项。
答案由 patch-line 编写:只有 :answer :note :at :by 发生变化;文件的其他每个字节都保持不变。
该技能位于 ~/projects/enchanted-skills/bl-org-dossier(公共的 undefine-org/enchanted-skills 仓库);~/.claude/skills/bl-org-dossier 是指向它的符号链接。
它由使用此技能的每个会话共享。边工作边提交,一个想法一个提交,并且只暂存你自己的文件(首先执行 git status:另一个会话可能正在编辑中)。对档案服务的 work restart 会加载磁盘上的任何内容,包括未提交的编辑。
将读者的答案保存在其位于档案仓库中的单独提交中,这样 git diff 就能显示他们说过什么。