Blizzard
|
55b8a697aa
|
ci(gitea): 132 上扁平布局 —— compose 与 .env 同放 $REMOTE_DIR 根
deploy-132 / deploy (push) Successful in 1m45s
之前 scp 到 $REMOTE_DIR/deploy/132-app/ 并在那跑,需 .env 也放深层目录。改为 compose
直接落 $REMOTE_DIR/docker-compose.yml、在 $REMOTE_DIR 根下 up,与用户预放的 $REMOTE_DIR/.env
同级。--no-build 不碰构建上下文,compose 里的相对 build 路径在 132 上无影响。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-07-18 16:17:48 +08:00 |
|
Blizzard
|
09ac03b758
|
perf(docker): 依赖下载全部换国内源(内网构建提速)
deploy-132 / deploy (push) Failing after 5m35s
go mod download 走默认 proxy.golang.org 内网极慢。四个 Dockerfile 统一换国内镜像:
- Go: GOPROXY=https://goproxy.cn,direct(三个 Go 服务)
- alpine: apk 源换 mirrors.aliyun.com(ca-certificates/tzdata)
- pip: mcp-py 换清华 pypi 源
- npm: gateway admin 已在用 npmmirror(此前已有)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-07-18 16:03:45 +08:00 |
|
Blizzard
|
2d14245086
|
fix(docker): 运行镜像 gcr.io/distroless → alpine(内网 runner 连不上 gcr.io)
deploy-132 / deploy (push) Has been cancelled
128 的 runner 能拉 docker.io 但连不上 gcr.io(dial 443 i/o timeout),distroless 拉不到。
三个 Go 服务运行 stage 换 alpine:3.20 + ca-certificates(TLS 连 LLM/MinIO)+ tzdata +
非 root 用户。二进制 CGO_ENABLED=0 全静态,alpine 上直接跑;本地已验证构建+启动正常。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-07-18 15:54:35 +08:00 |
|
Blizzard
|
84a32023ee
|
ci(gitea): 检出改为从内网 Gitea 克隆,绕开 github.com 拉 actions/checkout
deploy-132 / deploy (push) Failing after 33s
内网 runner 连不上 github.com,actions/checkout@v4 下载 action 时 unexpected EOF。
改用 git init+fetch 从 git.sundynix.cn(同内网必达)拉当前分支,用工作流自带
github.token 认证,不再依赖 github.com。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-07-18 15:47:33 +08:00 |
|
Blizzard
|
7cacfdb5a3
|
Merge remote-tracking branch 'fork/main'
deploy-132 / deploy (push) Failing after 1m31s
|
2026-07-18 15:41:18 +08:00 |
|
Blizzard
|
e350fa9595
|
ci: release.yml 也加 Gitea 守卫(打 tag 时不在 Gitea 上误跑桌面发布构建)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-07-18 15:33:29 +08:00 |
|
Blizzard
|
05b66399f9
|
ci(gitea): 改为 runner 构建镜像 + save/scp(照 sundynix-site 模式)
之前写成 rsync 源码到 132 本地 build,需要 132 上有全套源码。改回 sundynix-site 的做法:
runner(128) 构建 4 个应用镜像 → docker save 打包 → scp 镜像包 + compose 到 132 →
132 docker load + docker compose up -d --no-build(用预构建镜像,不在 132 上 build)。
于是 132 上只有 docker-compose.yml + .env(+ 临时镜像包),没有源码。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-07-18 15:17:50 +08:00 |
|
Blizzard
|
da0d964054
|
ci(gitea): 部署目录改为 /home/workspace/sundynix-agentix
与 sundynix-site 的 /home/workspace/ 一致(用户实际部署路径)。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-07-18 15:11:10 +08:00 |
|
Blizzard
|
2599936777
|
ci(gitea): push-to-main 自动部署到 132 + 防 GitHub CI 在 Gitea 误跑
- .gitea/workflows/deploy.yml:runner(128) 检出 → rsync 仓库到 132 → 在 132 本地
docker compose up -d --build → 循环 curl :3000/healthz。在 132 构建、132 运行(不拿
4 个大镜像去 128 构建);rsync 排除 .env/node_modules,不覆盖 132 上的密钥。
Gitea secrets:DEPLOY_HOST/DEPLOY_USER/DEPLOY_PASSWORD。
- .github/workflows/ci.yml:6 个 job 加 if !contains(github.server_url,'sundynix.cn'),
Gitea 上只跑 deploy、不误跑 GitHub CI(此前为此把 Gitea 工作流整个关了)。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-07-18 14:45:24 +08:00 |
|
Blizzard
|
6b45adbbd2
|
chore(deploy): 三机部署 compose 拆分 + NATS 3 节点集群
126=MinIO(已部署)、128=基础设施、132=应用。
- deploy/128-infra:NATS 3 节点集群(独立卷+cluster routes,暴露4222/4223/4224)+PG/Redis/
Milvus(自带etcd+自带内部minio,不复用126)/Neo4j/Jaeger;端口对局域网暴露、卷持久化。
- deploy/nats-cluster/nats{1,2,3}.conf:max_payload 一致 + jetstream + cluster。
- deploy/132-app:仅 gateway(3000:8080)+dispatcher+mcp-go+mcp-py,无 admin nginx;env 指向
126/128;mcp-go 补齐 MINIO env(本会话 blob 遗漏);NATS_STREAM_REPLICAS=3;.env.example 模板。
- docker-compose.prod.yml(单机):删 admin 服务(已内嵌)、mcp-go 补 MINIO env。
- 三个 compose 均 docker compose config 校验通过;.env 已 gitignore、.env.example 可提交。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-07-18 14:45:24 +08:00 |
|
Blizzard
|
2e02a12260
|
feat(bus): JetStream 流/KV 副本数可配(NATS_STREAM_REPLICAS,默认1)
三机/单机多节点集群部署需 Replicas=3,durable 流才有 Raft quorum 容错(计费/状态/
用量/评测/审批/入库回写不因单节点挂掉而丢)。此前 6 处 CreateOrUpdateStream + 1 处
KV 桶都未设 Replicas(默认1),集群等于白搭。加 streamReplicas() 读 env(合法值 1/3/5),
7 处统一带上。默认 1 保证单机/CI 不变。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-07-18 14:45:24 +08:00 |
|
Blizzard
|
28a1543249
|
feat(gateway): 内嵌 admin 控制台(go:embed),去掉单独 nginx 容器
复刻 sundynix-site 的 webfs 模式:gateway 一个容器同时 serve 控制台 UI + API + 供
桌面端直连。admin 用 HashRouter + API base 走相对 /api/v1(构建传 VITE_GATEWAY=''),
源码零改动即可同源 serve。
- internal/webui:go:embed all:admin_dist + Dist();占位 index.html 让不构建前端也能编译。
目录命名 admin_dist(避开 .gitignore dist/ 与 .dockerignore **/dist 通配)。
- router.go NoRoute:非 /api/ 路径走内嵌静态,命中 /assets/* 直吐、否则回退 index.html;
embed 失败仅 log 降级不影响 API。/metrics /healthz /readyz /api/v1 已注册,永不进 NoRoute。
- Dockerfile 加 admin node 构建 stage,go build 前 COPY dist 覆盖 embed 占位。
- live 验证:/ 返回 admin(200)、/assets/*.js(200)、/api/v1/admin/overview(401 走 API)、/healthz(200)。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-07-18 14:45:24 +08:00 |
|
Blizzard
|
3a4e1d53a5
|
refactor(payment): 渠道抽象成 Channel 接口 —— 接新渠道=加 adapter 不动骨架
PAYMENT_DESIGN §3 承诺的 internal/payment/channel.go 适配器接口此前不存在,微信硬编码在
manager/handler 里。补齐抽象:
- channel.go:Channel 接口(Name/CreatePay/QueryOrder/VerifyCallback)+ 统一 PayIntent/PayResult
+ 渠道名常量。入参用基本类型不吃 *store.PaymentOrder,payment 包不反依赖 store。
- Wechat 实现 Channel(编译期断言 var _ Channel);QueryResult 归一为 PayResult;CreatePay 返回 PayIntent。
- Manager 从「持一个 *Wechat」改为渠道注册表:Get(name)/Available()/Status(name)/ReloadWechat;
按渠道名持有已装配实例,热重载不变。
- 回调路由收敛 /billing/callback/wechat → /billing/callback/:channel 按名路由(旧 notify URL 仍匹配);
查单/掉单补偿据 order.Channel 路由,不再写死微信。下单支持可选 channel(缺省 wechat)。
- 支付宝/Stripe 现在真·只差一个 adapter+注册。唯一未泛化:回调 ack 应答格式(现微信态,注释标明)。
- payment 包首个测试:Manager 注册表 4 用例(空/注册摘除/空配置/配置不全)。build/vet/test/lint 全绿。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-07-18 13:56:24 +08:00 |
|
Blizzard
|
86e79d76ac
|
docs(eino): 诚实修正 Phase C —— 报告流水线未 compose 化(刻意保留独立路径)
Phase C 改动清单曾把「report.go 改用 compose」标 ✅,实际 report intent 仍走独立
handleReport(writeSections 并发流水线),不经 compose.Graph。改为诚实记录:报告是
固定形状专用流水线、并行已由 writeSections 拿到,硬迁移只是拿稳定功能冒回归风险、
无用户可见收益,故刻意保留为 compose 之外的第二条编排路径。顺带把 Phase C 头部
灰度开关的陈旧描述更新为「已单引擎、graph.go 退役」的实况(与代码一致)。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-07-18 13:45:51 +08:00 |
|
Blizzard
|
1beef44625
|
ci: 加 -race + golangci-lint(new-issues) + govulncheck/gitleaks 安全扫描
补齐审计指出的「CI 缺 lint/安全扫描/-race」低成本质量门:
- go job:go test → go test -race(4 模块本地已验证 race-clean,ubuntu 自带 gcc)。
- lint job:golangci-lint(standard 集)仅 PR 跑 + only-new-issues —— 新代码必须干净,
存量 ~42 处(多为未检 Close/死代码)单独消化,不拿存量红门。加 .golangci.yml(测试放过 errcheck)。
- security job:govulncheck advisory(stdlib/nats CVE 靠 toolchain/依赖升级,只做可见性不阻断)
+ gitleaks 扫提交防密钥泄漏。
- 顺手清掉本会话新代码的 lint:blob GetBytes 的 defer Close、退款测试未检 CreateOrder。
注:gitleaks-action 个人/公开仓库免费;组织仓库需 GITLEAKS_LICENSE。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-07-18 13:44:38 +08:00 |
|
Blizzard
|
5e5f6e9610
|
feat(billing): 人工退款入口 —— paid 单冲销积分+置 refunded(幂等)
PAYMENT_DESIGN §5 承诺却只留了 OrderRefunded 常量、无入口。补齐全链路:
- store.RefundOrder:订单 CAS(paid→refunded) 为主闸(重复退 changed=false 幂等),
同事务记 adjust 负分录(ref=订单号)+ 回退物化余额。照抄 MarkOrderPaid 双闸范式;
新增 idx_ledger_refund_ref 部分唯一索引(kind='adjust' AND ref<>'')做账本级兜底,
与 grant 索引对称、不与 admin 手工校正(ref 空)冲突。
- 积分若已消费,回退后余额可为负(人工退款预期,账本仍自洽,后续消费被硬拦截)。
- handler AdminRefundOrder + POST /admin/orders/:id/refund(admin 组已挂 Audit 留痕);
真渠道钱款原路退回需 admin 另在商户后台操作,本地仅冲销积分与订单态(不接自动退款 API)。
- admin 订单流加「退款」按钮(仅 paid 单可见,二次确认+填原因)。
- 测试:RefundOrder 冲销+幂等、只退 paid 两个不变量测试(sqlite 真 DB,余额=账本之和)。
gateway build/vet/test 全绿,admin tsc 干净。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-07-18 13:35:31 +08:00 |
|
Blizzard
|
ec9431bfa4
|
fix(report): 报告源/产物落 MinIO,去掉 gateway↔mcp-go 本地盘耦合
多机/容器化第一时间断裂的隐患(ARCHITECTURE_REVIEW §7 #1):此前报告
源(reportStore)与 .docx 产物(reportExport/reportRender)都写 mcp-go
本地 SUNDYNIX_REPORTS_DIR,gateway 再按同一路径 c.File 回流——mcp-go
与 gateway 不共享磁盘即断。
- blob 包从 gateway/internal 提到 sundynix-shared/blob,gateway 与 mcp-go
共用同一 MinIO;新增 PutBytes/GetBytes 走二进制(.docx)。
- mcp-go 注入 blob:report 源/产物优先 Put 到对象存储(键 reports/<id>.{json,docx}),
导出结果返回 minio://<key>;MinIO 未就绪回退本地盘(单机降级,getSource 兼容旧本地报告)。
- gateway ExportReport 识别 minio:// → GetBytes 流式下载,否则 c.File 本地降级。
- 测试:mcp 本地回落往返单测 + shared/blob 真 MinIO 二进制往返(BLOB_TEST_ENDPOINT 门控)。
四模块 build 绿,受影响测试全过。
- 附带:go mod tidy 引入 genproto 拆包冲突,四模块统一钉单体 genproto 新版解决
(注:勿 go work sync,会剪掉 indirect require 回落旧版重现冲突)。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-07-18 13:29:31 +08:00 |
|
Blizzard
|
1c7e8e1ea9
|
docs: 修 DEPTH_ROADMAP/EINO 两处过期标记
- 删 T4.E 熔断器接回 failover 重复的过期 [ ] 项(222 行 ✅ 为准);
T2.1 熔断器联动注明已整合。
- EINO Phase D FSM 回写主题名笔误 sundynix.tasks.status → sundynix.status.task
(文档 141 行自己记过这次迁移)。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-07-18 13:29:01 +08:00 |
|
Blizzard
|
7ae7f7be67
|
feat(memory): 记忆召回加 Relevance —— Generative Agents 打分补齐第三项 (P1)
审计三真桩之一。记忆召回此前打分只有 Recency+Importance,缺 Relevance(对当前
任务的语义相关性)——注释写"待接 Milvus",但召回时甚至不知道当前问什么。
关键发现:dispatcher 注入点 fetchMemory(ctx,uid,_) 手上已有当前任务文本(b.query),
只是被 `_` 丢弃了。所以不是"接 Milvus"那么重,把 query 一路传下去 + 缓存嵌入即可。
设计(偏离注释的"接 Milvus"——用户偏好量小,不值当上向量库):
- Profile 加 embedding 列(float32 小端打包存 bytea);Upsert 时对 value 向量化缓存
(value 没变不重算,失败留空不阻断)。
- memory 包定义 Embedder 小接口,gateway 注入 rag.Engine(复用同一控制面下发的
embedding 模型),不硬依赖 rag 内部;rag.Engine 加导出 Embed 方法。
- memory_get 工具加可选 query 入参;fetchMemory 停止丢弃 b.query 传下去。
- Get(ctx,uid,query):query 非空且 embedder 就绪 → embed(query) 对每条缓存向量
内存算余弦 → 三项打分 0.25R+0.35I+0.4Rel;否则回落两项(升级前行为)。
- 优雅降级贯穿:无 query/无 embedder/query 嵌入失败/行无向量 → 静默回落,绝不报错。
零 Milvus 依赖、零向量库同步问题、保住"没 embedding 也能跑"。
验证:单测(编解码往返/cosine 截0/三项模式相关性翻转顺序/降级返 nil)+ 端到端
(真 PG:写入即向量化、query=咖啡把低重要度的咖啡记忆翻到运动前面)。migration
加列已 live;embedding 复用 RAG 已验证基建。三模块 build/vet/test 全绿。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-07-18 12:47:55 +08:00 |
|
Blizzard
|
a742d118ad
|
test(web): 薄 Web 面加 RTL 组件测试 —— 补最大覆盖缺口 (P1)
完成度审计:web 端几乎零覆盖(仅3个 api 纯逻辑用例、无任何组件测试)——三前端里
最薄。补上 RTL 基建 + 入口流组件测试。
- 加 @testing-library/react+jest-dom+user-event,src/test/setup.ts,
vitest setupFiles 接入。
- AuthPage.test.tsx(5 例,mock api 层):登录/注册态切换(名字字段显隐)、
邮箱密码空时按钮禁用、登录成功回调 onAuthed 且带对参数、登录失败显示后端
错误文案不回调、注册态走 authRegister 带名字。
web 测试 3→8。CI web 矩阵已跑 npm test,自动纳入关卡。
tsc + 三前端(desktop 68/admin 41/web 8)全绿。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-07-18 12:28:57 +08:00 |
|
Blizzard
|
e29bc9a91e
|
feat(admin): 「数据源 & RAG」页做实 —— admin 三 mock 页清零 (P1)
审计 P1「admin 三页纯 mock」最后一页。此前 DatasourcesPage 的 GraphRAG 拓扑图
写死节点、「向量/全文/图谱权重滑块」纯 mock——而且权重概念本身虚构:mcp-go 的
RRF 融合是各路等权的倒排互惠融合(rrfK=60 平滑常数),根本没有"每路占几成"。
- store/datasource_query.go:AllDatasources(全平台 KB + 各库文档数/总字数,
按 (space_id,name) 关联 doc,WithoutTenant);GET /admin/datasources(含
SystemCounts 平台计数)。
- DatasourcesPage 重写:保留真实 Embedding 模型配置(ModelManager) + 诚实的
混合检索管线说明(三路 Milvus/Bleve/Neo4j + RRF 等权融合 k=60,非可调权重) +
平台计数卡片 + 真知识库清单表。删假滑块+假拓扑(~200行 mock)。
诚实边界:RRF 无每路权重,不摆假滑块;检索参数在 mcp-go 代码中。
live:/admin/datasources 返 34用户/25库/53文档,清单带真实文档数字数;
浏览器渲染全对。tsc+41 vitest 全绿。**admin 三 mock 页(Evals/Guardrails/
Datasources)全部做实。**
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-07-18 12:26:16 +08:00 |
|
Blizzard
|
c02ebc7bce
|
feat(admin): 「安全护栏」页做实 —— 接真护栏事件,去 mock (P1)
审计 P1「admin 三页纯 mock」之二。此前 GuardrailsPage 是写死的正则/敏感词
编辑框(改了不生效的假配置)+编造拦截日志。后端 /admin/guardrail-events 早已
现成(T4.B),纯前端做实。
- 命中事件流接真数据(guardrail_event,middleware.Guardrail 命中即落库):
blocked 硬拦/suspect 灰区放行,带原因/信号/路径/来源;计数卡片+原因 Top 分布
(客户端从近100条聚合)+按 kind 筛。
- 诚实处理假配置:护栏规则(Tier1 正则/词库 + Tier2 LLM 分类器)是中间件代码
常量、非运行时可配,故删掉"能改却不生效"的编辑框,换成只读规则说明 + 指出
运行时可配需另建配置存储(参考提示词控制面)。不摆假控件。
live:触发注入越狱输入→422 硬拦+落库(reason「疑似提示词注入」)→页面渲染
真事件流+原因分布。tsc+41 vitest 全绿。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-07-18 12:07:32 +08:00 |
|
Blizzard
|
d04d830c37
|
feat(admin): 「自动评测」页做实 —— 接真评测数据,去 mock (P1)
审计 P1「admin 三页纯 mock」之一。此前 EvalsPage 是写死的质量趋势+编造的
错题本+虚构纠偏轨迹。现接 sundynix_eval 真数据(评测经 JetStream eval 流持久
落库,刚升级)。
- store/eval_query.go:EvalTrend(按天 avg 综合分/忠实度+低分计数)、EvalSummaryFor
(ok/warn/poor/corrected 计数+均值)、PoorEvals(错题本,level in poor/warn +
评语+纠偏标记+租户名)。全 WithoutTenant 平台口径;忠实度均值只算 sources>0
(无来源的忠实度恒0会压低失真)。
- GET /admin/evals?days=(RequireAdmin);admin api.ts + EvalsPage 重写:
总览卡片(综合分/合格率/低分占比/纠偏采纳率)+质量&忠实度趋势(纯SVG折线+低分
背景条)+错题本(点行展开评语)。
- 诚实边界:纠偏「前后全文对照」后端未持久化,只存了 Reason/Corrected/各维度分,
故错题本展示评语+「已纠偏」标记,不再编造 before/after。
live:/admin/evals 返 57 次评测 avg=0.88、错题本10条、11天趋势;浏览器渲染
真数据(趋势线07-15真实下探)。go+tsc+41 vitest 全绿。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-07-18 12:03:54 +08:00 |
|
Blizzard
|
a1c35852ef
|
chore: 删死代码 —— OpenReport/ReadLocalFile/isDesktop/openReport + admin Soon (P2)
完成度审计确认全链零生产调用:
- app.go OpenReport(仅被死包装引用)、ReadLocalFile(仅测试引用);
desktop.ts openReport 包装、isDesktop 导出(实际用 isMacDesktop)——全删。
绑定重新生成(OpenReport/ReadLocalFile 归零,PrintReportPage/SaveReportAs/
Notify 保留)。删对应的 app_test TestReadLocalFile。
- admin Soon.tsx(规划中占位组件)已成未引用死组件——routes.tsx 9 条路由全
ready:true,import 未用。删组件+import。
openInSystem/filepath/os 仍被 PrintReportPage/download 使用,不孤立。
desktop go test + tsc + 68 vitest、admin tsc + 41 vitest 全绿。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-07-18 11:56:51 +08:00 |
|
Blizzard
|
940330cdb7
|
feat(eval): 评测结果回写升 JetStream 持久 —— 消灭最后一处 core NATS 回写 (P1)
完成度审计 P1 + 记忆 nats-durability 的既定规矩「计费/需落库的回写一律
JetStream+幂等,别fire-and-forget」。此前 eval 是全仓最后一处 core NATS pub-sub
回写:网关离线/慢消费者时评测结果直接丢——而质量趋势/门控都依赖它。
照抄已升级的 status/usage 范式(同为 dispatcher→gateway→PG 回写):
- contract 加 StreamEval/ConsumerEval;bus 加 EnsureEvalStream + ConsumeEval
(durable consumer,AckExplicit,落库失败 Nak 重投自愈),PublishEval 改
js.Publish 同步等 ack。删 core NATS 的 SubscribeEval。
- gateway/dispatcher 两个 wrapper 在 connect 时 EnsureEvalStream;gateway main
的评测订阅从 SubscribeEval(fire-and-forget)换 ConsumeEval(handler 返 error→
Nak),接入优雅停机 drain。
- 幂等前提已满足:SaveEval 按 task_id upsert,at-least-once 重投只覆盖不重复。
验证:e2e 去重测试(TestGatewayQueueDedup)升级到 ConsumeEval,50 条两副本合计
处理50次零重复;live 真端到端:提交任务→跑完→评测经新 eval 流落 PG(level=ok)
一次成功;两服务启动 eval 流 ensure 无报错。go build/vet/test 全绿。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-07-18 11:52:40 +08:00 |
|
Blizzard
|
3d15cdb493
|
fix(gateway): 任务落库失败不再吞 —— 关键写失败上浮 5xx (P0-2)
DEPTH_ROADMAP:225 未完项 + 完成度审计 P0-2:launch() 里 SaveTask 是 best-effort,
DB 写失败只 log 却照样 PublishTask + 返 202。结果任务发出去在后端跑了,却不进
运行历史、复盘不了、报告类的用户切页面回来彻底找不回——用户以为成功、实际没落库。
- SaveTask 对「DB 降级(nil)」返 nil、对「DB 活着写失败」返真 error,天然可区分:
前者静默跳过(开发态本就无库),后者上浮为提交失败。落库在 Publish 之前,
失败时还没发布,中止干净、不产生"看不见的执行"。
- 两个调用点(SubmitTask/GenerateReport)已把 launch 错误映射 5xx,无需再改。
范围克制:审计列的其它 best-effort 点保持不动——dispatcher 的异步回写
(UpdateTaskStatus/SaveTaskOutput/SaveTaskTrace)、审计日志、用量累计计数,
都不是"用户等着响应"的路径,没有请求可返 5xx,best-effort 是对的。
唯独 launch 这处是"用户以为提交成功实际没有",才该阻断。
live 验证:改名 task 表模拟 DB 写失败 → 提交返 502+明确文案(不再假 202);
表改回 → 立刻恢复 202。happy path 202+落库不变。go build/vet/test 全绿。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-07-18 11:45:08 +08:00 |
|
Blizzard
|
bff6e5c7fd
|
test(gateway): 补上「钱路径」与租户隔离的回归测试 —— P0 最高优先级缺口
完成度审计(见记忆 completion-audit)最尖的一条:涉及钱的路径此前 0 测试,
支付一上线 bug=真实错账;租户数据层隔离也只测了角色门禁没测数据层真隔离。
把本会话 live 手验过的断言固化成回归测试。
测试基建:纯 Go sqlite(glebarez,无 CGO)内存库,迁同款模型+建部分唯一索引+
挂租户作用域回调,复用生产 store 方法测真逻辑。CI ubuntu 无 Postgres 也能跑
(此前 store 测试全是纯逻辑,DB 事务逻辑从没进过关卡)。
钱路径不变量(6):
- GrantCredits 记分录+增余额,余额恒等于 SUM(ledger)
- 兑换码核销一次性(CAS)+原子入账,重复核销余额纹丝不动
- 账本(kind,ref)部分唯一索引:重复 grant 被兜底拦下、usage 不受约束
- MarkOrderPaid CAS 幂等:重复回调 changed=false 不重复入账
- SaveUsageEvent task_id 幂等:同任务重投不重复扣费
- ReconcileOrders 抓出 order_without_ledger(钱到了积分没给)
租户隔离(3):创建自动填 tenant_id、查询按 ctx 租户过滤、跨租户改/删命不中、
WithoutTenant 系统视角全可见。
go build/vet + 全 gateway 测试全绿;sqlite 仅测试引用,不进生产二进制。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-07-18 11:39:36 +08:00 |
|
Blizzard
|
3db2de1ef6
|
feat(billing): 支付 P5.3 —— 掉单补偿定时器 + admin 订单流 + 日终对账
支付线封口。此前 pending 单只在「用户开着账单页轮询」时才查单确认——用户扫完码
关页面,钱付了、积分永不到账。
- 掉单补偿定时器(payment_reconcile.go):gateway 内每分钟扫 pending 微信单,
逐单 reconcileOrder 主动查单落态。把「用户在不在场」从入账链路摘掉。
reconcileOrder 从 BillingOrderStatus 抽出、前端轮询与定时器共用一份幂等
落态逻辑(不重蹈 GenerateReport/SubmitTask 的漂移)。渠道未配置时空转不炸。
- admin 订单流 GET /admin/orders(状态计数+全平台订单,可筛)。
- 日终对账 GET /admin/orders/reconcile:paid 单 ↔ 账本 grant 分录逐单比对,
抓 order_without_ledger(钱到了积分没给,最严重)/ ledger_without_paid_order。
- admin 计费页「充值订单与对账」块:计数卡片+订单流+一键对账。
⚠️ live 抓到并修掉一个真 bug:OrderStats 复用同一个 gorm.DB 链式 Count 三次,
WHERE 累加成 status=A AND status=B → 恒 0(订单流显示 2 单但计数全 0)。
改成每次起新 query builder。—— 又一次只有 live 才暴露的。
验证:go 6 包测试+tsc+41 vitest 全绿;live 造差异单对账正确抓出
order_without_ledger、清账后回零差异;补偿器启动日志+渠道未配置空转不炸;
浏览器验订单流卡片+一键对账绿条。TTL 过期路径需真渠道触发,部署后自然覆盖。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-07-18 11:25:37 +08:00 |
|
Blizzard
|
3767c78ee8
|
chore: gitignore desktop 模块目录裸二进制(go build ./... 遗留)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-07-18 11:13:48 +08:00 |
|
Blizzard
|
53d9f49e96
|
docs: create VOICE_DESIGN.md and update go.work.sum dependencies
|
2026-07-17 22:54:53 +08:00 |
|
Blizzard
|
5d7eca5de3
|
fix(billing): 微信支付钉死「微信支付公钥」验签体系 —— 商户 2025-09 开户没有平台证书
用户拿旧项目代码对出来的真问题:我此前用 WithWechatPayAutoAuthCipher(平台证书
模式,APIv3 密钥自动下载平台证书验签),但 2024 起新注册商户只发「微信支付公钥」
(PUB_KEY_ID_ 开头)、没有平台证书——在该商户号上初始化/回调验签都会挂。
- 改 WithWechatPayPublicKeyAuthCipher(商户私钥+公钥ID+公钥文件);回调验签用
NewSHA256WithRSAPubkeyVerifier;平台证书模式不留双模式赘肉(YAGNI)。
- Config 增 public_key_path/public_key_id(必填,公钥文件同样只存路径);
admin 卡片补两字段;env 兜底加 WECHAT_PUBLIC_KEY(_ID)。
- 顺手修 live 撞出的真 bug:sundynix_setting.value 是 varchar(255),
支付配置 JSON(含加密密钥)一条就超(SQLSTATE 22001)→ 改 text。
live:列类型已迁 text;缺公钥两项报「配置不全,缺: public_key_path,
public_key_id」;GET 回显含新字段。go 6 包测试+tsc+41 vitest 全绿。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-07-17 11:12:26 +08:00 |
|
Blizzard
|
d1e1e0fc4a
|
feat(billing): 微信支付配置进 DB —— admin 控制面保存即热生效
用户定的形态:配置存数据库、密钥文件放服务器磁盘(库里只存路径)。
env 降级为兜底(DB 优先 → env → 隐藏,与 tokens_per_credit 同一约定)。
- payment 包重构:Config(6 字段)+ Manager(RWMutex 热重载,学 prompt 控制面
改完即生效不重启);未启用原因人话化(未配置/缺哪些字段/初始化失败具体错)。
- APIv3 密钥入库前 AES-GCM 加密(shared/secrets,与模型 API Key 同一把
SUNDYNIX_SECRET_KEY);GET 只回 has_apiv3_key 不回显;PUT 留空=沿用旧密钥
(只写不回显语义,同模型 Key)。
- admin GET/PUT /admin/payment/wechat;业务路径全部改经 Manager.Current()
取快照(BillingPacks/下单/查单/回调)。
- admin 计费页「微信支付配置」卡片:状态徽章(已启用/未启用+原因)+六字段
+保存并热生效。
live:无配置→「未配置」;存假配置→热重载报「私钥加载失败:decode err」;
去掉 appid→「配置不全,缺: appid」;密钥留空沿用(has_apiv3_key 保持 true);
psql 复核库内密文 enc:1: 前缀、不含明文子串。go/tsc/41 vitest 全绿。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-07-17 10:55:30 +08:00 |
|
Blizzard
|
efd185b779
|
feat(billing): 支付 P5.2 —— 微信支付 Native 渠道(扫码充值)
wechatpay-go v0.2.21。凭据全 env 注入(WECHAT_MCHID/MCH_CERT_SERIAL/
MCH_PRIVATE_KEY/APIV3_KEY/APPID/NOTIFY_URL),缺一渠道即隐藏——半配置/假凭据
只打日志不拖垮 gateway(用假私钥实测过降级)。
- internal/payment:Native 下单出 code_url、APIv3 回调验签解密、主动查单,
三者统一收敛为 QueryResult。
- 下单 POST /billing/orders {pack_id}(≥member+审计):金额/积分按在售包服务端
锁定进订单行,不信任客户端;渠道下单失败即作废,不留付不了的 pending。
- 到账两条路汇入同一个 MarkOrderPaid 幂等闸(CAS+唯一索引双闸,同 P5.1):
①公开回调路由(验签是唯一的门;金额与订单不符不入账);②前端轮询的
GET /billing/orders/:id 在 pending 时顺路主动查单——本地/内网收不到公网
回调也能确认到账,回调只是生产更快的通道。pending 超 30 分钟置 expired。
- Web 面:在售包卡片(渠道亮才出现)→扫码弹窗(qrcode 画 code_url,二维码底色
固定纯白——暗色主题下低对比码扫不出来)→2.5s 轮询→到账 toast+刷余额。
验证:go/tsc/vitest 全绿;无凭据+假凭据两种降级 live 四连
(channels 只剩 redeem/下单 400 引导兑换码/回调 503/兑换码闭环不受影响)。
⚠️ 真通道(prepay→扫码→回调/查单→入账)需真实商户号,未 live——用户配好
env 后用小额包实测,建议先配 ¥0.01 测试包走一单。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-07-17 10:39:24 +08:00 |
|
Blizzard
|
929bbf334b
|
feat(admin): 计费页长出「充值渠道」块 —— 兑换码生成/台账 + 积分包定价
P5.1 收尾:此前生成码只有 API。计费页现在从上到下 = 计费规则(积分→token
汇率) → 充值渠道(钱→积分:兑换码 + 微信定价用的积分包) → 用量观测,
两层汇率在同一页可见、各管各的。
- 兑换码:面额/张数/备注生成;**明文码只在生成响应显示一次**(等同现金,
台账 GET /admin/redeem-codes 服务端脱敏只露首尾,丢码重生成、不提供找回
——顺手把接口这个第二明文出口堵了);台账含核销状态。
- 积分包:新增/上下架(微信 P5.2 上线前把定价面备好);admin api.ts 补
packs/redeem-codes 四个函数。
- launch.json 加 admin-console-alt(:5176)——5174 被用户自己的 sundynix-site
占着,不动别人端口。
live:5176 登录→生成 5 张(绿色一次性面板+复制全部)→配「入门包 1000分/¥9.9」
在售可下架→台账脱敏 curl 复核;tsc+41 vitest 全绿。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-07-17 10:28:03 +08:00 |
|
Blizzard
|
8e05f4b3fe
|
feat(billing): 支付 P5.1 —— 订单骨架 + 兑换码渠道全闭环
按 PAYMENT_DESIGN.md 施工。零资质渠道先把「订单→入账→对账」的骨架跑真,
微信 Native(P5.2)进来只是多一个 adapter。
- store/payment.go:CreditPack(admin 可配包)/PaymentOrder(支付侧事实源,兑换
也写单,全部充值一个查法)/RedeemCode(SDX-XXXX-XXXX-XXXX,crypto/rand,剔除
易混字符)。三模型都不标 isTenantScoped——订单归计费租户,与活跃租户可能
分叉,插件自动注入会写错归属(RecentRuns 同款教训),显式赋值+显式过滤。
- Redeem 单事务:码 CAS 占用(unused→used 只成功一次,幂等主闸)→建已支付
订单→账本分录+物化余额。credit_ledger 加 (kind,ref) 部分唯一索引兜底
——GrantCredits 此前对 ref 零约束,回调 at-least-once 就是重复入账事故。
- 路由:/billing/packs|orders(查,全员) + /billing/redeem(≥member+审计);
admin /redeem-codes(生成/列表) + /packs(配包)。
- Web 面账单页:兑换码输入(viewer 不摆输入框,真闸在后端)+最近充值订单流。
- 测试:码形态/200 样本无撞码/参数边界;go+tsc+vitest 全绿。
live 13 项:生成→核销余额精确+100→重core 400(主闸)→DB 直插重复 ref 被唯一
索引打回(兜底闸实证)→viewer 403→balance==SUM(ledger) 不变量→审计留痕→
浏览器 UI 兑换 100→200+订单流展示。
已知余项:admin 生成码暂只有 API(admin 页 UI 下一刀);微信 adapter=P5.2。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-07-17 10:18:14 +08:00 |
|
Blizzard
|
039e7a2f06
|
docs(payment): 渠道拍板微信支付 Native + 两层汇率解耦写明
- P5.2 = wechatpay-go(Native 下单 code_url→二维码,APIv3 验签解密);商户号/证书
env 注入,未配置渠道自动隐藏只剩兑换码,半配置状态不许把下单路由搞出 5xx。
- 用户强调积分↔token 要可动态调:第二层 tokens_per_credit 是 P2 期现成的
(admin 计费页,DB 热生效);本期只新增第一层(积分包定价,admin 配包)。
订单锁定下单当时的包价,改包不影响已付订单。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-07-17 09:55:02 +08:00 |
|
Blizzard
|
d5836fd190
|
docs: 支付设计一页纸(P5) —— 预付积分包路线,渠道适配器,双闸幂等入账
订阅制砍掉(等付费用户拉动),充值积分包复用现成 credit_ledger/GrantCredits;
指出必须先补的闸:GrantCredits 对 ref 无唯一约束,支付回调 at-least-once
会重复入账——订单状态机 CAS 主闸 + (kind,ref) 部分唯一索引兜底。
P5.1 用零资质的兑换码渠道先跑通全闭环,真渠道(支付宝/微信/Stripe)等拍板。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-07-17 09:47:41 +08:00 |
|
Blizzard
|
206cd51efd
|
ci: 补 desktop 覆盖 —— go 模块进关卡 + 三个前端跑 vitest
两个此前的盲区:
- sundynix-desktop 的 Go 模块不在 go.work,go job 的四模块循环从来没测过它
——app.go 桥方法(download 残file/另存为)的单测在 CI 一次没跑过。
新开 desktop job 用 macos-latest:免装 gtk/webkit(linux 编 wails 一堆 CGO 头),
且 darwin 才是实际发行目标;go:embed 需要 frontend/dist,先 npm build。
- web job 只跑 tsc:desktop 68 例、admin 41 例、web 3 例 vitest 全都不进关卡。
统一补 npm test。
全部命令本地(macOS,与 runner 同环境)原样跑过:前端构建/go build+vet+test/
三处 vitest 112 例全绿;YAML 解析与 GOWORK 引号校验过。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-07-17 09:41:41 +08:00 |
|
Blizzard
|
b4012dbbba
|
fix(desktop): 报告 PDF 导出在壳内改走原生桥 —— WKWebView 拦死 window.open
实机验证抓到的:桌面壳内点 PDF 报「打印窗口被拦截」——Wails v3 的 WKWebView
把 window.open 拦成 null,前端弹打印窗那条路在壳内根本走不通(浏览器预览没事)。
- app.go 加 PrintReportPage(filename, html):打印视图 HTML 落临时文件(文件名
过滤路径字符),openInSystem 交系统默认浏览器打开,页面 onload 自动唤起打印框,
用户直接「存储为 PDF」。CJK 零字体依赖的原有优势不变。
- desktop.ts printReportHtml 改 async:inWails 走原生桥,浏览器维持 window.open;
RunsView 调用点随之 async + 错误透 toast。
- 绑定重新生成(注意要 `wails3 generate bindings -ts`,裸跑默认吐 JS 且要
GOWORK=off,否则 go.work 干扰找不到 Service)。
验证:go test/tsc/68 例 vitest 全绿;壳内被拦是用户实机复现的。
⚠️ 原生桥新路径(临时文件→浏览器→打印框)用户尚未实机点验,重新打的包已就位,
下次跑报告顺手验。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-07-17 09:40:15 +08:00 |
|
Blizzard
|
f0f9eccf6f
|
Merge pull request 'feat(memory): P1 长期记忆升级 —— 异步攒批 Consolidate + 软删 + importance/last_seen' (#1) from feat/wails3 into main
Reviewed-on: #1
|
2026-07-17 01:12:31 +00:00 |
|
Blizzard
|
7c67256f87
|
feat(web): 薄 Web 面 sundynix-web —— 注册/组织/团队/账单自助入口
CI / Go · build + vet + test (pull_request) Failing after 30m3s
CI / Frontend · tsc (sundynix-admin) (pull_request) Failing after 9m42s
CI / Frontend · tsc (sundynix-desktop/frontend) (pull_request) Failing after 16m53s
CI / Frontend · tsc (sundynix-web) (pull_request) Has been cancelled
CI / mcp-py · sandbox guard (pull_request) Has been cancelled
SaaS P3 收口第二刀(设计见 SAAS_DESIGN.md §8)。第三个产品面:desktop=用户
工作产品、admin=平台超管控制塔、web=租户客户的自助柜台——做「装桌面端之前
就要能用」的那些事,三者不合并。
- 骨架抄 admin(HashRouter+路由注册表+me()/sdx:logout 鉴权门+vite/vitest
单文件配置);UI 整目录搬 desktop 的 ui/ 组件+ink/brand 主题 token(亮暗
双主题),品牌观感与桌面端一致;api.ts 抄 desktop 的 auth/tenant/usage 段
(纯 fetch 零 Wails 依赖),成员管理/建组织打新的租户自助接口。
- 页面:登录注册(一页两态)/概览(组织+角色+余额+桌面端下载指引)/团队(名册+
邀请+改角色+移除,写控件按角色显隐、真闸在后端)/组织(列表+自助新建+切换)/
用量与账单(余额 hero+趋势+合计+最近消耗,数据全来自现成 /me/usage;充值
只放说明占位,支付 P5 再接,不做假入口)。
- 工程:vite :5175、launch.json 配置、make webface、ci.yml web 矩阵纳入。
- 测试:tsc 干净;vitest 3 例(fmtCredits 去尾零——测试先行抓出 1.50 毛刺;
ASSIGNABLE_ROLES 不含 owner)。浏览器 live 全流程:甲(owner)登录→概览→
团队邀请乙→乙(member)登录写控件全隐藏、名册只读,组织/用量页真数据渲染。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-07-16 17:47:23 +08:00 |
|
Blizzard
|
65340616df
|
feat(gateway): 租户自助管理接口 —— 薄 Web 面增量1
SaaS P3 收口第一刀。成员管理此前只有平台超管口径(/admin/tenants/:id/*),
租户 owner 无法自助管自己的组织;建租户同样只有 admin 能做。
- 成员自助四路由(作用于活跃租户,id 取 ctx 不接受路径任意租户):
GET /tenants/current/members(≥viewer) + POST/PUT/DELETE(≥admin+审计)。
RequireTenantRole 是 RBAC 极简版预留的架子,这次直接挂上,零改动生效。
- handler 挡两个平台超管可以、租户 admin 不该有的动作:邀请为 owner;
借 AddMemberByEmail 的 upsert 语义「重新邀请」owner 实现降权——后者
是真实的越权路径,admin 拿 owner 邮箱重邀成 viewer 就把 owner 拉下马。
- POST /me/tenants 自助建组织:创建者即 owner、建完切入;slug 用雪花 id
生成不让用户填(全局唯一,自助场景撞名只添乱);owner 没挂上按错误返回
而不是 warn+200(孤儿租户没人能管)。
- CORS 支持逗号分隔多 origin(desktop 预览/薄 Web 面/admin 不同源):命中
白名单回显请求 Origin+Vary;单值/通配/生产未配置全拒的旧语义不变。
- 测试:RequireTenantRole 补 ≥admin 门槛用例;cors 四场景单测。
live 验证 13 项全过(门槛/owner 保护/降权攻击挡下/CORS 回显/审计落痕)。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-07-16 17:33:04 +08:00 |
|
Blizzard
|
d14a10479e
|
fix(desktop): 补回报告页剥离时漏搬的 PDF 导出与完成系统通知
759483b 把报告页剥成纯启动器时,Word/Markdown 导出搬去了运行页,但漏了两个:
printReportHtml(前端打印出 PDF,CJK 零字体依赖)和 notify(完成弹系统通知)
外部调用点归零,成了被孤立的活功能。
- 运行页报告正文面板补 PDF 按钮:previewRef 抓已渲染 DOM 送打印视图,
与剥离前同一条路径。
- 完成通知挪进 attachRun 的 token 流 done 回调并加 label 参数:原先只有
报告会通知,但「跑几分钟、人早切走了」对编排任务一样成立,且 attachRun
是所有运行的唯一汇合点,放这儿不会再漂移。恢复的待审任务也带主题。
验证:tsc 干净、68 个 vitest 全过、两函数调用点恢复非零。
⚠️ 未实机点验(需登录态):PDF 按钮实际点击出打印视图、通知实际弹出,
下次起服务后补验。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-07-16 16:28:28 +08:00 |
|
Blizzard
|
027ccc0ccc
|
chore(desktop): 版本号三处对齐 0.1.1,追平已存在的 v0.1.1 tag
git tag 已打到 v0.1.1,但 APP_VERSION/package.json/build/config.yml 全停在
0.1.0 —— 按更新提示的比对逻辑这会错乱。三处统一 0.1.1,并跑
wails3 task common:update:build-assets 重新生成原生元数据(Info.plist 等,
CFBundleShortVersionString 已核实落到 0.1.1,包名/标识符未被冲回模板值)。
生成器顺手拉的 build/ios/ 脚手架已删,不做 iOS。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-07-16 16:28:03 +08:00 |
|
Blizzard
|
a779953ed9
|
docs(go.work): 注释里的 wails build 是 v2 说法,改 wails3 package
v3 的 build 只出裸二进制,出 .app 得 package;顺带点明任务链自带前端构建,
不用手动 npm run build。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-07-16 15:16:54 +08:00 |
|
Blizzard
|
a5cf2c5f36
|
ci: 构建链补完 wails v2→v3 迁移,CI 触发收敛到 main
代码早就全量迁到 v3(go.mod 只要 wails/v3、main.go import v3/pkg/application),
但"怎么构建"这一层从没跟上,全还是 v2:
- release.yml 装 v2 CLI(wails@v2.12.0)去编 v3 代码,编不过;产物路径也还
指向 v2 的 build/bin/,而 v3 落在 bin/。这条只在打 v* tag 时触发,迁移后
一次都没跑过,所以坏了半个月没人知道 —— 下次发版必炸。
- Makefile 的 wails dev/build 直接 command not found。
- README 叫人装 v2 CLI;RELEASE.md 还在提早已删除的 wails.json。
改动:
- ci.yml 触发从 [main, dev] 收到 [main]。dev 分支已删,开发在 feat/wails3,
留着 dev 是死条件;PR 以 main 为目标仍会跑,合入前有关卡。
- release.yml 全量转 v3:
* CLI 钉死 v3.0.0-alpha2.117,与 go.mod 的 wails/v3 版本一致 —— alpha 阶段
CLI 生成的 bindings 和运行时配套,版本错开会出难查的怪问题。
* mac 走 wails3 task darwin:package:universal。v3 的 build 只出裸二进制
(macOS 上双击不起窗),必须 package 才有 .app;本地实跑验证过,
lipo -archs 确认是真 universal(x86_64 + arm64)。
* Windows 保持 v2 时代行为发裸 .exe。v3 的 package 默认走 NSIS 出 installer,
依赖 runner 上有 makensis,属额外未知数,要升级安装包再单独议。
* 产物路径 build/bin/ → bin/。
* fail_on_unmatched_files 改 true,并按 matrix 只匹配自己那个产物 ——
原来是 false 且两个平台都列全量文件,产物路径写错会安静发一个没附件的
Release。这次的 bug 正是路径漂移,得让它当场炸。
- RELEASE.md 版本号对齐表补上第三处 build/config.yml 的 info.version(此前写着
"必须三处对齐"却只列了两处,第三处是已删的 wails.json),并记下改完必须跑
wails3 task common:update:build-assets 重新生成 Info.plist —— 这坑踩过。
- 清掉 v2 残留:frontend/wailsjs/(v2 绑定,v3 用已跟踪的 bindings/)及其
gitignore 规则、build/bin/ 里 v2 时代的 .app。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-07-16 15:03:32 +08:00 |
|
Blizzard
|
759483bf92
|
refactor(desktop,gateway): 用户端去重 —— 一个功能一个入口,一份数据一个查法
用户反馈「UI 太乱」。数出来的:report/kb/memory 各 4 个入口、studio 5 个;
运行历史两套渲染。乱的机制是同一件事写了两遍,改一处另一处必然掉队。
**一份数据一个查法**
- 工作台「最近任务」和运行页「运行历史」是同一批数据,却各查各的:
前者走 store.RecentTasks(),后者走 RecentRuns()。于是我给运行历史加的
topic 字段工作台完全没跟上,还在 mono 显示 report_<hex>;而且 RecentTasks
没有租户过滤,口径也不一致。现在统一走 RecentRuns,RecentTasks 删掉
(它只有那一个调用点)。前端 recent_runs 也改用 RunSummary 同一个类型。
**一个功能一个入口**
- 工作台删掉「入库知识/生成报告/管理记忆」按钮排 —— 跳的目标和上面四张
能力卡片完全重合,同一页给两个入口。
- 「最近任务」现在点击直达运行页对应那条(App 加 goto(view, taskId) →
RunsView focusTaskId),而不是只把页面切过去让用户自己再找一遍。
**报告归 RUN,只做启动器**
- 报告是「执行」不是「构建」,从 BUILD 组移到 RUN。
- 报告页砍掉「执行轨迹」「报告正文」两个面板 —— 和运行页完全重复,而且
那套是组件本地 state,切页面就没。现在它只有主题输入框。
- 报告改走 App 的全局运行态(新增 onRunReport,与 onRun 同构、复用 attachRun):
提交 → 跳「运行 · 观测」→ 实时看轨迹/正文/导出。App 里 onRun 早就写着
「发起即跳运行页,观测统一收敛在此页」——这条方针一直在,只有报告没遵守。
**顺带清死代码**
- 市场(规划中)从导航下架,ViewKey 里的 "market" 和 Boxes 图标一并删。
- PLACEHOLDERS 整块死代码:它列的 home/kb/report/runs 全都早已真实实现,
market 是唯一还可能用到的、下架后那个 Placeholder 分支永远走不到
(tsc 报 "Spread types" 就是因为 PLACEHOLDERS[view] 已成 never)。
连同 views/Placeholder.tsx 一起删。
live 验证(真账号,桌面端实机):报告页输入主题 → 提交 → 自动跳运行页 →
顶部「当前运行 · live」→ 轨迹实时跑 → 报告正文流式出 + 导出按钮在位 →
report_7a0110a10b534f90 落库带 topic「归口测试」,POST /api/v1/reports → 202。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-07-16 14:08:16 +08:00 |
|
Blizzard
|
fd99ba6226
|
feat(desktop,gateway): 报告归口运行页 —— 能找回、能复盘、能导出
承接上一个提交(报告终于落库进运行历史),把「所有执行归口运行页」做完整:
只能看不能存,不算归口。
- 运行页的输出面板:报告类运行显示「报告正文」并在右上角出 Word/Markdown
导出。报告页那两个导出按钮依赖它自己一切页面就没的本地状态,所以从历史
里找回来的报告,只能在运行页导。
- 运行历史列表显示报告主题。此前只有 report_<hex>,谁也认不出是哪份报告。
主题从 graph->>'topic' 取(报告的 graph 就是占位 DSL {"topic":"…"},普通
任务 DSL 没有顶层 topic → 空串,不会误伤)。
- isReportRun() 按 task_id 的 report_ 前缀判定 —— 这是既有契约,导出接口
/reports/:id/export 本来就按同一个 id 寻址。+4 单测(含"不能只看是否包含
report"的误判防线)。
- reportFilename 从 ReportView 提到 lib:运行页也要用,不能私藏在一个 view 里。
live 验证(真账号,桌面端实机):运行历史首条显示「Redis 缓存穿透的三种解法」
→ 点开复盘 8 节点 + 全文 → 点 Word → **原生另存为对话框弹出**(文件名预填
主题)→ 保存 → 落盘 5KB,file 认 Microsoft Word 2007+,解 zip 得
word/document.xml,正文 2446 字。
**顺带把 wails3 迁移最后一个盲区验掉了**:v3 把 runtime.SaveFileDialog 重写成
application.Get().Dialog.SaveFile().SetFilename().AddFilter()
.PromptForSingleSelection(),此前从没被点过一次,记忆里只敢写"理应工作"。
现在实测通了。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-07-16 13:20:57 +08:00 |
|
Blizzard
|
fd268664c8
|
fix(gateway): 报告生成绕过了落库/计费/RBAC —— 生成完就永远找不回
用户实测:报告页输入主题、点生成、看着没反应,切到别的页面再回来,什么都没了。
真相是报告在后端好好地生成完了(26s),是 UI 把它弄丢了、而且永远找不回。
根因:两条提交路径漂移。SubmitTask(POST /tasks) 这一年陆续长出了预算门控/
计费租户/积分硬拦截/落库/录像五道,而 GenerateReport(POST /reports) 还停在
最初的「发个 NATS」,一道都没有。后果远不止看不到历史:
- 不落库 → 运行历史(读 sundynix_task)永远看不到报告。实测修复前该表
report_% 前缀 0 行。
- 无 token/轨迹录像 → SSE 没有回放能力,切走即永久丢失。
- 无 MetaTenantID → 报告用量记不到租户头上 = 漏账。
- 无预算门控/积分硬拦截 → 余额为 0 也能生成,绕过全部成本护栏。
- 路由漏了 RequireTenantRole → **viewer 只读角色能生成报告烧积分**,
而隔壁 /tasks 的注释白纸黑字写着「viewer 只读拦下」。报告一样烧钱。
修法不是把代码抄一份(那只会再漂一次),而是抽两个共用函数:
preflight() — 预算 → 计费租户 → 积分硬拦截,返回 billingTenant
launch() — 落库 + PublishTask + token/轨迹录像
两条路径都走它们,不可能再各长各的。SubmitTask 行为逐行不变。
live 验证(真账号 blizzardzhang,桌面端实机):生成 report_5dae9155af5cb500
→ sundynix_task 建行且带 owner+tenant_id → 出现在运行历史首条 → 点开
完整复盘 8 节点轨迹 + 报告全文。gin 路由表也确认 /reports 中间件数
12 → 13(与 /tasks 齐平)。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-07-16 13:13:21 +08:00 |
|
Blizzard
|
bf3b0048fd
|
fix(desktop): 原生下载失败会在用户选定路径上留半截文件
桌面端 Go 侧此前零测试。补 5 个(download/ReadLocalFile/Ping),
逮到一个真 bug:io.Copy 中途断开时,用户选定的路径上会留下一个半截的
.docx —— 带着用户自己起的名字躺在那儿,虽然前端会弹错误,但以后双击打不开,
而用户会以为是导出功能坏了。现在失败一律 os.Remove 不留残file。
同批修的两处(同一段代码,都没测试盖到):
- Close 的错误被 defer 吞掉。写文件时 io.Copy 成功不代表数据落盘,
flush 失败只在 Close 上报——吞掉就是静默截断,且 download 返回 nil(成功)。
- http.Get 用默认 client,没有超时。上游卡住的话「另存为」会永远转,
用户只能强杀 app。改用带 3 分钟超时的 client。
顺带核实过一个可疑点、结论是不用改:download 走裸 http.Get 不带鉴权头,
但报告导出路由 `/reports/:id/export` 是故意公开的(router.go 注释:
"EventSource/下载无法带 Bearer"),所以能通。
对话框本身(application.Get().Dialog)要真窗口,自动化盖不到,仍需手点。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-07-16 12:06:21 +08:00 |
|