贡献工作流¶
当前内容契约更新:固定来源 PDF 已恢复为 paper_access 开关下的鉴权字节, 关闭404,qa_、别名、Range、历史原件不能绕过。新内容在独立 qatlas-content 桶的 content/ 命名空间不可变发布:source 绑定确切 PDF SHA,arXiv vN 是语义版本、 S3VersionId 是物理版本、parse revision 是独立不可变身份;三者不可混用。 同 paper/SHA 可复用 source;冻结导入别名不同字节409(overwrite也不替换)。 解析上传必须完整支持的 Middle+Markdown,保留所有成员原名/字节,MD-only拒收; manifest最后核验发布且PG必需,重传新revision,不覆盖历史。旧MD/JSON/images忽略, 只在内容/PDF访问按需冻结旧PDF,不bulk回填、不删除旧桶;没有nightly/boot/入库自动推理。 下文涉及旧三桶覆盖、仅full.md+images、原PDF410/外部直链的段落保留为历史背景, 不作为当前操作规则。当前API/上传/续读统一见论文内容契约 与Upload API;完整read视图不是原Middle JSON。
这份文档描述如何把内容贡献到 QuantumAtlas:把论文 PDF、解析 Markdown、元数据 落到对象存储,并登记进 PostgreSQL paper registry。
如果你想理解项目分层和设计动机,先看 architecture.md。如果你只想跑起来一个服务,看 deployment.md。本文档定位是「内容贡献的 how-to」。
0. 配置加载约定¶
v0.17.0 起 client 和 server 配置完全分离:
Server (
qatlasd): YAML-only,默认~/.qatlas/config.yaml,通过--config选择;旧业务环境变量会被拒绝,见服务端配置。PocketBase 监听/数据目录等显式启动参数不等于逐字段 env 配置。Client (
qatlas): 单入口 YAML,首次跑任何qatlas <cmd>自动创建模板到platformdirs解析的位置(Linux~/.config/qatlas/、macOS~/Library/Application Support/qatlas/、Windows%APPDATA%\qatlas\);不读 任何 CLI flag / OS env /QATLAS_DOTENV
贡献者通常只需要配 client(不跑 server):
# 路径在哪?(不同平台不一样)
qatlas config path
# 设 server URL
qatlas config set server_url https://quantum-atlas.ai
# 设 PAT(写操作需要;建议跑 `qatlas auth login -s quantum-atlas.ai`
# 走 OAuth device-code flow,浏览器 Approve 自动写 hosts.yml。
# 已有明文的话从 stdin 注入:
# echo qat_xxxxx | qatlas auth login -s quantum-atlas.ai --with-token)
# 远端是自签 HTTPS(开发环境 Caddy `tls internal`)时打开
qatlas config set insecure true
# 仅在使用 qatlas contrib mineru 本地解析时需要
echo mn_xxxxx | qatlas config set mineru_api_token # 从 stdin 读,不进 history
# 其余 mineru_* 字段都有合理默认值,按需覆盖
完整字段映射 + YAML schema 见 client config reference 与 env-vars.md §Client。
服务端的 .env(含 QATLAS_POSTGRES_DSN / QATLAS_S3_* / GITHUB_CLIENT_* / QATLAS_SYSTEM_PAT 等)见 server-config.md 与 operations.md 的「推荐的单机生产目录」段。
1. Raw 资产贡献的三条路径¶
RAW_DIR 是论文资产的 canonical store(默认 ${XDG_DATA_HOME:-$HOME/.local/share}/qatlasd/raw,或显式 QATLAS_RAW_DIR 覆盖)。文件名遵循 arXiv 规范并按 YYMM 分片:
风格 |
例子 |
存储路径(相对 |
|---|---|---|
老式(pre-Apr 2007) |
|
|
新式(post-Apr 2007) |
|
|
markdown/ / json/ / images/ 子目录采用同样的分片布局。版本号 vN 是强制的——所有 upload 端点都拒绝不带版本的 ID。
下面三条路径都会落到这一套布局。选哪条取决于谁出算力、谁出资产。
Path A:服务器侧按 arXiv ID 抓取¶
最轻量。贡献者只需要给一个 arXiv ID,服务端负责抓 PDF 并解析(fetch + parse),产物落对象存储并登记进 PostgreSQL registry。
qatlas ingest quant-ph/9508027 # 默认走 MinerU 解析
qatlas ingest 2501.00010 --parser mineru # 显式声明也可以,等同上一行
--parser 是可选——开源版本目前只支持 mineru 这一个 parser,省略则按默认走。
qatlas ingest 会同步走 fetch + parse 流水线并轮询任务状态。完整选项:
qatlas ingest --help
关键选项:
选项 |
行为 |
|---|---|
|
可省略;保留为显式 flag 是为了将来接口协议扩展(万一以后又加了新 parser,签名不动)。 |
|
在指定阶段后停止( |
|
只跑明确列出的阶段( |
|
即使本地已有 PDF/Markdown 也强制重做 |
|
让 MinerU 绕过它自己的缓存 |
|
提交后立即返回,不等任务完成 |
任务状态可以单独查询:
qatlas ingest status TASK_ID
要在 stop_after=fetch 之后续跑 parse,用:
qatlas ingest continue TASK_ID --stages parse
底层端点:
POST /api/ingest/paperPOST /api/ingest/{task_id}/continueGET /api/ingest/{task_id}/GET /api/ingest/stages/GET /api/ingests
Path B:鉴权用户直接上传 PDF¶
适用场景:贡献者本地已有 PDF(或 arXiv 不可达),希望直接把资产推到服务器。
qatlas contrib pdf quant-ph/9508027v1 --pdf paper.pdf
qatlas contrib pdf 2501.00010v1 --pdf paper.pdf --overwrite
对应端点 POST /api/papers/{arxiv_id}/upload-pdf 行为:
arXiv ID 必须携带版本号
vN,否则 400。对 PDF 做最基本的
%PDF-magic 校验,不通过 400 并清理临时文件。默认拒绝覆盖(409);显式
--overwrite才允许替换。单文件大小上限:PDF 100 MiB。
论文 metadata(题目 / 作者 / 摘要 / 引用)由服务器从 OpenAlex 上游同步进 PostgreSQL paper registry,不再走 upload 端点(v0.7.0 起)。
MinerU 结果(markdown + 图片)通过 Path C 的 qatlas contrib mineru 推送——它跑完 MinerU 后把整包推给 server 的 upload-mineru 端点:
对应端点 POST /api/papers/{arxiv_id}/upload-mineru:
接受完整 MinerU 结果 zip(必含
full.md,可选images/*)。server 端解包:
full.md写到qatlas-md桶,每张images/<name>写到qatlas-images/<yymm>/<stem>/。单文件上限 200 MiB(zip 整体)。
source是可选审计标签(如mineru-client、hand-edited),不影响存储位置。
Path C:本地跑 MinerU 后把解析结果推回云端¶
适用场景:贡献者愿意用自己的 MinerU 配额(mineru_api_token 写在自己电脑的 yaml 里),服务器配置可以完全不带这个 token。
前置条件:要处理的 PDF 必须已经在服务器的 RAW_DIR 里(通过 Path A 或 Path B 进入)。qatlas contrib mineru 不接受用户自带 URL——这是为了保证生成的 markdown 永远对应 raw 中已知的 PDF,不会产生孤儿数据。
# 队列 / batch 模式:从服务器的"需要 MinerU"队列里取至多 --batch-size 个还没人处理的
# 论文,逐个 claim + 校验后**一次 batch** 提交给 MinerU;每完成一篇立刻 upload +
# 释放该 claim。默认 batch_size=50(MinerU 单批硬上限)。
qatlas contrib mineru
qatlas contrib mineru --batch-size 20
# 单篇模式:处理指定 arxiv 论文(同样要求服务器已有 PDF)。走单 task API,开销最小。
qatlas contrib mineru quant-ph/9508027v1
# daemon 模式:挂着持续贡献,跑完一批 sleep --watch-interval 再来。
# 命中 MinerU 每日额度时自动 sleep 到次日 00:01 quota 重置。
qatlas contrib mineru --watch
qatlas contrib mineru --watch --watch-interval 600 --batch-size 30
# 只跑 MinerU 不上传(zip 留在本地临时目录,lease 立刻释放)
qatlas contrib mineru 2501.00010v1 --no-push
并发模型(MinerU lease):
想处理一篇 →
POST /api/v1/papers/{arxiv_id}/mineru-lease申请一个短期租约(默认 30 分钟,最长 2 小时,可用--ttl-seconds调整)。服务端原子地更新 catalog lease 字段;如果已有未过期 lease → 409 + 现有租约元数据。
上传成功 → 服务端自动删除 lease。
客户端异常中断 → 用
DELETE /api/v1/papers/{arxiv_id}/mineru-lease/{claim_id}主动释放(qatlas contrib mineru在 except 分支和 SIGINT 处理里都会自动调用)。没释放也没事:lease 到期后会被下一个 lease 请求覆盖。
qatlas contrib mineru 流程:
从本地
~/.config/qatlas/config.yaml读mineru_api_token等字段。没传 arxiv_id →
GET /api/papers/needs-mineru?limit=<max>取队列;传了就只处理那一篇。对每篇 → POST claim;服务端返回
pdf_url(指向 arxiv.org 原 PDF URL)+claim_id。提交 MinerU → 按
MINERU_POLL_INTERVAL/MINERU_TIMEOUT轮询。完成后下载整个 MinerU 结果 zip → POST
/api/papers/{arxiv_id}/upload-mineru?source=mineru。server 解包写两桶(markdown + images),上传成功 = claim 自动释放。异常路径(MinerU 失败 / 上传失败 / Ctrl+C)→ DELETE claim 主动释放。
目前唯一的 MinerU 推送路径就是 qatlas contrib mineru(claim → 解析 → upload 一条龙);server 端 upload-mineru 端点不再有独立的 CLI 直传入口。
Path D:直接读 PDF / Markdown(self-hosted PAPER_ACCESS 部署)¶
只在部署方启用 QATLAS_PAPER_ACCESS_ENABLED=true 的 self-hosted 实例上可用
(公开 quantum-atlas.ai 默认不开)。Agent / 客户端不需要持有任何资产,
直接按 arxiv ID 或 DOI 拉服务器侧缓存。
推荐:用 qatlas paper 子命令(自动处理 LRO 轮询、进度行、defaults 提示、
错误结构化展示):
# Markdown(缓存命中即拿;未命中自动等 silent fetch + MinerU convert)
qatlas paper get markdown quant-ph/9508027v2 -o paper.md
# 图片 zip(MinerU 转换产物;不带 vN → server 自动补 latest)
qatlas paper get images 0811.3171 -o hhl-images.zip
# 当前可获取固定PDF及完整解析成员;所有内容受paper_access与认证约束
# DOI 入口
qatlas paper get markdown 10.1103/PhysRevLett.103.150502 -o hhl.md
# 只查不触发(poll 状态用)
qatlas paper status 0811.3171v3 --kind markdown
详细 flag 与退出码见 CLI · qatlas paper。
底层:直接 curl —— agent 不依赖 Python client、或想要更细粒度控制时:
# Markdown(缓存命中即 200 + text/markdown bytes)
curl -i https://<server>/api/papers/quant-ph/9508027v2/markdown \
-H "Authorization: Bearer $QATLAS_TOKEN"
# 当前 GET /pdf 是鉴权固定来源原字节;禁用开关404,不是恒410。
# PDF 抓取仍作为 markdown 转换管线的内部阶段存在。
# DOI 入口
curl -i https://<server>/api/papers/10.1103/PhysRevLett.103.150502/markdown \
-H "Authorization: Bearer $QATLAS_TOKEN"
缓存未命中时 markdown 端点走 Long-Running Operation 协议:
服务器立即返回
202 Accepted+Operation-Location: …/status+Retry-After: 5。后台异步执行(PDF 不在 → silent fetch from arxiv.org → 写桶 → 串 MinerU convert → 写桶)。
客户端 poll 对应
/markdown/status(side-effect-free,永远 200;/pdf/status保留为内部抓取管线的 debug 探针),看state/pdf_ready/md_ready/phase/fetch.bytes_received/convert.stage等字段做决策。state == cached后再 GET 资源拿字节。
N 个并发请求同一篇论文被同一 process 内 server-side 自动 dedupe 成 1 次 fetch + 1 次 convert; 所有调用方看到同一份 Job snapshot。跨 edge 不 dedupe(issue #13 跟踪 active-active 部署的优化)。完整 LRO 状态表、各 phase 字段含义、 agent 决策三元组(state / pdf_ready / md_ready)见 冻结内容访问 · 异步读取。
鉴权:papers:read scope(与现有 /api/papers/stats 同)。没有匿名入口——
对外受众范围由 PAT / session token 控制,部署方对 markdown / PDF 字节的对外
分发后果自负,详见 License & Attribution · 论文访问开关。
2. 鉴权与审计¶
服务器使用 PocketBase 内嵌的 GitHub OAuth 流程做浏览器登录,并通过 authGuard(internal/routes/auth.go)门禁数据端点。论文搜索、统计与资产读口收敛到 papers:read scope,写口收敛到 papers:write。
authGuard 接受三种凭据,按到达顺序检查:
System PAT(环境变量加载) —— server 启动时从
QATLAS_SYSTEM_PATenv 读取;命中常时比较直接通过 authGuard。永不过期、跟 PocketBase 完全解耦、pb_data 挂了还能用——专供 CI / cron / 灾难恢复等 ops 路径。详见 auth-model.md § System PAT。Personal Access Token (PAT) —— bearer 以
qat_开头,从 SPA/pat页面创建,明文一次性显示。强制设置过期时间(7 / 30 / 60 / 90 / 365 天,最长 1 年)。每条 PAT 携带显式 scope 列表,默认空集 = 什么写口都调不了,必须勾选具体 scope 才能用。撤销 = 同页 Revoke。人工脚本 / 想钉到具体身份的 ingest 工具推荐这条。PocketBase 用户 session token —— OAuth 登录后浏览器自动持有(
pb.authStore),SPA 内所有调用自动带在Authorization头里。没有 UI 入口去手动 copy——只在浏览器里用得了。隐式拥有全部权限,跳过 scope 检查。
任何写口都同时接受这三种形式;非浏览器调用在 Authorization: Bearer <...> 里塞 PAT 或 system PAT 都行。
Scope 词表¶
参考 GitHub fine-grained PAT 设计。没勾任何 scope 的 PAT 调写口直接 403——这是有意为之的安全默认。
Scope |
覆盖端点 |
说明 |
|---|---|---|
|
|
上传 PDF / MinerU 结果包、跑 MinerU 任务 |
|
|
读取 paper catalog 元数据;可触发 server 端 silent fetch + MinerU convert(需要部署方知情) |
|
|
theorems builtin 插件(Lean 证明 registry 的 read-through) |
scope 的 obj/act 在 scopeGuard 抛 403 时会回显在 detail 里——CLI 报错能直接告诉你"该 PAT 缺 papers:write scope,去 /pat 重发一条"。
底层用 casbin 做 enforce,model + policies 都 hardcode 在 internal/pat/scopes.go(不外置文件,部署简单);将来加 path-pattern scope(如"仅允许 quant-ph/* 命名空间")改 matcher 为 keyMatch 即可,调用方代码无需动。
Scope 运维须知(仅当你在改 scope 词表或部署 server 时需要看):
enforcer 在
cmd/qatlasd/main.go启动时一次性构建(pat.NewEnforcer()),失败 =log.Fatalf进程退出,没有降级路径。启动日志里看到build PAT scope enforcer: ...是 fatal 不是 warning——通常意味着改scopes.go时引入了 model / policies 语法错误,回滚或修语法再重启。词表是编译时静态的(
scopes.go::scopePolicies表 +AllScopes切片),加 / 删 scope = 改代码 + 重新编译 + 重启 server,不支持运行时AddPolicy/ 热加载。也正因如此 enforcer 实例可以安全在所有 handler 间共享(Enforce()并发读安全)。新加 endpoint 时如果忘记把 enforcer 传给
scopeGuard(传nil),那个 endpoint 会对每个请求返回 500"scopeGuard: enforcer not configured (server wiring bug)"——fail loud 而不是放行。code review 时确认所有RegisterXXX(se, cfg, ..., enforcer)签名一路传递。pat_tokens.scopes列 JSON 损坏时(手动编辑 / 迁移失误),decodeScopes返回 nil,该 PAT 被视为"无 scope",所有写口收到 403,不会返回 500。修复直接在 admin UI 或 SQLite CLI 改回合法 JSON。fail-closed 行为由internal/routes/auth_test.go::TestDecodeScopes钉住。
获取 (1) PAT¶
A. 浏览器(多数人)
浏览器登录后打开
https://<server>/pat;"New token" → 填名字(如
nightly-ci)+ 可选 description + 必选过期天数(默认 90 天)+ 勾选需要的 scope;服务器返回的明文只显示一次——立即拷到 GH Actions secret / systemd
EnvironmentFile(server 用)或qatlas auth login --with-token(从 stdin 读,写 client hosts.yml);之后这条 PAT 在列表里只显示前缀(
qat_xxxxxxxx…)、scope 标签和 last-used 时间戳,需要换号就 Revoke 再创建一条。
B. 服务端 shell(运维 / CI 自动化)
如果你 SSH 上了部署服务端二进制的主机,可以直接用 qatlasd pat 子命令而不开浏览器:
sudo -u qatlasd /opt/quantum-atlas/qatlasd pat mint \
--user me@example.com --name nightly-ci \
--scopes papers:write --expires-in-days 365
# stdout: qat_xxxxxxxxxxxxxxxxxxxxxxxxxxx
# stderr: minted PAT id=... prefix=qat_xxxx... user=me@example.com scopes=[papers:write] expires_at=...
mint 输出的明文走 stdout(可以 SECRET=$(qatlasd pat mint ...) 直接捕获到变量),元数据走 stderr。配套 list / revoke <id> / scopes 三个子命令分别看现状、撤销、查 scope 词表。这条路径绕开 sessionGuard(前提:你已经是有 shell 权限的运维人,DB 文件你本来就能直接改),适合 nightly secret 之类不开浏览器的场景。
PAT 不能用来管理 PAT:/api/pat HTTP 端点用 sessionGuard 而非 authGuard——只接受浏览器 session token,PAT auth 一律 403。这跟 GitHub fine-grained PAT 一致:一条 PAT 即便泄露也不能 mint 出更多 PAT,限制爆炸半径。服务端 CLI 之所以可以"绕过",是因为执行者已经是 shell 权限持有者,跟 HTTP 远端调用是两套信任模型。
获取 (2) Session token¶
浏览器登录 GitHub OAuth 后,session 自动存在 pb.authStore(前端的 localStorage 里),SPA 调用 /api/* 时自动附 Authorization: Bearer <session>,14 天到期会自动续期,无需手动复制 / 粘贴。
没有 /token 这种 UI 页面让你 copy session token——如果你想脱离浏览器调用(CLI / curl / CI),请用上面的 PAT(人工身份)或 System PAT(运维身份)。这是有意设计的限制:session token 短期、自动续期、绑浏览器;要给"长跑 / 自动化"用就该走专门的长寿命凭据。
涉及到的服务端配置:
GITHUB_CLIENT_ID/GITHUB_CLIENT_SECRET:启动时把 GitHub OAuth provider 注入 users collection(internal/auth/oauth.go::syncGitHubProvider,幂等)。QATLAS_ADMIN_GITHUB_LOGINS:GitHub 用户名白名单(逗号分隔),未来用于自动 admin 提权(handler 待补)。QATLAS_USER_HEADER:仍可用,反代/SSO 注入审计头时由 upload 端点写入uploaded_by字段;与 PocketBase auth 并行,不互斥。
PAT 的实现说明:每条 PAT 在 SQLite pat_tokens 集合里只存 bcrypt 哈希(token_hash 字段 hidden=true,admin UI 都不显示),明文从不持久化;scope 列表以 JSON 字符串存在 scopes 字段;过期时间存 expires_at(强制非空);服务端用 last_used_at 记录每次成功认证的时间戳。代码见 internal/pat/{pat.go,scopes.go,migrations.go} 和 internal/routes/{pat.go,auth.go,scope_guard.go}。
客户端 CLI 调用写口时,要带 bearer token。推荐用 qatlas auth login(类似 gh auth login),凭证存在 ~/.config/qatlas/hosts.yml,之后任何 qatlas 子命令自动捡起来,不必每次 export:
# 一次性配置:
qatlas auth login -s quantum-atlas.ai
# 浏览器自动打开走 OAuth,Approve 后 token 自动写进 hosts.yml
# 已有明文则:echo qat_... | qatlas auth login -s quantum-atlas.ai --with-token
qatlas auth status # 查看已登录的所有 host
qatlas auth token # 打印当前 host 的 token,方便 pipe 给 curl
qatlas auth logout -s quantum-atlas.ai # 撤销本地凭证
# 之后所有 qatlas 子命令直接用:
qatlas contrib pdf quant-ph/9508027v1 --pdf paper.pdf
token 解析优先级(v0.19.0+ 精简):~/.config/qatlas/hosts.yml 里匹配当前 host 的条目 > 无凭证。gh CLI 风格的 --token flag 和 QATLAS_TOKEN env 在 v0.17.0 已删除;config.yaml 的 token: 字段在 v0.19.0 移除(会静默盖住所有 per-host token,是 footgun)。client 只读 hosts.yml;server 端有独立的 QATLAS_SYSTEM_PAT env 给运维 break-glass 用,详见 server-config.md。
直接 curl 方式(绕开 client,调底层 API):
# 先从 yaml 读出来
TOKEN=$(qatlas config get token)
curl -X POST -H "Authorization: Bearer $TOKEN" \
-F pdf=@paper.pdf -F metadata=@meta.json \
"https://quantum-atlas.ai/api/papers/quant-ph/9508027v1/upload-pdf?overwrite=true"
具体反代配置(Caddy 现在已经是纯 reverse_proxy)见 deployment.md。
3. 推荐协作节奏¶
摄入论文或资料:按 Path A / B / C 之一把 raw 资产入库,保留证据链。
确认 registry 状态:
/api/papers/stats看整体资产覆盖;缺 metadata 的由 server 从 OpenAlex 上游补齐。下游使用:用
POST /api/search多 provider 检索,或按 id / DOI 拉取论文资产(self-hosted PAPER_ACCESS 部署)。
5. 客户端插件系统(qatlas CLI 插件)¶
qatlas 客户端有一个轻量插件机制:插件可以往 CLI 里注册顶层命令(qatlas <name>)
或 contrib 子命令(qatlas contrib <name>)。内置 lean 透传插件,按是否配置了 checkout
自动启停:
插件 |
贡献的命令 |
默认 |
怎么开 |
|---|---|---|---|
|
|
配了 checkout 就开 |
|
旧的
claim插件(qatlas contrib claim本地 WebUI)已停维并移除:claim 草稿 + 提 issue 的流程整体迁到上游 qatlas-lean(qatlas-lean contrib claim——见 ADR 0008,取代 0005)。
qatlas --help 会列出当前可用的插件命令;第三方插件可通过 qatlas.plugins entry point 注册。
qatlas lean —— 透传到本地 qatlas-lean¶
把本地的 qatlas-lean checkout 接进来,直接用它原本的 CLI(不改 lean 仓库代码,
子进程透传)。配了 lean_dir 后:
export QATLAS_LEAN_DIR=~/path/to/qatlas-lean # 或 qatlas config set lean_dir <path>
qatlas lean help # 等价于在那个 checkout 里跑 ./qatlas-lean help
qatlas lean contrib claim 2208.06941 # 起草 Claim + 提 type/theorem issue(§6)
qatlas lean start # 起 dashboard;start-daemon / status / logs / …
claim 草稿、提 issue、证明全部走 lean 自己那套(contrib claim 的本地 WebUI 起草 + 提 issue,
daemon 接 issue 并证明);qatlas lean 只是个统一入口。
6. 起草 Claim 并提 issue:已迁到上游 lean(qatlas-lean contrib claim)¶
claim 草稿 + 提
type/theoremissue 的 WebUI 已从 QA 客户端迁到上游 qatlas-lean 仓库 (见 ADR 0008,取代旧的 ADR 0005)。QA 不再内置 claim 插件;它只继续当文献 host (被读:取论文字节 + 解析 references),从不写 issue。
把一篇论文里"可形式化的命题"(Claim——预证明的近似逐字自然语言陈述)整理成一个
type/theorem gitea issue,交给上游 Lean prover(agony/qatlas-lean)去证。这是
localhost 的人在环路(human-in-the-loop)工作流,不经过 QA server 的写路径。
在 qatlas-lean checkout 里直接跑,或经 QA 的 lean 透传插件:
# 直接在 qatlas-lean checkout 里:
./qatlas-lean contrib claim 2208.06941
# 或经 QA 的 lean 透传插件(配了 lean_dir 后等价):
qatlas lean contrib claim 10.22331/q-2023-03-20-955 --no-browser --port 8731
流程(实现细节见 qatlas-lean 的 README / docs/cli.md):
qatlas-lean 用 suspend-and-wait 端点(
GET /api/papers/{id}/markdown,202 → poll → 200)从 QA 把论文 markdown 取下来——PDF 还没下载/转换时 server 后台静默处理。WebUI 里起草 Claim:点 "Draft with agent" 让 qatlas-lean 自己的 CLI flat-agent (
claim-drafter,不用 SDK / 不需要 LLM key)抽取所有可形式化命题;也可手填(manual 模式)。每条 Claim 编辑:近似逐字的自然语言陈述、LaTeX、假设、
source-md行号、references (命名空间无版本kind:id,如arxiv:2208.06941/openalex:W…/doi:10.x/y)。"Resolve references" 走 QA 的
GET /api/papers/lookup解析 references 元数据;语料库不可达时 自动回落公共 OpenAlex API(ADR 0007 的协议不变,只是改由 qatlas-lean 调用)。"Confirm claim / 确认 claim" → 提一个
type/theorem+status/readyissue。 幂等:如果已有一个 open issue 带相同claim_id,直接拒绝并给出已存在 issue 的 URL (不重复提、不自动更新——daemon 可能正在证它)。
QA server 侧看不到 Claim;它只在 Claim 被证成 Theorem、落进上游 registry.json、
theorems 插件 git pull 之后才在 /theorems 页面出现(ADR 0002 / 0004)。