Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
79 commits
Select commit Hold shift + click to select a range
9a0e896
feat(permission): add unified resource permission entry
zgqgit Aug 7, 2026
b3551d9
fix(linsight): 子代理轮次预算按 task 调用分桶,write_todos 空转退还预算
jieyuhuayang Aug 9, 2026
1f0cefc
fix(linsight): 任务进度收敛,已完成的会话不再显示「4/7」假比例
jieyuhuayang Aug 9, 2026
f7b04ca
fix(linsight): 对齐文件工具与代码执行器的两套路径命名空间
jieyuhuayang Aug 9, 2026
f7aa83f
fix(linsight): 厂商上游取数中断的 400 判为可重试,不再一次性判死任务
jieyuhuayang Aug 9, 2026
8b0a0d2
style(chat): rework the conversation rename row
KinyooZ Aug 10, 2026
2d4d0c9
ci: change version
zgqgit Aug 11, 2026
7fc93ee
Merge branch 'hotfix/2.6.0-2' of github.com:dataelement/bisheng into …
zgqgit Aug 11, 2026
365f194
fix(permission): align unified creation pages with figma
zgqgit Aug 11, 2026
0076f21
Hotfix/2.6.0 2 (#2277)
zgqgit Aug 11, 2026
32aac4b
feat(frontend): upgrade bisheng icons to 0.2.30
zgqgit Aug 11, 2026
9c8cbda
fix(frontend): stop repeated permission user requests
zgqgit Aug 11, 2026
d07580a
fix(frontend): align channel settings layout styles
zgqgit Aug 12, 2026
516130d
fix(file-viewers): 把 xlsx 图片绑定到它真正所属的 sheet
jieyuhuayang Aug 12, 2026
9e66f29
fix(frontend): use white settings page backgrounds
zgqgit Aug 12, 2026
f127c26
fix(frontend): normalize permission revoke scope
zgqgit Aug 12, 2026
611f142
fix(frontend): prevent repeated knowledge space error toast
zgqgit Aug 12, 2026
748fc43
refactor(permission): one level menu and one dialog shell across the …
KinyooZ Aug 12, 2026
e8132a7
Revert "fix(file-viewers): 把 xlsx 图片绑定到它真正所属的 sheet"
jieyuhuayang Aug 12, 2026
d34f251
style(permission): selectable access-mode rows, centered settings footer
KinyooZ Aug 12, 2026
b66398e
feat(chat): confirm a recorded feedback verdict with a toast
KinyooZ Aug 12, 2026
f94d370
style(chat): center submenu panels on their trigger row, drop dup hea…
KinyooZ Aug 12, 2026
ec1f09b
style(client): knowledge icons from bisheng-icons, semibold sidebar t…
KinyooZ Aug 12, 2026
787314c
Merge remote-tracking branch 'origin/feat/2.6.0' into feat/2.6.0
zgqgit Aug 12, 2026
cbe1cac
style(error-page): plain copy link, toast instead of a label swap
KinyooZ Aug 13, 2026
78fb5e1
Feat/2.6.0 0811 (#2293)
zgqgit Aug 13, 2026
1ccd263
fix(knowledge): commit an inline rename when the click lands outside
KinyooZ Aug 13, 2026
e2ab7d2
style(chat): keep a conversation row filled while it owns transient UI
KinyooZ Aug 13, 2026
a9409dd
style(chat): one chrome for the input menus, one resting icon tint
KinyooZ Aug 13, 2026
f9b3bbe
refactor(client): share the file-type icon map, drop two dead modules
KinyooZ Aug 13, 2026
b6f48b6
style(chat): mobile drill panels match the menu chrome
KinyooZ Aug 13, 2026
65669cd
style(permission): one empty state for every permission list
KinyooZ Aug 13, 2026
dfadbda
fix(channel): keep edit form initialization stable
zgqgit Aug 13, 2026
384c766
fix(settings): align action footer divider
zgqgit Aug 13, 2026
34746ed
fix(knowledge): enable mobile settings scrolling
zgqgit Aug 13, 2026
f5a5210
docs(client): document useLocalize unstable-reference pitfall
Aug 13, 2026
eb4940f
Merge remote-tracking branch 'origin/feat/2.6.0-0811' into feat/2.6.0
zgqgit Aug 13, 2026
f7e23ea
style(permission): both grant dialogs bottom out the same way
KinyooZ Aug 13, 2026
901fa1a
fix(permission): use bindings for permission list reads
zgqgit Aug 13, 2026
932280e
fix(permission): remove visible reconcile deprecation warning
zgqgit Aug 14, 2026
a1c7b36
fix(config): preserve stored YAML child indentation
zgqgit Aug 14, 2026
2d0c645
fix(linsight): 技能 bundle 改存对象存储,多节点下不再各存各的
jieyuhuayang Aug 14, 2026
f2de9e8
feat: perform knowledge space list api
zgqgit Aug 14, 2026
7cd7b4e
Merge branch 'feat/3.0.0-beta1' of github.com:dataelement/bisheng int…
zgqgit Aug 14, 2026
3f34e39
Merge origin/main into feat/3.0.0-beta1
zgqgit Aug 14, 2026
6b3300c
fix(linsight): 超大工具结果不再让任务陷入逐字重复的死循环
jieyuhuayang Aug 14, 2026
a34bcdc
Merge remote feat/3.0.0-beta1 before main sync push
zgqgit Aug 14, 2026
a51dbc0
fix(linsight): 中转网关的余额不足 403 不再被报成「凭证无效」
jieyuhuayang Aug 14, 2026
7b7adea
fix(title): 标题只用问题生成,并让取消路径也能落下兜底标题
jieyuhuayang Aug 14, 2026
88dca49
docs(f050): define unified permission settings migration
zgqgit Aug 17, 2026
ddfacda
Merge latest feat/3.0.0-beta1 into F050 integration
zgqgit Aug 17, 2026
99e3ea3
Merge feat/2.6.0 UI baseline for F050 adaptation
zgqgit Aug 17, 2026
2683d79
feat(f050): add durable creation idempotency schema
zgqgit Aug 17, 2026
81480b7
feat(f050): add prospective grant protocol
zgqgit Aug 17, 2026
a69361b
feat(f050): add initial grant protocol
zgqgit Aug 17, 2026
baed5db
feat(f050): orchestrate knowledge initial grants
zgqgit Aug 17, 2026
3c6ae4b
style(knowledge): default tooltip skin for the upload-capability hint
KinyooZ Aug 17, 2026
097e3d0
feat(f050): orchestrate channel initial grants
zgqgit Aug 17, 2026
de56acd
feat(f050): expose creation permission context
zgqgit Aug 17, 2026
8997488
fix(f050): persist settings before permission cleanup
zgqgit Aug 17, 2026
5119913
feat(f050): add client permission adapters
zgqgit Aug 17, 2026
9a867d7
feat(f050): model permission drafts with grants
zgqgit Aug 17, 2026
cce70db
feat(f050): wire unified permission settings UI
zgqgit Aug 17, 2026
e66d7be
feat(f050): converge unified settings entry routes
zgqgit Aug 17, 2026
c22f672
fix(i18n): localize creation idempotency conflicts
zgqgit Aug 17, 2026
1c699af
test(f050): cover unified permission settings
zgqgit Aug 17, 2026
f489c1d
refactor(client): clear the lint backlog outside the chat core
KinyooZ Aug 17, 2026
a7b1cce
fix(f050): isolate permission settings reads
zgqgit Aug 17, 2026
e639b48
fix(f050): refresh permission versions before mutate
zgqgit Aug 17, 2026
8b2f710
docs(f050): record quality gate results
zgqgit Aug 17, 2026
3cd62f3
refactor(chatApi): type the request builders as unknown
KinyooZ Aug 17, 2026
3b0d79f
fix(f050): classify initial target failures as partial
zgqgit Aug 17, 2026
08df017
fix(f050): preserve complete permission roster context
zgqgit Aug 17, 2026
1d76cd0
fix(f050): preserve permission draft baseline
zgqgit Aug 17, 2026
0c3d6d6
refactor(f050): remove legacy channel member adapter
zgqgit Aug 17, 2026
e9e110f
merge: sync latest feat/2.6.0 into beta1 integration
zgqgit Aug 17, 2026
11e7d14
chore(i18n): generate creation conflict messages
zgqgit Aug 17, 2026
2f4b034
docs(f050): record merge and build evidence
zgqgit Aug 17, 2026
05ded69
merge: integrate feat/2.6.0 into feat/3.0.0-beta1
zgqgit Aug 17, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions docker/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ services:

backend:
container_name: bisheng-backend
image: dataelement/bisheng-backend:v2.6.0-fix
image: dataelement/bisheng-backend:v2.6.0-fix2
ports:
- "7860:7860"
environment:
Expand Down Expand Up @@ -128,7 +128,7 @@ services:

backend_worker:
container_name: bisheng-backend-worker
image: dataelement/bisheng-backend:v2.6.0-fix
image: dataelement/bisheng-backend:v2.6.0-fix2
environment:
TZ: Asia/Shanghai
BS_SSO_SYNC__GATEWAY_HMAC_SECRET: "bisheng-local-hmac-20260422"
Expand Down Expand Up @@ -161,7 +161,7 @@ services:

frontend:
container_name: bisheng-frontend
image: dataelement/bisheng-frontend:v2.6.0-fix
image: dataelement/bisheng-frontend:v2.6.0-fix2
ports:
- "3001:3001"
environment:
Expand Down
49 changes: 47 additions & 2 deletions docs/architecture/08-deployment.md
Original file line number Diff line number Diff line change
Expand Up @@ -223,11 +223,56 @@ npm start -- --host 0.0.0.0 # 端口 3001,API 代理到 localhost:7860

要点:

- **为什么 2/3 自动、4 手动**:2/3 是纯 DB、幂等、轻量的 backfill,失败只影响菜单 / 模型配置且可自愈,适合放进启动 lifespan;步骤 4 要写对象存储(MinIO 上的 `SKILL.md`)、数据量可能大、需人工核对迁移摘要,副作用重,故保持手动运维脚本(详见 `src/backend/CLAUDE.md`「Migration vs. script」与 PRD 决策)。
- **步骤 4 说明**:需要完整 app context(写 MinIO 的 `SKILL.md`)。**不调用 LLM**——技能描述取 SOP 原描述,缺失时用 SOP 名称兜底(技能描述为必填,不会留空)。产出 JSON 迁移摘要(成功/跳过/失败,**运维产物,无管理页报告界面**),失败 / 超大 SOP 项需人工处理(拆分后经管理页重建)。`linsight_sop` 原表保留归档、不删。
- **为什么 2/3 自动、4 手动**:2/3 是纯 DB、幂等、轻量的 backfill,失败只影响菜单 / 模型配置且可自愈,适合放进启动 lifespan;步骤 4 要写技能正文、数据量可能大、需人工核对迁移摘要,副作用重,故保持手动运维脚本(详见 `src/backend/CLAUDE.md`「Migration vs. script」与 PRD 决策)。
- **步骤 4 说明**:需要完整 app context(写技能 bundle)。**不调用 LLM**——技能描述取 SOP 原描述,缺失时用 SOP 名称兜底(技能描述为必填,不会留空)。产出 JSON 迁移摘要(成功/跳过/失败,**运维产物,无管理页报告界面**),失败 / 超大 SOP 项需人工处理(拆分后经管理页重建)。`linsight_sop` 原表保留归档、不删。
- **幂等**:四步均可安全重跑。步骤 2/3 重复启动是 no-op;步骤 4 借 `metadata.sop-id` 识别已迁移项并覆盖自身 bundle,不会重复产生带后缀的技能。
- 步骤 4 单租户灰度可加 `--tenant-id <id>`。

### v3.0 · 灵思技能 bundle 迁至对象存储

技能正文/脚本/附件此前以**节点本地文件系统**(`SKILLS_ROOT`)为权威存储,DB 只存元数据。单机
compose 下 `backend` 与 `backend_worker` 恰好 bind-mount 同一个 `/app/data` 才让它工作;一旦两者
不在同一台宿主机,A 机上传的技能在 B 机 worker 上读不到,而失败是**静默**的——任务照跑,只是技能
不生效。现改为对象存储(MinIO)为唯一权威 + 节点本地按内容哈希缓存。

| # | 步骤 | 触发方式 | 命令(从 `src/backend/`) | 不执行的后果 |
|---|------|---------|--------------------------|-------------|
| 1 | **加 `linsight_skill.content_hash` 列** | 🔧 部署流程 | `uv run alembic upgrade head` | 后端起不来(缺列) |
| 2 | **发布本机残留的技能 bundle** | ✅ 启动自动(窄) | `python scripts/migrate_skills_to_object_storage.py` → `--apply` | 存量的**用户自建/导入**技能不生效 |
| 3 | **内置技能重新发布** | ✅ 启动自动 | —(seeder 按内容哈希幂等) | 三个官方技能不生效 |

要点:

- **步骤 2 的启动自愈是刻意做窄的**:只发布本机确实持有、且字节数与 DB 记录一致的 bundle。多副本
同时启动时各自看到不同的本地盘,若谁都能发布自己那份,胜出者就是随机的——那正是本次要消除的
多节点不一致。凡不满足条件的,日志会**按技能名列出**,这就是「去持有该 bundle 的那台机器上跑一次
脚本」的信号。
- **每台曾经跑过 API 的机器都要跑一次**步骤 2 的脚本。某台机器盘上没有的,它会报告出来。
- **内置技能不需要迁移**:seeder 直接从镜像重新发布,比任何一台机器的磁盘都更权威。
- **回滚**:新版本期间创建/编辑的技能只存在于对象存储中,旧代码只读本地盘,会**静默失效**。若确需
回滚,先在每台目标机器上跑 `python scripts/restore_skills_to_local.py --apply` 把 bundle 写回
`SKILLS_ROOT`。
- 配置项 `linsight.skills_root` 已降级为「迁移脚本读取本地遗留 bundle 的来源」,运行期不再使用;
本地缓存目录由 `linsight.skills_cache_dir` 指定(留空 = 进程缓存目录下的 `linsight_skills`)。
**不要**把缓存目录指向共享卷。

## 多节点部署

官方 `docker/docker-compose.yml` 是**单机单副本**编排,但组件本身按多节点设计(`entrypoint.sh` 中
workflow worker 明确标注「支持多节点运行」,灵思 worker 用 hostname 级 `node_id` + 心跳 + 任务
ownership)。横向扩容时按下表核对。

| 组件 | 可否多副本 | 注意事项 |
|------|-----------|---------|
| `backend`(FastAPI) | ✅ | 无状态。启动期 backfill/seeder 幂等,多副本同启安全 |
| `backend_worker`(Celery + 灵思 worker + Beat) | ⚠️ | Celery worker 可多节点,队列名需按 `entrypoint.sh` 注释约定;**Beat 只能有一个实例**,否则定时任务重复触发 |
| MySQL / Redis / MinIO / Milvus / ES / OpenFGA | — | 有状态,按各自方案做高可用 |

**关键约束:不要用共享卷在节点间传递业务数据。** 权威存储只有 MySQL/DM8、Redis、MinIO 三处;节点
本地磁盘(`/app/data`、进程缓存目录)一律视为可随时丢弃的缓存。这条已写入架构宪法
[C8](../constitution.md#c8-no-shared-state-on-the-local-filesystem)。单机 compose 下 `backend` 与
`backend_worker` 共享同一个 `/app/data`,会让「跨进程传文件」看起来能用——这是巧合,不是保证。

## 相关文档

- 系统架构总览 -- `docs/architecture/01-architecture-overview.md`
Expand Down
30 changes: 29 additions & 1 deletion docs/constitution.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
> - `scripts/arch-guard.sh` is the **machine-enforcement arm** of this document: each RULE maps to a clause below (see the anchor table).
> - Violations are reported as **BLOCKER** during `/sdd-review design`.
> - **Change governance**: editing this file requires PR review (a law change affects every feature). If a RULE is involved, sync the "→ Cx" note in `arch-guard.sh`.
> - Last revised: 2026-08-06 (F048: lazy permission runtime and operational migration traffic control).
> - Last revised: 2026-08-14 (C8: no shared state on the local filesystem).

## Anchor Table (clause ↔ arch-guard RULE)

Expand All @@ -19,6 +19,7 @@
| **C5** | Error-code convention | — (review) | — |
| **C6** | No hardcoded secrets | RULE-7 | WARNING |
| **C7** | Frontend store must not call HTTP directly | RULE-6 | WARNING |
| **C8** | No shared state on the local filesystem | — (review) | — |

---

Expand Down Expand Up @@ -133,3 +134,30 @@ No `password` / `secret_key` / `api_key` / `access_token` literals in code. Use

A frontend store must not call HTTP directly — go through `controllers/API/` (platform) or `api/` (client).
All other frontend conventions (state library, UI library, path aliases, i18n, Toast, etc.) live in `.claude/rules/platform-frontend.md` and `.claude/rules/client-frontend.md` (see also `AGENTS.md §4`).

## C8. No Shared State on the Local Filesystem ⚠️

**Multi-node is the default assumption, not an edge case.** The backend already runs as several
processes that need not share a machine: API replicas (`uvicorn --workers`), Celery workers, the
Linsight worker (`bisheng/linsight/worker.py`, hostname-derived `node_id` + heartbeats), and Beat.
Two processes agreeing today only because a single-host `docker compose` happens to bind-mount the
same `/app/data` is an accident, not a design.

The authoritative store for anything read by more than one process is **MySQL/DM8, Redis, or MinIO**.
The local filesystem is a cache: disposable, rebuildable, never the source of truth.

| ✅ Use | ❌ Never |
|--------|---------|
| Object storage for bytes + DB row for the pointer | A DB row whose payload only exists on the writer's disk |
| Content-addressed keys, local cache keyed by that hash | A mutable local path treated as the live copy |
| Startup work registered in **every** process role that needs it | Initialization only in `main.py`'s FastAPI lifespan |
| Fail loudly, or report the gap to the user | Log a warning and continue silently degraded |

Reference implementations: `WorkspaceBackend` (MinIO truth + write-through cache) and `SkillStore`
(content-addressed objects + local materialization) in `bisheng/linsight/domain/services/`.

Precedents that make this a law rather than advice: skill bundles shipped as node-local files and
were unreadable from any other host (fixed by moving them to object storage); the F048 resource
registry was installed only in the API process and had to be retrofitted into the background
workers (`02cbb921a`). Both failed **silently** — which is the real cost, and why the last row of
the table matters as much as the first.
4 changes: 3 additions & 1 deletion features/v2.6.0/035-linsight-task-mode/design.md
Original file line number Diff line number Diff line change
Expand Up @@ -577,7 +577,9 @@ SKILLS_ROOT/ # 配置项 linsight_conf.skills_root(v2

`FilesystemBackend(root_dir=SKILLS_ROOT, virtual_mode=True)`:`virtual_mode=True` 把所有路径约束在 `root_dir` 内,**防路径穿越**(`../` 逃逸被拦截)。租户自定义目录按 `tenant_id` 分片,配合 §6 自动注入实现隔离。

> ⚠️ **多节点部署约束**:`worker.py` 的 `NodeManager` 为多节点设计(hostname 级 node_id + 心跳 + ownership),磁盘 Skill 要求 `SKILLS_ROOT` 为**所有 Worker 节点可见的同一卷** —— 须满足「灵思 Worker 单机部署」或「多机挂共享存储(NFS 等)」之一,否则 A 机新建/编辑的 Skill 在 B 机 worker 上读不到、CRUD 与执行不一致。元数据若入 DB 则跨节点一致;**正文一致性依赖共享卷**。运维须将此约束写入部署文档;启动期可加 `SKILLS_ROOT` 可写 + 共享性自检告警。若未来多机且不便共享盘,演进为 MinIO 正文 + 本地物化(`FilesystemBackend` 退化为物化后的本地只读层)。
> ~~⚠️ **多节点部署约束**:`worker.py` 的 `NodeManager` 为多节点设计(hostname 级 node_id + 心跳 + ownership),磁盘 Skill 要求 `SKILLS_ROOT` 为**所有 Worker 节点可见的同一卷** —— 须满足「灵思 Worker 单机部署」或「多机挂共享存储(NFS 等)」之一,否则 A 机新建/编辑的 Skill 在 B 机 worker 上读不到、CRUD 与执行不一致。元数据若入 DB 则跨节点一致;**正文一致性依赖共享卷**。运维须将此约束写入部署文档;启动期可加 `SKILLS_ROOT` 可写 + 共享性自检告警。若未来多机且不便共享盘,演进为 MinIO 正文 + 本地物化(`FilesystemBackend` 退化为物化后的本地只读层)。~~
>
> **已解决(v3.0,2026-08-14)**:本段末尾预留的演进路径已实施 —— Skill bundle 改为**对象存储内容寻址**(`linsight/skills/{tenant_id}/{name}/{content_hash}.zip`)为唯一权威,节点按内容哈希本地物化;共享卷不再是正确性前提。`SKILLS_ROOT` 降级为迁移脚本读取遗留 bundle 的来源。约束本身也已上升为架构宪法 C8(禁止用本地文件系统承载跨进程共享状态),并写入 `docs/architecture/08-deployment.md`「多节点部署」。**当时把它降级成一条"运维须知"而没有落地,是这次返工的根因**:契约写在设计文档里、部署文档零覆盖、启动自检也没做,等于没有约束。

> **两类技能的本质区别(对齐 PRD §4.5/§4.7)**:`built-in/` 是任务模式**内核能力**,所有租户共用同一份磁盘文件、随内核常驻加载,**不出现在技能选择器与管理页,也不经 `/skill` API**;`data/skills/{tenant_id}/` 才是前端可见、可管理的**租户自定义技能**(租户管理员从 0 新建 / 导入)。系统管理员不直接管 built-in,如需维护某租户的自定义技能须经 admin-scope 切入该租户。

Expand Down
Loading
Loading