Skip to content

Commit 189b889

Browse files
committed
Add private Zcode RFC chat and reviewed source edits
1 parent 986cd86 commit 189b889

22 files changed

Lines changed: 2807 additions & 45 deletions

‎.github/workflows/rfc-portability.yml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,4 +25,4 @@ jobs:
2525
with:
2626
node-version: '22'
2727
- run: python -m pip install -e '.[dev]'
28-
- run: python -m pytest test/test_rfc_application.py test/test_rfc_providers.py test/test_rfc_http.py test/test_rfc_integration.py test/test_rfc_security.py test/test_rfc_migration.py test/test_rfc_graph.py test/test_rfc_markdown.py test/test_rfc_components.py test/test_rfc_translations.py
28+
- run: python -m pytest test/test_rfc_application.py test/test_rfc_providers.py test/test_rfc_http.py test/test_rfc_integration.py test/test_rfc_security.py test/test_rfc_migration.py test/test_rfc_graph.py test/test_rfc_markdown.py test/test_rfc_components.py test/test_rfc_translations.py test/test_rfc_chat.py test/test_rfc_chat_web.py test/test_rfc_chat_integration.py test/test_rfc_source_updates.py

‎doc/architecture/SPEC/rfc_service.md‎

Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
<!-- verified-against: 2026-10-05 -->
44

5-
`LOC ~2890(8 个实现模块) · 可移植 RFC 应用与接口 · refactor-status: ok`
5+
`可移植 RFC 应用、授权工作台与受限 Agent · refactor-status: ok`
66

77
## 职责
88

@@ -28,14 +28,17 @@
2828
| `http` | 固定资源列表、会话、同源 HTTP 传输及受保护的旧 roadmap 路由 |
2929
| `cli` | 独立服务命令、用户请求、文件交换、前台 worker 与显式迁移入口 |
3030
| `migration` | 旧 Personal-Agent 跟踪状态的只读比较、备份和幂等 sidecar 导入 |
31+
| `chat` | 私有会话、独立任务租约、受限模型工具、完整候选提案与校验 |
32+
| `translations` | 受权限过滤的双语缓存、独立队列与隔离 GLM 调用 |
3133

3234
## 公开契约
3335

3436
- `RFCService(state_dir, providers=None, roots=None, clock=...)`;
3537
`bootstrap_admin`、`authenticate`、`dispatch`、`process_pending`、`sync_due`;
36-
`build_service(state_dir, config_path=None)` 从执行主机配置构建提供方。
38+
`build_service(state_dir, config_path=None)` 从执行主机配置构建提供方及可选模型适配器;
39+
`ChatService.dispatch/process` 管理独立聊天能力,服务可注入 `chat_agent`。
3740
- `dispatch(principal, action, payload)` 是所有业务入口的共享授权点。动作族包含
38-
`users.*`、`tokens.*`、`grants.set`、`repositories.*`、`sources.preview`、
41+
`users.*`、`tokens.*`、`grants.set`、`repositories.*`、`sources.preview`、`chat.*`、
3942
`rfcs.*`、`operations.*`、`audit.list`、`service.settings/configure`。
4043
- `rfcs.get` / JSON export 返回完整的可见记录;`rfcs.status` 返回去掉源正文和候选
4144
列表的紧凑进度;`rfcs.suggestions` 按可见候选分页,limit 为 1–100。
@@ -49,6 +52,29 @@
4952

5053
## 不变量
5154

55+
- **私有聊天与明确编辑。** 会话绑定用户、仓库和 RFC;历史、事件、工具上下文及
56+
提案读取重查当前所有权、RFC 和来源权限。独立 SQLite 队列在服务内最多运行两轮,
57+
每个会话一轮,模型 I/O 在事务外执行。Zcode GLM-5.3-Flash 通过隔离 complete
58+
调用受应用控制的查询/提案工具,每轮最多四次调用和 180 秒;取消、撤权、过期租约
59+
和重启均不能提交迟到结果。历史按最近 20 条及字符预算裁剪,明确告知裁剪。
60+
审计和通用操作结果不包含聊天正文或原始模型事件。
61+
62+
- **提案版本绑定。** 原文替换锚点、来源行范围和稳定任务 ID 组成候选;服务校验
63+
依赖、角色、RFC revision、手工草稿摘要和完整候选摘要。人工编辑也进入预览与确认,
64+
用户确认后一次事务保存并为已有来源创建 `rfcs.update_source` 操作。无来源草稿
65+
不自动发布。未涉及的正文、内部注释、人工删除、证据和决策历史保留;模型不能
66+
宣布实现完成、验收通过或豁免。设计与条件修改使相关验证失效。
67+
68+
- **持久来源写回。** 等待、失败或结果不确定的写回保护已保存正文,后台仍可更新
69+
关联实现观察。写前重查身份、来源版本和候选摘要,写后读回;重试先核对远端结果,
70+
避免重复写入。已知冲突保留候选并要求重新审阅,不自动覆盖远端。页面分开显示
71+
已保存、等待同步、已同步和来源冲突;任务 sidecar 不写进上游正文。
72+
73+
- **持久聊天界面。** 侧栏在页面内容容器之外,编辑弹窗复用同一面板,移动端全屏。
74+
导航、局部刷新和语言切换保留输入、历史、滚动及任务;来源上下文使用未清理原文。
75+
发送语言决定回答语言,记录与差异不自动翻译;章节/节点/任务/条件有讨论入口。
76+
聊天更新不得重建现有 SVG 或丢失未提交草稿。
77+
5278
- **轻量交互。** `view=summary/detail` 为可选投影;默认 SDK 与导出契约保持完整。
5379
建议由授权分页接口读取。来源权限索引只存活于当前事务,撤销后重新计算。
5480
通用静态资源支持 gzip/ETag;登录页和私有数据保持 `no-store`。操作结果直接
@@ -138,7 +164,8 @@
138164
## 依赖(允许)
139165

