ci(build): 增加第三阶段全量门禁与验收记录
This commit is contained in:
@@ -0,0 +1,68 @@
|
||||
name: CI
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches: [main]
|
||||
push:
|
||||
branches: [main, "feat/**", "fix/**", "chore/**"]
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
docs-check:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-python@v5
|
||||
with: { python-version: "3.12" }
|
||||
- run: git diff --check
|
||||
- run: python scripts/check-doc-links.py
|
||||
|
||||
backend-test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-python@v5
|
||||
with: { python-version: "3.12" }
|
||||
- run: pip install uv==0.9.24
|
||||
- run: uv sync --frozen
|
||||
working-directory: backend
|
||||
- run: uv run python -m compileall -q app
|
||||
working-directory: backend
|
||||
- run: uv run pytest
|
||||
working-directory: backend
|
||||
|
||||
service-test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-python@v5
|
||||
with: { python-version: "3.12" }
|
||||
- run: pip install uv==0.9.24
|
||||
- run: uv sync --frozen
|
||||
working-directory: backend
|
||||
- run: uv sync --frozen && uv run pytest
|
||||
working-directory: server sync
|
||||
- run: uv sync --frozen && uv run pytest
|
||||
working-directory: community-server
|
||||
- run: backend/.venv/bin/python scripts/phase3-isolated-smoke.py
|
||||
|
||||
frontend-test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with: { node-version: "22", cache: pnpm, cache-dependency-path: frontend/pnpm-lock.yaml }
|
||||
- run: corepack enable && corepack prepare pnpm@10.28.0 --activate
|
||||
- run: pnpm install --frozen-lockfile
|
||||
working-directory: frontend
|
||||
- run: pnpm test && pnpm type-check && pnpm build
|
||||
working-directory: frontend
|
||||
|
||||
rust-core:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with: { components: rustfmt, clippy }
|
||||
- run: cargo fmt --check && cargo test --lib --locked && cargo clippy --lib --locked -- -D warnings
|
||||
working-directory: frontend/src-tauri
|
||||
@@ -4,6 +4,8 @@
|
||||
|
||||
> 本文件用于团队开发期间快速配置环境、启动项目并了解当前实现状态,不是正式的项目 README。
|
||||
|
||||
> 第三阶段分支状态(2026-09-07):已加入 Tauri/Rust 原生 Vault 预览、Sync v1 服务原型、签名社区目录原型和七类社区入口。完整 Sidecar、Stronghold、生产插件隔离、同步客户端、升级回滚及三平台发布门禁仍未交付;详见[第三阶段实施与验收记录](docs/development/第三阶段实施与验收记录.md)。
|
||||
|
||||
NotesAgent 是本地优先的 AI 笔记与知识库项目。当前可运行形态为 Vue/Vite Web 前端与 FastAPI AI Core:Markdown 和附件保存在本地 Vault,SQLite 管理元数据、全文索引、向量空间、搜索历史、AI 会话、任务、Agent Trace、多模态任务及运行诊断。AI 对话已接入知识库检索,会话与消息由后端持久化并供 Web 和桌面客户端共用。
|
||||
|
||||
截至 2026-09-06,第一阶段及第二阶段 A~F 的工程范围已经合并到 `main`。当前已完成真实 Workspace、混合检索与知识库问答、Agent/Tool/Permission、Skill/Plugin、MCP 配置与调用、模型提供商与路由、RAG Benchmark,以及本地 Embedding、音频转写和片段级声纹聚类。Tauri/Rust Host、Stronghold、原生多 Vault 文件系统、生产级 MCP 沙箱和 Sync Server 尚未接入。
|
||||
@@ -15,7 +17,8 @@ NotesAgent/
|
||||
├── frontend/ Vue 3 + TypeScript + Vite 前端
|
||||
├── backend/ FastAPI AI Core、SQLite 与本地模型运行管理
|
||||
├── docs/ 架构、契约、开发说明、协作规范与问题复盘
|
||||
└── server sync/ 云同步服务预留目录,当前未实现
|
||||
├── community-server/ 社区目录、签名发行与审核原型
|
||||
└── server sync/ 独立 Sync v1 服务原型
|
||||
```
|
||||
|
||||
## 当前能力
|
||||
|
||||
+6
-1
@@ -4,7 +4,7 @@
|
||||
|
||||
本目录集中保存团队开发期间需要长期维护的架构、接口、实现、协作和问题复盘文档。文档按用途分类,避免设计约束、开发记录与故障复盘混放。
|
||||
|
||||
当前文档基线为 2026-09-06:第一阶段和第二阶段 A~F 工程范围已经合并到 `main`,当前可运行形态仍为 Vue/Vite Web 前端与 FastAPI AI Core。Tauri/Rust Host、Stronghold、原生多 Vault 文件系统、生产级 MCP 沙箱和 Sync Server 尚未接入。
|
||||
当前第三阶段分支已实现 Tauri/Rust 原生 Vault、Sync v1 和签名社区目录的可测试原型;它仍缺少 Sidecar、Stronghold、生产插件隔离、完整同步客户端和发布门禁,不能作为第三阶段发布候选。状态与证据见[第三阶段实施与验收记录](development/第三阶段实施与验收记录.md)。
|
||||
|
||||
仓库入口文档:[项目 README](../README.md)、[前端 README](../frontend/README.md)、[后端 README](../backend/README.md)。
|
||||
|
||||
@@ -31,11 +31,16 @@
|
||||
- [第二阶段接口契约](contracts/第二阶段接口契约-开发版.md)
|
||||
- [前端页面需求说明](contracts/前端页面需求说明-开发版.md)
|
||||
- [Tauri / Rust 桌面客户端需求说明(第三阶段,计划)](contracts/Tauri-Rust桌面客户端需求说明-第三阶段.md)
|
||||
- [Rust Host v1 预览契约](contracts/Host-v1契约.md)
|
||||
- [Sync Protocol v1 契约](contracts/Sync-v1契约.md)
|
||||
- [Community Catalog v1 契约](contracts/Community-v1契约.md)
|
||||
|
||||
运行中的后端以 `/openapi.json` 为机器可读事实来源。接口契约用于描述设计意图、联调约束和实现状态;两者不一致时,应先确认代码行为,再在同一个 PR 中同步修正文档或实现。
|
||||
|
||||
## development:开发说明
|
||||
|
||||
- [第三阶段实施与验收记录](development/第三阶段实施与验收记录.md)
|
||||
|
||||
- [前端构建分块优化开发说明](development/前端构建分块优化开发说明.md)
|
||||
|
||||
- [工作区后台索引与保存开发说明](development/工作区后台索引与保存开发说明.md)
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
> 2026-09-06 后续修复补充:本地扩展安装登记、摘要复核恢复、ZIP 卸载清理以及工作区 context_menu/toolbar 入口已在第二阶段补丁实现。下文原始基线仍保留用于追踪;第三阶段应在此基础上完成迁移、签名、升级事务及生产隔离,不重复建设基础登记。真实质量与厂商验收仍未闭环。
|
||||
|
||||
> 2026-09-07 实施进展:分支已交付 Rust 原生 Vault、Sync v1、签名社区目录和社区前端的 Alpha 原型,细粒度状态与未完成门禁见[第三阶段实施与验收记录](../development/第三阶段实施与验收记录.md)。本规划的完整退出条件尚未满足。
|
||||
|
||||
基线日期:2026-09-06。状态:**计划,尚未交付第三阶段**。本规划以当前第二阶段代码及本地验收记录为起点;本次用户明确要求将各社区、Sync Server、Tauri / Rust 容器纳入第三阶段。未勾选项均为待实施,不以文档编写或接口命名代替实现。
|
||||
|
||||
## 1. 阶段目标与完成口径
|
||||
|
||||
@@ -9,26 +9,31 @@
|
||||
| 同步入口 | 冻结 `server sync/`,生产 PostgreSQL + S3 | SQLite + 磁盘仅用于隔离协议测试 |
|
||||
| Sync v1 | CAS、幂等、设备撤销、对象上传和历史已实现 | [契约](../contracts/Sync-v1契约.md),完整生产验收待执行 |
|
||||
| 历史保留 | 永久保留 Revision、tombstone 和历史对象 | 暂不 GC,容量达到配额时拒绝上传;可逆地增加保留策略 |
|
||||
| Host | `frontend/src-tauri/`,Rust 文件日志及 outbox | Tauri 与 Sidecar 生命周期、完整交互待验收 |
|
||||
| Host | `frontend/src-tauri/`,Rust 文件日志、outbox、原生菜单与 Web/桌面适配 | [Host v1](../contracts/Host-v1契约.md),Sidecar/Stronghold/完整交互待验收 |
|
||||
| Community | Ed25519 签名目录、七类校验和前端来源入口 | [Community v1](../contracts/Community-v1契约.md),升级事务与生产运营待完成 |
|
||||
| 平台隔离 | 未证明 OS 沙箱的第三方进程默认禁用 | 能力许可不等同 OS 沙箱,不以用户点击绕过 |
|
||||
| 发布 | 不启用自动更新与安装包分发 | 签名、迁移、恢复、三平台实机尚未通过 |
|
||||
|
||||
## 已执行验证
|
||||
|
||||
- 后端:887 项 pytest 通过;新增 Web/桌面单写入者门禁测试。
|
||||
- 前端:87 个文件、471 项 Vitest 通过;TypeScript 检查与 Vite 生产构建通过,保留既有大 chunk 提示。
|
||||
- Sync v1:18 项隔离测试通过,Windows / Python 3.13.9 / SQLite / 磁盘对象 Fixture。包括双线程并发 CAS;不是 PostgreSQL 多 Worker 压测。
|
||||
- 依赖:后端和前端 frozen 安装成功;Sync 独立 uv.lock 已生成。
|
||||
- Community v1:13 项隔离测试通过;前端使用 Python 生成的真实 Ed25519 向量互验。
|
||||
- Rust:5 项文件恢复测试、clippy `-D warnings`、Tauri desktop 编译检查和 Windows GNU 开发构建通过。没有安装包或实机 UI 证据。
|
||||
- 文档相对链接、`git diff --check` 及 AI Core / Sync / Community 隔离冒烟通过。Gitea 工作流已加入,但未推送,因此无远端 Runner 证据。
|
||||
- 真实模型、个人 Vault、真实音频和凭据未作为测试输入。
|
||||
|
||||
## 完整范围跟踪
|
||||
|
||||
| 里程碑 | 状态 | 未满足退出条件 |
|
||||
| --- | --- | --- |
|
||||
| M0 | 进行中 | 社区签名契约、平台原型、负责人/预算确认、完整 Host 原型 |
|
||||
| M1 | 待开始 | 原生编辑完整闭环、Sidecar、Stronghold 迁移、菜单与多窗口 |
|
||||
| M2 | 待开始 | 社区与安装升级事务、真实包全生命周期、安全隔离 |
|
||||
| M0 | 进行中 | 平台隔离决策、负责人/预算确认、Sidecar/Stronghold 契约 |
|
||||
| M1 | 进行中 | Sidecar、Stronghold 迁移、多窗口、文件监听和完整桌面交互 |
|
||||
| M2 | 进行中 | 安装升级/回滚事务、真实包全生命周期和生产 OS 隔离 |
|
||||
| M3 | 进行中 | PostgreSQL/MinIO 运行与备份恢复、运维和负载门禁 |
|
||||
| M4 | 待开始 | Rust 上传/拉取循环、冲突 UI、两台实机离线同步 |
|
||||
| M5 | 待开始 | 七类社区、OCR、质量专项和主题/WebView 交互矩阵 |
|
||||
| M5 | 进行中 | 声明式候选尚未接 Runtime;OCR、质量专项和主题/WebView 交互矩阵 |
|
||||
| M6 | 待开始 | 安装更新签名、升级迁移、三平台和性能门禁 |
|
||||
|
||||
本机未发现 Docker 命令;Compose 尚未运行。真实厂商调用需获准账号与费用上限,音频质量需有授权且带标注的数据,当前均未执行。不能以这些外部条件缺失解释尚未实现的普通工程范围。
|
||||
|
||||
@@ -33,10 +33,10 @@
|
||||
| 领域 | 当前边界 |
|
||||
| --- | --- |
|
||||
| Agent / Streaming | 运行事件仍在进程内保存,后续需要持久化、`Last-Event-ID` 和断线重连 |
|
||||
| Security | 本地主密钥目前保存在数据目录,桌面端接入后迁移到系统凭据库 |
|
||||
| Extension | 扩展安装状态尚未持久化;MCP Host、进程隔离、签名与来源校验属于第二阶段 |
|
||||
| Security | 本地主密钥目前保存在数据目录;Rust Vault 文件服务已接入,Stronghold 迁移尚未完成 |
|
||||
| Extension | 安装状态已持久化、社区发行签名原型已接入;生产 OS 隔离和桌面 Extension Manager 尚未完成 |
|
||||
| AI Core | 音频转写当前只读取文本或 Host 预生成旁路文本,后续接入本地 ASR 队列 |
|
||||
| Desktop | Web Workspace 已连接 FastAPI 单 Vault;后续由 Tauri IPC 增加原生目录选择、多 Vault 和文件监听 |
|
||||
| Desktop | Tauri IPC 已支持单个原生 Vault 的选择、版本保存和恢复;最近列表、多窗口、监听、Sidecar 与同步循环尚未完成 |
|
||||
| Editor / Chat | 待补文件冲突合并及受控链接对话框;会话持久化已接入后端 SQLite |
|
||||
| Performance | Shiki 已复用单例,后续按首屏指标评估延迟加载或 Web Worker |
|
||||
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
"""检查仓库 Markdown 相对链接;忽略远程 URL、页内锚点与示例占位。"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
from pathlib import Path
|
||||
from urllib.parse import unquote
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
LINK = re.compile(r"(?<!!)\[[^\]]*\]\(([^)]+)\)")
|
||||
|
||||
|
||||
def main() -> int:
|
||||
failures: list[str] = []
|
||||
for document in ROOT.rglob("*.md"):
|
||||
if any(part in {".build", ".venv", "node_modules", "target"} for part in document.parts):
|
||||
continue
|
||||
source = document.read_text(encoding="utf-8")
|
||||
for raw in LINK.findall(source):
|
||||
target = raw.strip().strip("<>").split()[0]
|
||||
if not target or target.startswith(("#", "http://", "https://", "mailto:")):
|
||||
continue
|
||||
path = unquote(target.split("#", 1)[0])
|
||||
resolved = (document.parent / path).resolve()
|
||||
try:
|
||||
resolved.relative_to(ROOT)
|
||||
except ValueError:
|
||||
failures.append(f"{document.relative_to(ROOT)}: 链接越出仓库: {target}")
|
||||
continue
|
||||
if not resolved.exists():
|
||||
failures.append(f"{document.relative_to(ROOT)}: 不存在: {target}")
|
||||
if failures:
|
||||
print("\n".join(failures))
|
||||
return 1
|
||||
print("Markdown 相对链接检查通过")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
@@ -0,0 +1,61 @@
|
||||
"""在临时目录启动三个 ASGI 应用,不读取仓库 data 或调用外部服务。"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
|
||||
|
||||
def main() -> int:
|
||||
with tempfile.TemporaryDirectory(prefix="notesagent-phase3-") as temporary:
|
||||
root = Path(temporary)
|
||||
os.environ.update({
|
||||
"APP_DATA_DIR": str(root / "backend"),
|
||||
"APP_DB_PATH": str(root / "backend" / "app.db"),
|
||||
"APP_VAULT_PATH": str(root / "vault"),
|
||||
"APP_ATTACHMENTS_PATH": str(root / "backend" / "attachments"),
|
||||
})
|
||||
sys.path.insert(0, str(ROOT / "backend"))
|
||||
from fastapi.testclient import TestClient
|
||||
from app.main import app as core
|
||||
|
||||
with TestClient(core) as client:
|
||||
assert client.get("/health").json()["status"] == "ok"
|
||||
assert client.get("/api/status").status_code == 200
|
||||
|
||||
def project_python(project: Path) -> Path:
|
||||
candidates = [project / ".venv" / "Scripts" / "python.exe", project / ".venv" / "bin" / "python"]
|
||||
return next(path for path in candidates if path.exists())
|
||||
|
||||
sync_code = """from pathlib import Path
|
||||
from fastapi.testclient import TestClient
|
||||
from sync_server.app import create_app
|
||||
from sync_server.database import Database
|
||||
from sync_server.storage import DiskObjects
|
||||
root=Path(r'%s')
|
||||
with TestClient(create_app(Database('sqlite:///'+str(root/'sync.db')),DiskObjects(root/'objects'),root/'staging')) as c:
|
||||
assert c.get('/ready').json()=={'status':'ready','schema':1}
|
||||
assert c.get('/sync/v1/handshake').json()['protocol']==1
|
||||
""" % root
|
||||
subprocess.run([project_python(ROOT / "server sync"), "-c", sync_code], cwd=ROOT / "server sync", check=True)
|
||||
|
||||
community_code = """from pathlib import Path
|
||||
from fastapi.testclient import TestClient
|
||||
from community.app import Registry,create_app
|
||||
root=Path(r'%s')
|
||||
with TestClient(create_app(Registry(root/'community.db'))) as c:
|
||||
assert c.get('/health').json()['status']=='ok'
|
||||
assert c.get('/catalog/v1/sources').json()['schema_version']==1
|
||||
""" % root
|
||||
subprocess.run([project_python(ROOT / "community-server"), "-c", community_code], cwd=ROOT / "community-server", check=True)
|
||||
print("AI Core、Sync v1、Community v1 隔离冒烟通过")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
Reference in New Issue
Block a user