shangfangjian
|
e6373d3c9a
|
chore(quality): 接入 ESLint 与 GitLab CI,开启严格未用检查,构建期设计令牌守卫
- eslint.config.mjs(flat config):react-hooks/react-refresh/tseslint;自定义 no-restricted-syntax
规则禁止 className 手拼基元类名(error 级,JSXAttribute 选择器精确限定,探针双向验证)与硬编码色值
- tsconfig:noUnusedLocals/noUnusedParameters 置 true
- package.json 新增 typecheck/lint/test/verify:tokens 脚本;build 串联令牌校验(verify-tokens.sh
防止 <alpha-value> 类静默失效回归);pnpm-lock.yaml 入库供 CI --frozen-lockfile
- .gitlab-ci.yml:lint/typecheck/test/build 四作业,MR 与默认分支触发,dist 产物留存一周
|
2026-09-22 23:45:23 +08:00 |
|
shangfangjian
|
f71f29b4cb
|
fix(frontend): 按审计报告修复全部17项问题,数据层迁至 src/api/
P0(4): 假数据清除(avg_latency_ms:2400→null,无端点字段改null)、假进度条改诚实的不确定态、
公共页不再反向依赖 admin(api/public.ts)、PredictProgress 重写
P1(6): 23处 any 归零(对齐后端 Pydantic 契约新增 PredictionMatchRef/HealthProbe 等)、
a11y(aria-live 0→4,htmlFor 1→17,aria-describedby/invalid 补齐)、App.tsx 抽 SiteLayout、
路由级 lazy+代码分割(首屏 315KB→238KB)、index.html 补 SEO/favicon/OG、死代码清理(AdminIcon 抽出)
P2(7): Login/index.css 裸色值令牌化、groupByDate useMemo、滚动监听统一、原生控件基元化、
useLeagues 静默失败补告警、路由级 ErrorBoundary
另修复审计未列问题:
- Monitoring todos 过滤器 t!==false 放行 null 导致整页崩溃 → Boolean(t) 真值过滤
- Collection 渲染期 Date.now()(react-hooks/purity 捕获)→ 计时器 effect
- bg-press-wash/60 透明度修饰符静默失效 → RGB 三元组 + 构建期令牌守卫(下个提交接入)
工程化:数据层 dal/api/types(1047行)git mv 至 src/api/,admin 留 @deprecated 兼容壳,
21 个引用方直指新路径;tsconfig 开启 noUnusedLocals/noUnusedParameters(清理9处存量)
|
2026-09-22 23:45:06 +08:00 |
|
shangfangjian
|
2cfc9ccc0f
|
refactor(ui): 合并双组件库为 components/ui 基元层,设计令牌变量化,报头字体子集自托管
- 新建 components/ui/(Button/Input/Select/Modal/Tabs/Spinner/Skeleton 等),变体用联合类型约束,根治幻影变体
- admin/components.tsx 与 matches/ui.tsx 改为再导出壳,消除 Spinner 三份重复
- 日期工具收口到 lib/date.ts,滚动监听收口到 lib/useScroll.ts
- index.css 引入 :root RGB 三元组令牌,tailwind.config 接 <alpha-value>(修复透明度修饰符静默失效)
- 刘建毛草 5MB 全字库(jsDelivr 未锁版) → 1KB 自托管子集(仅「先知」二字,unicode-range 限定)
|
2026-09-22 23:44:46 +08:00 |
|
shangfangjian
|
a7e8b75503
|
Merge pull request 'feat(monitoring): 监控页三分区仪表 + 上游探针' (#22) from feat-monitoring-enrich into main
Reviewed-on: #22
|
2026-09-22 20:46:17 +08:00 |
|
shangfangjian
|
575507e44f
|
Merge branch 'main' into feat-monitoring-enrich
|
2026-09-22 20:46:08 +08:00 |
|
WorkBuddy
|
5001093c3b
|
feat(monitoring): 监控页三分区仪表 + 上游探针(信息丰度增强)
此前监控页 6 张卡只有 2 个布尔有效(版本/运行时间/检查项三张死卡,
后端 /health 从未返回对应字段),与顶栏健康点重复。
后端:
- /health 补 version(importlib.metadata,失败降级 None)与
uptime_seconds(monotonic,不受时钟跳变影响) —— 激活两张死卡
- 新增 GET /admin/monitoring/upstream:bzzoiro 可达性探针,
轻量 GET 根端点,不携带 Key、不消耗采集配额;HTTP<500 视为可达
(DNS/网络/TLS 层面),业务失败由死信与日志上报
前端: Monitoring 重排为三分区仪表
- 基础设施: 存活 / DB 就绪 / 版本+运行时间 / 上游探针(延迟)
- 数据管线: 最近采集成功率 / 死信待处理 / 数据缺口 / 统计覆盖
- LLM: 预测总数 / 平均延迟 / 有效率(<80% 警示色)
- 顶部「需要关注」聚合条: 任一异常亮红并直达处理页,全清显示状态行
- 各信号独立容错(Promise.allSettled),单项失败只降级对应卡片
测试: 新增 test_monitoring_endpoints.py 5 例(ASGITransport 真路由 +
mock 探针外呼);全量 331 passed 8 skipped
|
2026-09-22 20:44:16 +08:00 |
|
shangfangjian
|
45535aa921
|
Merge pull request 'fix(admin): 数据完整性页补齐自动刷新(文案承诺但未实现)' (#21) from fix-completeness-refresh into main
Reviewed-on: #21
|
2026-09-22 19:54:51 +08:00 |
|
shangfangjian
|
d7d903b35b
|
Merge branch 'main' into fix-completeness-refresh
|
2026-09-22 19:54:39 +08:00 |
|
WorkBuddy
|
a6740f6140
|
fix(admin): 数据完整性页补齐自动刷新(文案承诺但未实现)
页面描述写着「每 5 秒自动刷新」,实际只有挂载时加载一次 + 手动按钮
—— 采集完成后停在页面上的数字永远不动。
- 补 5s 自动轮询(可开关,复用 Logs 页的 checkbox 样式);已有数据时
刷新不闪骨架(loading && !data 条件已具备)
- 采集页任务终态广播 profeto:ingest-done 事件,完整性页监听后
立即刷新,不必等下一个 5s 周期
|
2026-09-22 19:52:39 +08:00 |
|
shangfangjian
|
38e1c6c31f
|
Merge pull request 'fix(quality): 质量检查引用已删除的 MatchStats.id 必然 500 + 检查无日志' (#20) from fix-data-quality-check into main
Reviewed-on: #20
|
2026-09-22 18:20:49 +08:00 |
|
WorkBuddy
|
0742eef52e
|
fix(quality): 质量检查引用已删除的 MatchStats.id 必然 500 + 检查无日志
生产实测: POST /admin/data-quality/run 100% 失败,且日志无任何记录。
根因链:
1. P0-02 把 MatchStats 主键改为 match_id(无 id 属性),但质量检查
查询仍写 MatchStats.id.is_(None) → AttributeError → 500
2. handler 无 logger 调用;未捕获异常走 uvicorn.error(默认
propagate=False),不经过 root handler → 内存日志页/文件日志
都看不到 traceback,排障无据可查
修复:
- MatchStats.id → MatchStats.match_id(与 P0-02 模型对齐)
- 成功路径记 info 日志(检查名=通过/未通过(计数))
- setup_logging 打开 uvicorn/uvicorn.error 的 propagate,
未捕获异常 traceback 进入内存缓冲与滚动文件
- 回归测试 3 例(mock session):修复前红(3 failed),修复后绿;
全量 329 passed 8 skipped 零回归
|
2026-09-22 18:03:20 +08:00 |
|
WorkBuddy
|
f00bf71e8f
|
Merge pull request 'design: 全站设计打磨(语义色 token/弹窗动效与无障碍/报头收敛/交互小项)' (#19) from design-polish into main
|
2026-09-22 17:46:06 +08:00 |
|
WorkBuddy
|
246b06379d
|
Merge pull request 'fix(admin): 日志页默认定位到最新而非最早' (#18) from fix-logs-order into main
|
2026-09-22 17:45:50 +08:00 |
|
WorkBuddy
|
3fc3e91bd3
|
Merge pull request 'fix(admin): /health 被误加 /api/v1 前缀,健康状态永远「系统异常」' (#17) from fix-healthcheck-url into main
|
2026-09-22 17:45:48 +08:00 |
|
WorkBuddy
|
007a276fb7
|
Merge pull request 'fix(docker): 预创建 /app/logs 属主,修复文件日志 PermissionError' (#16) from fix-log-volume-permission into main
|
2026-09-22 17:45:47 +08:00 |
|
WorkBuddy
|
83a1eed283
|
fix+polish(frontend): 预测弹窗打磨 + 全量语义色替换 + 前台交互小项
预测弹窗:
- 修复模板字符串直写 JSX,反引号原样显示在结果面板的 bug
- 入场动画(遮罩淡入+面板上浮);初始聚焦/Tab 焦点陷阱/关闭还焦;
打开时锁定背景滚动
- 「关闭窗口即取消」→ 如实描述:前端仅停止进度显示,后台任务
可能仍在执行并消耗额度
语义色:全部 emerald/amber/rose 裸色替换为 ok/warn/bad token
(Standings 分区与走势、KeyRing、定时任务、任务状态点、
数据完整性进度条、预测命中标记、净胜球)。
前台交互:
- 空态跳后台 a[href] → Link,不再整页刷新
- 联赛 tab 溢出右缘渐隐提示 + aria-current
- 状态行右侧计数组加细线分隔,小屏 wrap 后层级仍清晰
|
2026-09-22 16:56:19 +08:00 |
|
WorkBuddy
|
9c77efaa4b
|
refactor(frontend): 报头抽组件 Masthead + 回到顶部抽组件 BackTop
- Masthead:统一 HomePage/StandingsLayout 双份复制实现,当前版面
印报红高亮 + aria-current;⚙ 字符改线条 SVG;管理类入口加 title
提示需登录,访客不再被无声踢到登录页
- BackTop:两页各一份的浮动按钮收敛为共享组件,去 rounded-full/
shadow-lg,与全站方角无阴影语言对齐
|
2026-09-22 16:55:59 +08:00 |
|
WorkBuddy
|
b9beddacf7
|
feat(design): ok/warn/bad 语义色 token + btn-outline + 弹窗动效 keyframes
- tailwind 新增语义状态色三组(纸底文字级 600/700 对比度 ≥4.5:1,AA),
色相降饱和贴近墨色印刷感;后续替换全部裸 tailwind 原色(emerald/amber/rose)
- 补 btn-outline 定义:此前三处使用但从未定义,样式静默失效
- 弹窗入场动画 keyframes:遮罩淡入 0.18s + 面板上浮 0.2s,仅 opacity/transform
|
2026-09-22 16:55:58 +08:00 |
|
WorkBuddy
|
77ecb078a3
|
fix(admin): 日志页改为保持倒序,打开自动定位到顶部(最新)
按用户预期调整方案:列表保持最新在最上面(后端倒序),打开页面/
自动刷新时 scrollTop=0 定位顶部;向下滚动回看历史时暂停定位,
拉回顶部即恢复。替代上一版的时间正序+贴底方案。
|
2026-09-22 15:39:19 +08:00 |
|
WorkBuddy
|
51fbc1828e
|
fix(admin): 日志页默认定位到最新而非最早
渲染顺序(最新在前)与滚动逻辑假设(正序+贴底=最新)相互矛盾:
自动刷新(默认开启)每 10s 强制贴底,而底部恰好是最早的日志,
用户打开页面被钉在最旧的一条上。
改为渲染时间正序(旧→新),与贴底滚动/上滚暂停跟随的 tail -f
行为一致:打开页面即定位最新。
|
2026-09-22 15:35:59 +08:00 |
|
WorkBuddy
|
1422afd6f6
|
fix(admin): /health 请求被误加 /api/v1 前缀,健康状态永远「系统异常」
build_url 对所有路径强制加 /api/v1,但后端 /health 与 /health/ready
挂在根路径(app.py,与 compose healthcheck 一致)→ 前端请求 /api/v1/health
404 → fetchHealth 吞错返回 status=unknown → 右上角/监控页永远「系统异常」。
- build_url 对 /health 与 /health/* 豁免前缀
- http.test.ts 补 URL 豁免用例(该测试此前因 ApiError 参数属性不被
strip-types 支持而完全无法运行,顺手改为显式赋值使基建可用)
|
2026-09-22 14:22:51 +08:00 |
|
WorkBuddy
|
7c4a244ce9
|
fix(docker): 预创建 /app/logs 并赋属主,修复卷挂载点 root 权限拒绝
生产实测: applogs 命名卷挂到 /app/logs 时挂载点由 Docker 以 root:root
创建,而非 root 进程(profeto)写入 app.log 时 PermissionError(Errno 13),
best-effort 降级生效但文件日志实际未启用。
- Dockerfile: 镜像内预创建 /app/logs 并 chown(chown -R 已有,覆盖新目录);
命名卷为空且首次挂载时 Docker 会复制镜像目录属主,重建镜像即可修复
- log_buffer: PermissionError 单独捕获,warning 给出可操作的排查方向
(镜像预建目录/chown/重建空卷),其余异常仍保持原通用降级
|
2026-09-22 14:14:53 +08:00 |
|
shangfangjian
|
432bfee8ad
|
Merge pull request 'feat(logging): 日志持久化落盘(LOG_FILE 滚动文件)' (#14) from log-persistence into main
Reviewed-on: #14
|
2026-09-22 13:34:46 +08:00 |
|
shangfangjian
|
f1c586111f
|
Merge pull request 'admin: 信息架构重组 + Settings tab 化 + 待办驱动 Dashboard + 任务历史' (#15) from admin-ux-overhaul into main
Reviewed-on: #15
|
2026-09-22 13:34:37 +08:00 |
|
WorkBuddy
|
db3c64d49b
|
test: 修复 standings 约束断言的 flaky(匿名约束 name=None)
与 log-persistence 分支同款修复:constraints 集合含匿名约束(name=None),
any() 子串匹配撞上 None 直接 TypeError,集合顺序不定导致间歇失败。
先判真值再匹配。
|
2026-09-22 12:46:43 +08:00 |
|
WorkBuddy
|
d013407aa1
|
feat(admin): 采集任务历史列表 + 图标语言统一 + 存量样式修复
- dal 新增 fetchIngestJobs(接已有 GET /admin/ingest/jobs,此前前端未用)
- Collection 新增「最近任务」卡:最新 10 条,状态点/任务类型/结果摘要,
任务终态后自动刷新 —— 补齐采集失败后的回看入口
- 任务选择按钮修复:brand-* 色在 tailwind 中不存在(选中态样式失效),
改用全站 press/press-wash;去掉与报刊风方角语言不符的 rounded-lg
- 任务图标 ⚽🏆📊 → ◈◇▤⇉,与全站几何字符一致,不混用 emoji
- Logs 页描述补充 LOG_FILE 持久化边界说明
|
2026-09-22 12:44:29 +08:00 |
|
WorkBuddy
|
0ab838258c
|
feat(admin): Settings 分区 tab 化 + Dashboard 改待办驱动
- Settings(496 行五块内容单页)拆为 4 个 tab:数据源/LLM/定时任务/登录认证;
tab 状态写入 URL(?tab=llm)可深链;数据加载与 API 调用不变
- Dashboard 改待办驱动:死信待处理/联赛数据缺口/可结算预测,
有待办才显示,每项直达处理页;全部清零时显示状态行
- 三步工作流卡仅在库里无比赛时显示(首次引导,老用户不再占版面)
- 修文案漂移:「数据源健康」卡不再引用已不存在的「数据源」页;
「运行预测」链接语义修正(预测在前台比赛详情页发起)
- 数据源单源硬编码列表简化为一行;未配 Key 直达设置分区
- <a href> 全改 <Link>,不再整页刷新
|
2026-09-22 12:44:12 +08:00 |
|
WorkBuddy
|
9cedb874f7
|
refactor(admin): 导航按心智模型三组重排(数据/预测/系统)
- 预测历史/回测/评估移出「数据流水线」,组成「预测」组(生命周期闭环)
- 「评估与监控」拆开:评估归预测,监控归系统
- 删除 SIDEBAR_GROUP_TITLES 别名映射(分组名与面板名统一)
- 预测历史图标改 target(靶心),数据完整性改 eval(核对文档),不再与仪表盘共用 chart
|
2026-09-22 12:34:18 +08:00 |
|
WorkBuddy
|
1cdc6411f5
|
chore(admin): 删除合并后遗留的 4 个死代码页面(~1280 行)
DataSources/LLMConfig/Config 已合并进统一 Settings 页,
Predictions 已被 PredictionHistory 取代,均无任何路由或 import 引用。
|
2026-09-22 12:33:15 +08:00 |
|
WorkBuddy
|
f0c1cc1491
|
feat(logging): 日志持久化落盘(LOG_FILE 滚动文件)
- LOG_FILE 配置: 空(默认)保持 stdout + Admin 内存日志页(重启清零);
填路径后额外写 RotatingFileHandler(单文件 10MB × 5 份,utf-8)
- setup_logging 幂等挂载(按 abspath 判重,相对/绝对同文件算一个);
目录自动创建;文件基础设施失败只 warning 绝不拖垮启动
- docker-compose: api 挂 applogs 卷,默认 LOG_FILE=/app/logs/app.log,
容器重建日志不丢;.env.example/.gitignore/docs 同步
- tests/test_log_persistence.py: 落盘/幂等/空值禁用/自动建目录/失败降级
- test_p0_standings_cutoff: 匿名约束 name=None 使 any() 子串匹配 TypeError
(集合顺序不定 → flaky),先判真值再匹配(存量缺陷顺手加固)
|
2026-09-22 12:18:03 +08:00 |
|
shangfangjian
|
8fef411ced
|
fix: 清理 LLM prompt 中的过时引用(confidence/injuries)
- 全部 agent/match_prediction prompt: deprecated "confidence" → "subjective_confidence"
- aggregator prompt: 移除已删除的 injuries 专家(→standings),修正专家中文名(阵容完整性→联赛排名)
消除预测日志中的 Deprecated field / 未知专家警告。
|
2026-09-22 11:05:07 +08:00 |
|
shangfangjian
|
197641b9f7
|
fix(P1-async): 预测改为异步 POST+轮询,避免网关超时(Cloudflare 524)
后端:POST /predict 立即返回 job_id,后台 asyncio.create_task 执行;
新增 GET /predict/jobs/{job_id} 轮询状态(running/success/failed)。
前端: useMatchPredict 改为 POST 拿 job_id → 每 3s 轮询直到终态;
整体超时 5 分钟不变。避免多专家预测 60-180s 触发 Cloudflare 100s 超时(HTTP 524)。
|
2026-09-22 11:00:40 +08:00 |
|
shangfangjian
|
1a9dc63edd
|
fix: Standing 无 xgd 属性,用 xg_for - xg_against 计算 xG差
standings_slice._fmt 误用 row.xgd(Standing 无此列),预测时报 AttributeError。
修正:直接用 xg_for - xg_against 计算 xG差,去掉对 goal_diff 的冗余判断。
|
2026-09-22 10:52:26 +08:00 |
|
shangfangjian
|
b15678b3f6
|
fix: http client URL 构造必须统一加 /api/v1 前缀
公开页调用 /matches(无 /api/v1 前缀),被 nginx 当 SPA 回退返回 index.html,
前端 JSON 解析报 "Unexpected token <"。
修复:build_url 函数确保所有相对路径统一走 /api/v1,
已含前缀或绝对 URL 保持不变。
首页加载比赛恢复正常。
|
2026-09-22 10:44:12 +08:00 |
|
shangfangjian
|
28ecf85da8
|
fix: MatchStats 主键是 match_id 非 id
scheduler._run_checks 误用 MatchStats.id(MatchStats 无此列,主键为 match_id),
导致数据质量检查定时任务抛 AttributeError。修正为 MatchStats.match_id.is_(None)。
|
2026-09-22 10:18:00 +08:00 |
|
shangfangjian
|
b749b1c621
|
fix: 前端构建修复 + P3-1 子组件路径校正
- tsconfig 排除 *.test.ts(生产构建不编译测试)
- OutcomePanel TeamSideTag 导入路径 ../../ → ../../../
- AgentsPanel/OutcomePanel/ReasoningPanel types 导入 ../../ → ../
- MatchPredictPanel JSDoc 注释 **. → /** (P3-1 编辑损坏)
前端构建成功,容器 healthy。
|
2026-09-22 10:09:48 +08:00 |
|
shangfangjian
|
69ec19e646
|
fix: api healthcheck 改用 python urllib(生产镜像无 curl)
python:3.11-slim 生产镜像不含 curl,原 healthcheck 始终失败;
改为 python urllib.request,与 Dockerfile 非 root 用户兼容。
|
2026-09-22 09:59:50 +08:00 |
|
shangfangjian
|
5f4075be22
|
fix(P1 follow-ups): 恢复误删 set_runtime_value + 假 DB 适配 P1-K UPSERT
- 恢复 P1-G 编辑时误删的 clear_runtime_value/set_runtime_value
- 修复 bronze 测试 patch 目标(pipeline_write 替代门面 bz.*)
- 增强假 DB:处理 pg_insert(Insert 语句)+ Team name 回退 + League import
全量 319 通过。
|
2026-09-22 09:43:48 +08:00 |
|
shangfangjian
|
eae88f4cd9
|
fix(P1-G/H/I/K): 运行时配置解密/Redis JSON/eval summary/UPSERT
P1-G runtime_config:DB 故障回落 env;解密失败在生产环境抛出(ValueError),
非生产回落;DB 异常与解密异常不再共用 except Exception。
P1-H Redis 预测缓存:去掉 pickle 改用 JSON + dataclasses.asdict,
datetime 字段 ISO 序列化。
P1-I eval get_eval_summary:默认 limit=None(不冒充全集);新增 run_type(默认 live)
与 season 过滤;_build_filters 同步扩展。
P1-K Team/League get_or_create:INSERT 改为 PG UPSERT(ON CONFLICT DO NOTHING)
防并发重复插入。
测试 test_p1_g_runtime_config(4/4) + test_p1_k_upsert(4/4);全量绿。
|
2026-09-22 09:08:35 +08:00 |
|
shangfangjian
|
2b52478b8f
|
test(P1-F): BIGINT 主键表 INSERT 不带 id 自动生成验证
真 PG 测试(独立 engine,避免事件循环污染):
- data_lineage / raw_event 未指定 id → DB 自动生成递增唯一 id
- 连续 INSERT id 递增且唯一
3/3 通过。
|
2026-09-22 08:57:20 +08:00 |
|
shangfangjian
|
b30ad56319
|
fix(P1-E): 启动时标 failed 残留 pending/running ingest_jobs
lifespan 加 _fail_stale_ingest_jobs():UPDATE ingest_jobs SET status=failed
WHERE status IN ('pending','running');异常仅记 warning 不阻断启动。
顺手修复 0021(op.text→字符串)/0023(索引名 leason→league)/0024(type_unique→type_)
三个待执行迁移 bug。
测试 test_p1_e_stale_ingest_jobs(2/2 mock);全量 315 通过。
|
2026-09-22 08:49:30 +08:00 |
|
shangfangjian
|
b997c06ede
|
fix(P1-D): 全局 LLM 并发限制 + 删除未接线的 provider 字段
- 删除 PredictRequest.provider(未接线,路由从未传入,符合禁令 #7)
- 新增 _GLOBAL_LLM_SEMAPHORE(默认 4) + _predict_with_concurrency:
公开 /predict 所有模式汇总受全局并发限制,与 orchestrator 内 match 级
Semaphore(8) 并存。
测试 test_p1_d_concurrency(4/4);全量 313 通过。
|
2026-09-22 03:44:09 +08:00 |
|
shangfangjian
|
1ddf697c97
|
fix(P1-C): 公开预测仅 live+success,且不含 reasoning/agent_outputs
GET /matches/{id}:查询加 WHERE run_type='live' AND status='success';
recent_predictions 不再输出 reasoning/agent_outputs(避免泄露内部推理)。
MatchOut.recent_predictions schema 放宽为 list[dict]。
测试 test_p1_c_public_predictions(2/2);全量 309 通过。
|
2026-09-22 03:39:36 +08:00 |
|
shangfangjian
|
5ff43d4984
|
fix(P1-A/P1-B): ingest 联赛级计数修正 + 非法 cursor 400
P1-A: ingest 联赛级 inserted/updated 读 r["leagues"][code] 而非顶层 r.get("inserted");
抽 _accumulate_ingest_result 纯函数 + 合约测试(4/4)。
P1-B: 非法 cursor 不再静默忽略,返回 400 + detail.code=INVALID_CURSOR;
抽 _parse_cursor 纯函数 + 测试(8/8)。
全量 307 通过。
|
2026-09-22 03:31:40 +08:00 |
|
shangfangjian
|
41cb2edd47
|
fix(P1-A): ingest 联赛级计数读 leagues[code],抽为纯函数
旧代码读 r.get("inserted")(顶层无此 key)导致联赛级计数总为 0;
改为读 r["leagues"][code].inserted/updated,与顶层 total_* 分离。
抽 _accumulate_ingest_result 纯函数 + 4 合约测试(全绿)。
|
2026-09-22 03:23:41 +08:00 |
|
shangfangjian
|
64ae8e663a
|
fix(P0-03): Prediction 幂等指纹——只追加,不覆盖
_upsert_prediction 改为 _insert_or_find_by_fingerprint:
- 同 input_hash → 返回已有行(绝不 UPDATE pred_/reasoning/agent_outputs)
- 不同 input_hash → INSERT 新行
input_hash 升级为规范 JSON SHA-256,捕获:match_id, cutoff, prompt_version,
prompt_hash, system_prompt_hash, provider, model, mode, run_type, temperature,
context_hash, agent_ids。移除旧 (match, provider, model, mode, run_type) 唯一约束,
改为 partial unique index(WHERE input_hash IS NOT NULL,兼容旧 NULL 数据)。
三条路径(single/multi/baseline)统一传足指纹字段。
迁移 0024 + 测试 test_p0_prediction_fingerprint(10/10);全量 295 通过。
|
2026-09-22 03:13:32 +08:00 |
|
shangfangjian
|
49d78136a1
|
fix(P0-02): 积分榜改为追加快照(append-only) + available_at cutoff
去掉 uq_standings_league_season_team,改为 (league, season, team, available_at) 唯一;
每次采集 INSERT 新行(available_at=now),ON CONFLICT DO NOTHING,不覆盖旧行。
standings_slice(before):DISTINCT ON (team_id) WHERE available_at<=cutoff
ORDER available_at DESC;before=None → cutoff=now()。
公开 list_standings 取每队最新可用快照(子查询 max available_at)。
迁移 0023 + 切片/路由/docs 同步;测试 test_p0_standings_cutoff(5/5)。
284 测试全绿。
|
2026-09-22 02:45:50 +08:00 |
|
shangfangjian
|
63caa6736c
|
fix(P0-01): missing score 不得变 0:0 — score_status + CHECK 约束
新增 matches.score_status(known/missing/unknown):
- 替换 ck_matches_finished_has_score 为 ck_matches_score_integrity:
known → 必须有分; missing/unknown → goals 必须 NULL(不伪造 0:0)
- normalize: 完赛缺分不再静默降级为 scheduled,改设 score_status=missing
- events ingest: 创建/更新 Match 同步 score_status(比分由缺变 known / 确认缺分 missing)
- slices(form/h2h/home_away)/backtest: 显式加 score_status='known' 过滤完赛样本
- 迁移 0022 回填现有数据(绝不 UPDATE goals=0)
测试 tests/test_p0_score_status.py(9/9):约束存在性/Match 构造/normalize 行为。
54 相关测试全绿。
|
2026-09-22 02:32:53 +08:00 |
|
shangfangjian
|
f563d5cc99
|
fix(P0-00): HTTP client 必须传递 method/body,仅 body 存在时设 Content-Type
request() 此前忽略 options.method(默认 GET)与 options.body,导致 POST/PUT/DELETE
全部以 GET 空 body 发出;且无条件设置 Content-Type 误污染无 body 请求。
修复: 显式传递 method(默认 GET) 与 body; 仅当 body 非空时加 Content-Type。
测试 frontend/src/lib/http.test.ts(5/5):GET/POST/PUT/DELETE 的 method/body/headers。
运行: node --experimental-transform-types frontend/src/lib/http.test.ts
|
2026-09-22 02:24:50 +08:00 |
|
shangfangjian
|
7d2eabf750
|
chore: 死代码与重复逻辑清理
删除未引用/未调用符号:
- PredictionRepository(无引用)
- is_correct_1x2(无调用者)
- LeagueOut(路由用 list[dict])
- IngestResponse(IngestBzzoiroResponse 已替代)
- SecurityCheckError(从未 raise,assert_security_on_startup 用 sys.exit)
- short_write(仅自引用,全仓库无外部调用)
- fetchIngestJobs(列表函数无页面使用,单数 fetchIngestJob 仍保留)
- clear_prompt_cache(无入口)
去重:
- eval._actual_1x2 改为委托 utils.actual_1x2(单一权威源)
全量测试 270 通过,业务行为不变。
|
2026-09-22 01:55:52 +08:00 |
|