140166
应用、状态、提供方、HTTP 与迁移使用 stdlib 和包内模块。CLI 用户动作通过 RFC SDK;
141-
SDK 自身的惰性导入、版本校验和本地/HTTP 选择属于 SDK 契约。HTTP 资源通过
167+
SDK 自身的惰性导入、版本校验和本地/HTTP 选择属于 SDK 契约。可选聊天与翻译适配器
168+
在启用时惰性导入现有 ZCodeTransport,默认应用不加载模型依赖。HTTP 资源通过
142169
`importlib.resources` 从同一 wheel 读取。核心不反向依赖 MCP、模型配置或顶层 CLI。
143170

144171
## 扩展点
@@ -150,7 +177,8 @@ SDK 自身的惰性导入、版本校验和本地/HTTP 选择属于 SDK 契约
150177
## 测试
151178

152179
`test_rfc_application.py`、`test_rfc_providers.py`、`test_rfc_http.py`、
153-
`test_rfc_integration.py`、`test_rfc_security.py`、`test_rfc_migration.py`。
180+
`test_rfc_integration.py`、`test_rfc_security.py`、`test_rfc_migration.py`、
181+
`test_rfc_chat.py`、`test_rfc_chat_integration.py`、`test_rfc_source_updates.py`。
154182
提供方测试使用注入 transport;多页 GET 预算、恢复不完整、opaque ID、角色区分、
155183
本地路径与原子文件更新均有护栏。迁移测试验证原文、历史身份、重复 apply、备份恢复
156184
和失败回滚;接口测试覆盖失效凭据、ACL、紧凑状态和行尾交换。

‎doc/features/rfc-service-personal-agent.md‎

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,19 @@ HttpOnly、Secure、SameSite 会话,API/MCP 使用个人 Bearer 令牌。
3434
纳管用户的有效凭据与当前权限;凭据过期、撤销或角色被收回时,状态为
3535
`requires_reauthorization`,维护者须重新纳管。普通用户不需要部署端平台令牌。
3636

37+
聊天通过独立 Zcode GLM-5.3-Flash 队列运行,服务最多两轮并发;对话按用户、仓库和
38+
RFC 隔离。确认提案后先保存候选,再为已有来源排队 `rfcs.update_source`;来源冲突
39+
或失败不会丢弃候选,重新审阅同时显示当前来源与候选版本的差异。来源更新未确认时
40+
普通核验保留候选正文,关联 PR 的状态仍可刷新。`wm-7074` 上线回归只读取页面,
41+
真实模型和来源写回使用专用测试 RFC。
42+
3743
## 回滚
3844

39-
先停止 `infermatrix-rfc.service`,通过 SQLite backup API 恢复迁移输出的
40-
`backup_path`,再启动服务。不要直接覆盖仍有写入进程的 SQLite/WAL 文件。
45+
先停止 `infermatrix-rfc.service` 和其他前台 worker,检查 pending/running/uncertain
46+
来源操作是否已经对外写入。用 SQLite backup API 保存当前数据库,再回滚程序和配置;
47+
新增聊天表、会话、提案和已保存候选应保留。出现来源写回后不能盲目恢复较早数据库,
48+
需要先核对远端结果和本地操作记录。仅无后续写入的初次迁移可直接恢复迁移生成的
49+
`backup_path`。不要直接覆盖仍有写入进程的 SQLite/WAL 文件。
4150
机器迁移或从备份恢复时,先停止旧写入方;数据库的 workspace ID 会保留,
4251
复制数据库并同时启动两个独立部署不属于允许的写入拓扑。
4352

‎doc/features/rfc-service.md‎

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,85 @@ policy. Changing source refresh frequency does not change the worker tick.
304304
Provider credentials, filesystem roots and public endpoints remain operator
305305
configuration.
306306

307+
## Private RFC chat and reviewed edits
308+
309+
The RFC workbench includes a collapsible right-hand Zcode Agent panel, with a
310+
full-screen panel on phones. Sections, roadmap nodes, tasks and criteria provide
311+
discussion entries using stable identifiers and original source line ranges.
312+
The same panel moves into the source editor; unsaved manual edits are included
313+
in its context and in the complete candidate reviewed before saving. Page refresh,
314+
task updates and language changes preserve the conversation and editor input.
315+
Conversations are private to the authenticated user, repository and RFC, survive
316+
sign-out and restart, and can be deleted by their owner. Readers may discuss;
317+
editing uses the existing contributor/maintainer rules.
318+
319+
Enable the isolated GLM adapter on a host with a logged-in Zcode CLI:
320+
321+
```json
322+
{"chat":{"enabled":true,"backend":"zcode","model":"GLM-5.3-Flash","reasoning":"low","cli":"/path/to/zcode","timeout_seconds":180}}
323+
```
324+
325+
`serve` runs two independent chat workers, separate from source reconciliation
326+
and translation. Each conversation runs one round at a time. A round makes at
327+
most four model calls within 180 seconds; model I/O never holds a database
328+
transaction. With `serve --no-worker`, use `chat-worker --watch` in a separate
329+
process, or `chat-worker --once` for a single cycle. Worker claims, execution
330+
leases, cancellation and authorization checks fence late or revoked results.
331+
The UI polls normalized job status once a second and displays the final reply
332+
only after validation. Recent context is bounded to 20 messages and a character
333+
budget; a truncation notice is included when history is shortened. Platform
334+
credentials and unrestricted repository tools are never given to the model.
335+
336+
The edit flow is **chat → proposal → source and plan diff → confirm once**.
337+
The service validates raw-text replacement anchors, stable task IDs, dependencies,
338+
current permissions and source/editor versions. A proposal cannot invent merged
339+
implementation, passing evidence, acceptance or waivers. Diff text, conversation
340+
history and source Markdown are not passed through page translation. Answers use
341+
the send-time language; source edits preserve its language unless translation is
342+
explicitly requested. A changed editor or RFC requires a new proposal/review.
343+
344+
Confirmation saves the exact complete candidate, including manual edits, and
345+
creates a durable `rfcs.update_source` operation for an existing source. Drafts
346+
without a source remain unpublished. Task/acceptance metadata stays in the
347+
workspace; only source title/body is written upstream. The source indicator
348+
separates saved, waiting to synchronize, synchronized and source conflict.
349+
Pending or failed writeback protects the saved body from background refresh,
350+
while linked PR observations can still update. Execution rechecks permission and
351+
source version, then reads back the write; uncertain results are reconciled before
352+
retry. Provider APIs do not supply an atomic compare-and-swap guarantee. Known
353+
conflicts preserve the candidate and require a fresh review.
354+
355+
All transports use the same actions under `/api/v1/actions/`: `chat.create`,
356+
`chat.list`, `chat.get`, `chat.send`, `chat.events`, `chat.cancel`, `chat.retry`,
357+
`chat.delete`, and `chat.proposals.preview/apply/reject`. Sending returns a job ID
358+
immediately. Messages and events are paginated; every read rechecks thread
359+
ownership and current RFC/source access. Audit records contain operation identity
360+
and status, never private transcript or raw model events.
361+
362+
```python
363+
from infermatrix_copilot.sdk.v1 import RFCClient
364+
365+
client = RFCClient.from_env()
366+
thread = client.chat_create("RFC_ID")["thread"]
367+
queued = client.chat_send(thread["id"], "Explain the remaining work",
368+
idempotency_key="discuss-once", language="en")
369+
print(client.chat_events(thread["id"]))
370+
# After a validated proposal exists, review it before explicit confirmation:
371+
# preview = client.chat_preview("PROPOSAL_ID")
372+
# client.chat_apply("PROPOSAL_ID", candidate_digest=preview["candidate_digest"],
373+
# reason=preview["proposal"]["reason"])
374+
```
375+
376+
CLI and Copilot MCP use `request chat.send --data FILE` and
377+
`rfc_request("chat.send", payload)` with the host's configured user credential.
378+
They do not bypass review, authorize model tools independently, or choose another
379+
workspace when the configured remote is unavailable.
380+
381+
Before deployment, back up the installed program, operator configuration and
382+
SQLite with its backup API. Before rollback, stop all workers and inspect in-flight
383+
source operations; preserve the new chat tables and candidate bodies. Never
384+
restore an older database blindly after an upstream write.
385+
307386
## SDK, MCP and remote workspaces
308387

309388
```python

0 commit comments

Comments
 (0)