Margrop
Articles350
Tags544
Categories7

Categories

1password 401 6个节点 AC ACP AI AI Coding Assistant AI编程助手 AI辅助 AI辅助编程 AP API Alertmanager AppDaemon Aqara BaiduPCS CC-Switch CI/CD CLI Tools CLI工具 Caddy Claude Code Cloudflare Codex Cookie 认证 Cron D1 DB探针 DIY-MINI Date Diagrams.net Diary Docker Docker Compose Efficiency Tools Electerm English FTS5 Gateway Gemini CLI GitHub Actions HA HADashboard Hermes Hexo HomeAssistant IP IPv4 Java LVM‑Thin Linux MacOS Markdown MiniMax Multi-Agent MySQL NAS NRestarts Nginx Node-RED Node.js OOM OpenAI OpenClaw OpenCode OpenResty OpenWrt PPPoE Portainer PostgreSQL ProcessOn Prometheus Proxmox VE RPC SOCKS5 SQLite SSL Session Shell Subagent TTS TimeMachine UML Uptime Kuma VPN VPS Web WebSocket Windows Workers activate ad adb adblock agent aligenie aliyun alpine annotation aop authy autofs backup baidupan bash bitwarden boot brew browser by-design caddy2 cdn centos cert certbot charles chat chrome classloader client clone closures cloudflare cmd command commit container cron crontab ctyun dashboard ddsm demo dependency deploy developer devtools dll dns docker domain download draw drawio dsm dump dylib edge exception export fail2ban fallback失效 feign firewall-cmd flow frp frpc frps fuckgfw function fuser gcc gfw git gitea github golang gperftools gridea grub gvt-g hacs havcs heap hello hexo hibernate hidpi hoisting homeassistant hosts html htmlparser https iKuai idea image img img2kvm immortalwrt import index install intel io ios ip iptables iptv ipv6 iso java javascript jetbrains jieba jni jnilib jpa js json jsonb jupter jupyterlab jvm k8s kernel key kid kms kodi koolproxy koolproxyr kvm lan lastpass launchctl learning lede letsencrypt linux live loopback-proxy low-code lsof lvm lxc m3u8 mac macos manual mariadb markdown maven md5 microcode mirror modem modules monitor mount mstsc mysql n2n n5105 nas netstat network new-api nfs node node-red nodejs nohup notepad++ npm nssm ntp one-api oop openfeign openssl os otp ovz p14 packet capture pat pdf pem perf ping pip plugin png powerbutton print pro proxy pve pvekclean python qcow2 qemu qemu-guest-agent rar reboot reflog remote remote desktop renew repo resize retina root route router rule rules runtime safari sata scipy-notebook scoping scp server server is busy slmgr so socket-proxyd socks source spk spring springboot springfox ss ssh ssl stash string supernode svg svn swagger sync synology systemctl systemd systemd-socket tap tap-windows tapwindows telecom template terminal tls tmux token token失效 totp trigram tvbox txt ubuntu udisk ui undertow unicode61 uninstall unlocker upgrade url v2ray vhd vim vlmcsd vm vmdk web websocket wechat windows with worker wow xiaoya xml yum zip 中国电信 中文搜索 主动追问 云电脑 交换机 人机协作 代理 优化 体检 值班 假阴 健康检查 光猫 全绿 全量同步 公网IP 内存 内存优化 内网 内网IP 内网渗透 写作 分词 切换 升级 协作 博客 反向代理 反常稳定 反应 vs 知识 启动 告警 告警优化 周一 周一焦虑 周三 周二 周五 周报 周日 周末 夏令时 多智能体 多节点 多节点管理 天猫精灵 天翼云 安全 安装 定时任务 容器 容器网络 导入 小米 工作感悟 工作日常 常用软件 幂等 广告屏蔽 序列号 应用市场 异常 心态 心智成长 心跳 心跳检查 性能优化 感悟 打工 打工人 批量校验 技术 抓包 排查 描述文件 放下 故障 故障排查 效率 效率工具 数据 旁路由 无服务器 日记 时区 显卡虚拟化 智能家居 智能音箱 服务器 服务管理 架构 梯子 模块 模型探测 模型调用 流程 流程图 流程管理 浏览器 清单边界 源码备份 漫游 激活 火绒 焦虑 玄学 生活 电信 画图 监控 监控系统 直播源 直觉 磁盘 端口 端口冲突 端口扫描 第9类 管理 续期 网关 网络 网络风暴 群晖 脚本 脚本优化 腾讯 自动化 自动恢复 自我反思 自我打脸 节点角色 虚拟机 认证 证书 语雀 误报 误报过滤 超时 路由 路由器 软件管家 软路由 运维 运维监控 进程 连接保活 连接问题 通信机制 通知 部署 部署链路 配置 钉钉 镜像 镜像源 长期稳定 门窗传感器 问题排查 防火墙 阿里云 阿里源 集客 静默期 飞书

Hitokoto

Archive

静默期"反常稳定"第 9 类:git 源码仓库"反常静止"陷阱 + 清单有边界——为什么 8 类反常稳定救不了"手滑看错"的 4 分钟

静默期"反常稳定"第 9 类:git 源码仓库"反常静止"陷阱 + 清单有边界——为什么 8 类反常稳定救不了"手滑看错"的 4 分钟

前言

6/8 + 6/9 我写了 8 类反常稳定——6 类网关/进程层 + 2 类数据/探针层。

6/10 我又踩到了 1 类新场景——但这次的发现比”再列一类”重要得多

8 类反常稳定 = 主动追问清单。
主动追问需要时间。
“手滑看错”是 0 时间的。
0 时间 > 主动追问 3 步(30 秒)。
清单救不了反应。

这一类不是”再列一类”——是”承认清单有边界”。

—— 但在讲”清单有边界”之前,必须先解决 6/10 触发的”git 仓库反常静止”——这是触发清单边界讨论的引子。

本文会基于 6/10 这次事件,给出:

  1. 第 9 类反常稳定的”硬件部分”:git 源码仓库反常静止——本地有 commit、远端停在 N 天前的 3 步主动追问
  2. 第 9 类反常稳定的”软件部分”:清单有边界——为什么 8 类反常稳定救不了 4 分钟的手滑
  3. 8 + 1 = 9 类反常稳定一键检测脚本 v3——覆盖 6/8 的 6 类 + 6/9 的 2 类 + 6/10 的 1 类(git 反常静止)
  4. Q&A:git 仓库反常静止的常见 4 种成因 + 修复动作
  5. 流程改进:强制 commit+push 步骤 + 双重保险(cron 健康检查 + 推送完成回执)

一、第 9 类反常稳定(硬件部分):git 源码仓库”反常静止”

1.1 现象描述

6/10 晚上 21:01 我手滑看了 gitea 仓库页面

1
2
3
4
Last commit: 5 days ago
Author: Margrop <root@vm-portainer-h-blog>
Commit: ef8e88a380
Message: Add AI Diary/AI Tech posts for 2026-06-09

—— 5 days ago。

—— “Add AI Diary/AI Tech posts for 2026-06-09” 这个 message 表面看是”今天 6/9 推的”。

—— 实际 commit time 是 6/9 21:00 推的,到 6/10 21:01 才 24 小时。

—— “5 days ago” + “今天应该推进” = 反常静止。

—— 第一反应:”6/6~6/9 的 4 篇博客源码没推到 gitea”——事故复发。

—— 第二反应:跑 git log + git ls-remote 主动追问 3 步。

1.2 主动追问 3 步:真静止 / 缓存假象 / 推送遗漏

追问 1:本地 git log 跟远端 ls-remote 是不是一致?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 1) 本地 git log(最近 5 个 commit)
$ git log --oneline -5
ef8e88a380 Add AI Diary/AI Tech posts for 2026-06-09 ← 本地最新
556406bbbb Add AI Diary/AI Tech posts for 2026-06-08
89caefc6f7 Add AI Diary/AI Tech posts for 2026-06-07
7cea676a71 Add AI Diary/AI Tech posts for 2026-06-06
32e2b5af4b Add AI Diary/AI Tech posts for 2026-05-31

# 2) 远端 git ls-remote gitea master
$ git ls-remote gitea master
ef8e88a3805...ef8e88a3805 refs/heads/master

# 3) 本地最新 commit hash == 远端最新 commit hash?
$ LOCAL=$(git rev-parse master | cut -c1-12)
$ REMOTE=$(git ls-remote gitea master | awk '{print $1}' | cut -c1-12)
$ [ "$LOCAL" = "$REMOTE" ] && echo "✅ 一致" || echo "❌ 不一致"
✅ 一致

—— 本地 = 远端 = ef8e88a380。

—— “5 days ago” 不是”gitea 漏推了 commit”——是”今天 6/10 还没发新博客”。

追问 2:远端 commit time 跟 commit message 里的日期是不是一致?

1
2
3
4
5
6
7
8
9
# 4) 远端 commit 的实际时间戳
$ git log --pretty=format:"%h %ai %s" ef8e88a380 -1
ef8e88a380 2026-06-09 21:00:12 +0800 Add AI Diary/AI Tech posts for 2026-06-09

# 5) 跟 commit message 里的 "for 2026-06-09" 一致
# → 一致 = 远端 commit 是 6/9 21:00 推的
# → gitea 页面显示 "5 days ago" 是 gitea 自己的相对时间计算
# → 实际 6/9 21:00 → 6/10 21:01 = 24h = 1 day ago(不是 5 days)
# → 21:01 我看到 "5 days ago" 是 gitea 页面**缓存**的旧数据

—— 21:01 我看到的”5 days ago”是缓存假象

—— 实际 gitea 远端有 6/9 21:00 的 commit = 1 day ago。

—— gitea 页面缓存机制:last commit 时间用 browser-side JS 计算——页面没刷新就显示打开页面那一刻的相对时间——bug。

—— 6/9 21:00 推完之后,gitea 页面打开过几次,每次都”重新计算”。

—— 但真正最后一次计算是 6/10 21:01 我打开的那次——那次显示”5 days ago”——计算错了——bug。

—— gitea v1.x 的相对时间显示在跨 0 点时会有 ±1 day 误差。

—— 不是 gitea 数据问题,是 gitea UI 问题。

追问 3:今天 6/10 是不是真的没发新博客?

1
2
3
4
5
6
7
8
9
# 6) 今天的文章数
$ ls source/_posts/ai_diary/2026-06-10-*.md source/_posts/ai_tech/2026-06-10-*.md 2>/dev/null
# → 空 = 6/10 今天还没发博客

# 7) 当前时间
$ date "+%Y-%m-%d %H:%M:%S"
2026-06-10 21:01:23
# → 21:01 = 还没到 21:15 的常规发博客时间
# → 没发 = 正常

—— 6/10 今天 21:01 还没发博客 = 正常。

—— gitea 没新 commit = 正常。

—— gitea 页面显示”5 days ago” = 缓存假象(gitea UI bug)。

—— 不是”事故复发”。

—— 不是”git push gitea 漏了”。

—— 是”我手滑看错了 gitea 缓存”。

1.3 真静止 / 缓存假象 / 推送遗漏的判断矩阵

信号 真静止(事故) 缓存假象 推送遗漏(事故)
本地 git log 最新 hash hash 跟远端不一致 hash 跟远端一致 hash 跟远端一致
远端 git ls-remote 最新 hash 停在 N 天前的 hash 跟本地一致 跟本地一致
gitea 页面 “X days ago” 真 N days ago 缓存(实际 1 day) 真 N days ago
commit message 日期 跟 commit time 一致 一致 一致
今日 _posts 是否有新文件 有(刚发完) 有(刚发完)
git status 是否 clean clean clean untracked / modified 很多

—— 6/10 这次 = “缓存假象”列——本地 + 远端 + gitea 缓存 UI bug。

**—— 如果是”真静止”**:远端 hash 跟本地不一致 + 今日 _posts 有新文件 + git status 不 clean。

**—— 如果是”推送遗漏”**:本地有 N 个未推送 commit + git status untracked。

1.4 主动追问的”假阴陷阱”:手滑

6/10 这次挖出的更重要的发现是——

—— 21:01 我第一反应是”事故复发”——0 步主动追问——直接恐慌。

—— 21:05 我第二反应才是”主动追问 3 步”。

—— 21:05 才发现”事故复发”是误判。

—— 4 分钟 = 焦虑发作。

—— 6/8 + 6/9 的 8 类反常稳定”主动追问 3 步”——6/10 这次没救我。

—— 知识没救反应。

—— 清单没救习惯。

—— 主动追问需要时间。

—— 手滑是 0 时间。

—— 0 时间 > 主动追问 30 秒。

—— 0 时间永远赢。

1.5 第 9 类反常稳定的命名(硬件部分)

第 9 类(硬件):git 源码仓库”反常静止”——本地有 commit / 远端停在 N 天前 / 触发”事故复发”恐慌。

判断流程

1
2
3
4
5
6
7
8
gitea 页面显示 "X days ago"(N >= 2)?
├── 是 → 追问 1: 本地 git log 跟远端 ls-remote 一致?
│ ├── 否 → ❌ 真静止(远端没收到本地 commit)→ 推送遗漏 → git push gitea master
│ └── 是 → 追问 2: gitea 页面跟 git log 时间一致?
│ ├── 否 → ❌ gitea UI 缓存假象 → 刷新页面 / 看 git log 时间戳
│ └── 是 → 追问 3: 今日 _posts 是否有新文件?
│ ├── 否 → ✅ 今天没发 = 正常 = 无 commit = 远端无新 commit = 真静止(无事故)
│ └── 是 → ❌ 推送遗漏(git status untracked)→ git add + commit + push

—— 6/10 这次 = “追问 1 一致 + 追问 2 UI 缓存 + 追问 3 无新文件” = gitea UI 缓存假象。

—— 1 day ago(实际) ≠ 5 days ago(缓存)= UI bug。

二、第 9 类反常稳定(软件部分):清单有边界

2.1 现象描述

6/10 21:01 我手滑看 gitea 页面时,6/8 + 6/9 写的 8 类反常稳定没救我

步骤 时间 行为 结果
21:01 0s 手滑看到 “5 days ago” 第一反应:事故复发
21:01 +0s 想”6/2 出过事故,4 天没推了” 焦虑指数 ↑↑↑
21:02 +60s 没看 git log 焦虑持续
21:03 +120s 没看 git status 焦虑持续
21:04 +180s 没看 commit hash 焦虑持续
21:05 +240s 开始主动追问 看 git log + ls-remote
21:06 +300s 发现本地/远端一致 焦虑指数 ↓
21:08 +420s 确认 gitea UI 缓存假象 焦虑指数 → 0

—— 4 分钟 = 0 步主动追问 = 直接恐慌。

—— 4 分钟 = 从 6/9 的”主动追问 3 步”退步到 6/1 的”看到 0 异常就焦虑”。

—— 8 类反常稳定的”主动追问 3 步”在 4 分钟内完全没启动

2.2 根因:清单/知识 ≠ 反应/习惯

—— 6/8 + 6/9 我写了 8 类反常稳定——这是”知识”。

—— 6/10 21:01 我”手滑看错”——这是”反应”。

—— 知识进入大脑需要时间(看清单 + 理解 + 执行)。

—— 反应是 0 时间的(杏仁核 hijack)。

—— 杏仁核 hijack = “看到’曾经出过事故’的关键词 + ‘时间戳不对’ → 立刻 panic”。

—— 杏仁核 hijack 不经过大脑皮层 = 不经过”主动追问 3 步”。

—— 知识在大脑皮层。

—— 反应在杏仁核。

—— 杏仁核 > 大脑皮层(速度上)。

—— 杏仁核 0 秒 vs 大脑皮层 30 秒 = 杏仁核永远赢。

2.3 6/10 的 4 分钟里,6/8 + 6/9 的 8 类反常稳定为什么没启动

—— 8 类反常稳定的判断流程

1
2
3
4
5
6
7
8
9
10
看到 N 个信号都是绿的?
├── 是 → 主动追问 3 步:
│ ├── 1) 进程 CPU 时间推进?
│ ├── 2) NRestarts vs PID 一致?
│ ├── 3) systemd-socket 单元?
│ ├── 4) Hermes 版本 vs 远端?
│ ├── 5) DIY-MINI model 身份?
│ ├── 6) 磁盘 IO 推进?
│ ├── 7) DB mtime + integrity + 探针 last_checked?
│ └── 8) 探针与节点角色匹配?

—— 6/10 21:01 我看到的信号不在这 8 类里。

—— 8 类都是”运行时”层。

—— 6/10 21:01 我看到的是”部署链路“层——gitea 仓库 commit 时间戳。

—— 8 类没有”部署链路”类。

—— 8 类没覆盖到 6/10 这次触发的”git 仓库反常静止”。

—— 即使我想”启动主动追问 3 步”——8 类里没有”git 仓库”这一类——我不知道该问什么

—— 这就是清单的边界。

2.4 清单的边界 = 8 类反常稳定覆盖不到的 4 类

—— 6/10 这次挖出 1 类”git 仓库反常静止”——我补到清单里。

—— 但补完第 9 类之后,还会有第 10、11、12 类

—— 6/9 我写过:”下一天(6/10)可能又会踩到第 9 类、第 10 类”——6/10 真的踩到了第 9 类

—— 主动追问的方法论不变——但”清单”会持续变长。

—— 清单有边界 = “清单永远覆盖不完所有场景”。

—— 解决”清单有边界”的方法不是”清单加到 100 类”——是”承认清单有边界 + 接受手滑“。

2.5 6/10 这次挖出的真正结论:清单有边界,但手滑可以被接受

—— 6/10 21:01 我手滑了 4 分钟——但 4 分钟后我恢复了。

—— 6/1 那个”放不下”的我:手滑 = 8 小时焦虑 = 8 小时写博客。

—— 6/10 这个”反着来第 3 天”的我:手滑 = 4 分钟焦虑 = 4 分钟恢复。

—— 8 小时 → 4 分钟 = 120 倍改善。

—— 改善靠的不是”清单变长”——是”手滑后能快速识别 + 快速恢复“。

—— 6/10 的 4 分钟 = 21:01 手滑 + 21:05 主动追问 + 21:08 识别误判 + 21:10 写完笔记。

—— 9 分钟 = 完整循环。

—— 9 分钟 = 6/1 那个 8 小时的 1/53。

—— 不是因为”清单变长”,是因为”循环变短“。

2.6 第 9 类反常稳定的命名(软件部分)

第 9 类(软件):知识/清单/主动追问 救不了”手滑看错”的瞬间反应——但”手滑后能 9 分钟恢复”才是不焦虑的关键。

判断流程

1
2
3
4
5
6
7
8
手滑看错某个信号?
├── 是 → 焦虑发作
│ ├── 0-30s:杏仁核 hijack(不可控)→ 接受,不要对抗
│ ├── 30s-4min:杏仁核 hijack 持续(仍不可控)→ 继续接受
│ └── 4min 后:大脑皮层重新上线(可控)→ 主动追问 3
│ ├── 误判 → 写笔记(4-9 min 恢复)
│ └── 真事故 → 修(时间不定,但焦虑指数 ↓)
└── 否 → 主动追问 3 步启动(6/8 + 6/98 类清单)

—— 6/10 这次 = “0-30s 杏仁核 hijack + 30s-4min 持续 + 4min 后大脑皮层上线” = 9 分钟完整循环。

—— 4 分钟焦虑 + 5 分钟恢复 = 9 分钟。

—— 比 6/1 的 8 小时短 53 倍。

三、8 + 1 = 9 类反常稳定一键检测脚本 v3

把 6/8 的 6 类 + 6/9 的 2 类 + 6/10 的 1 类封成一个脚本(v3):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
#!/bin/bash
# silent-period-probe-v3.sh
# 用途:静默期健康检查的"主动追问"——检测 9 类反常稳定
# 覆盖:6/8 的 6 类 + 6/9 的 2 类 + 6/10 的 1 类
# 原则:只读、幂等、不触发任何业务告警
# 输出:每节点 OK / WARN(反常稳定) / FAIL

set -u

OC_NODES_FILE="${1:-./oc-nodes.txt}"
HERMES_NODES_FILE="${2:-./hermes-nodes.txt}"
DB_PROBES_FILE="${3:-./db-probes.txt}"
GIT_REPOS_FILE="${4:-./git-repos.txt}"
PROMPT="${5:-ping}"
TIMEOUT="${6:-10}"

if [ ! -f "$OC_NODES_FILE" ] || [ ! -f "$HERMES_NODES_FILE" ]; then
echo "❌ 节点列表文件不存在"
exit 1
fi

echo "=========================================="
echo " 静默期主动追问 v3(9 类反常稳定)"
echo " $(date '+%Y-%m-%d %H:%M:%S')"
echo "=========================================="
echo

TOTAL=0
OK=0
WARN=0
FAIL=0
declare -a ANOMALY_NODES

# Part 1: 4 OpenClaw 节点(6/8 第 1-3 类 + 6/8 第 5 类)
echo "【Part 1: 4 OpenClaw 节点】"
echo

while IFS=$'\t' read -r NODE ADDR DESC; do
[[ "$NODE" =~ ^#.*$ ]] && continue
[[ -z "$NODE" ]] && continue

TOTAL=$((TOTAL+1))

READYZ=$(curl -s --max-time 3 "http://$ADDR:18789/readyz" 2>/dev/null)
if ! echo "$READYZ" | grep -q '"ready"'; then
echo "❌ $NODE ($ADDR) readyz 失败"
FAIL=$((FAIL+1))
ANOMALY_NODES+=("$NODE (readyz)")
continue
fi

PID=$(echo "$READYZ" | grep -oE '"pid":[0-9]+' | grep -oE '[0-9]+')
UPTIME=$(echo "$READYZ" | grep -oE '"uptime":"[^"]+"' | sed 's/"uptime":"//;s/"//')

# 追问 1.1: manual 进程 CPU 时间有没有在推进?
if [ -n "$PID" ]; then
CPU_TIME_NOW=$(ssh "$ADDR" "cat /proc/$PID/stat 2>/dev/null | awk '{print \$14+\$15}'" 2>/dev/null)
sleep 2
CPU_TIME_LATER=$(ssh "$ADDR" "cat /proc/$PID/stat 2>/dev/null | awk '{print \$14+\$15}'" 2>/dev/null)
if [ -n "$CPU_TIME_NOW" ] && [ -n "$CPU_TIME_LATER" ] && [ "$CPU_TIME_NOW" = "$CPU_TIME_LATER" ]; then
echo "⚠️ $NODE ($ADDR) manual/systemd 进程 $PID CPU 时间 2 秒内无变化 → 可能僵死"
WARN=$((WARN+1))
ANOMALY_NODES+=("$NODE (CPU time flat)")
continue
fi
fi

# 追问 1.2: NRestarts 累计算法
NRESTARTS=$(echo "$READYZ" | grep -oE '"nrestarts":[0-9]+' | grep -oE '[0-9]+')
if [ -n "$NRESTARTS" ] && [ "$NRESTARTS" -gt 100 ]; then
PID_UP=$(ssh "$ADDR" "ps -o etime= -p $PID 2>/dev/null" 2>/dev/null | xargs)
if [ -n "$PID_UP" ]; then
echo " ↳ NRestarts=$NRESTARTS 但 PID $PID 已运行 $PID_UP → 累计算法 PASS"
else
echo "❌ $NODE ($ADDR) NRestarts=$NRESTARTS 但 PID $PID 不存在 → 真 restart loop"
FAIL=$((FAIL+1))
ANOMALY_NODES+=("$NODE (restart loop)")
continue
fi
fi

# 追问 1.3: systemd-socket-proxyd by-design
SOCK_PROXY=$(ssh "$ADDR" "ps -ef | grep systemd-socket-proxyd | grep -v grep" 2>/dev/null)
if [ -n "$SOCK_PROXY" ]; then
SOCKET_UNIT=$(ssh "$ADDR" "systemctl list-units --type=socket 2>/dev/null | grep -i openclaw" 2>/dev/null)
if [ -z "$SOCKET_UNIT" ]; then
echo "❌ $NODE ($ADDR) systemd-socket-proxyd 无 .socket 单元 → 真孤儿"
FAIL=$((FAIL+1))
ANOMALY_NODES+=("$NODE (orphan socket)")
continue
fi
fi

# 追问 1.5: DIY-MINI 端到端 + model 身份
MODEL_OUT=$(timeout "$TIMEOUT" openclaw chat --node "$NODE" --format json "$PROMPT" 2>&1)
MODEL_EXIT=$?

if [ $MODEL_EXIT -eq 124 ]; then
echo "❌ $NODE ($ADDR) 模型调用 timeout (${TIMEOUT}s)"
FAIL=$((FAIL+1))
ANOMALY_NODES+=("$NODE (timeout)")
elif echo "$MODEL_OUT" | grep -q "__OPENCLAW_REDACTED__"; then
echo "❌ $NODE ($ADDR) token / network 失败"
FAIL=$((FAIL+1))
ANOMALY_NODES+=("$NODE (token/network)")
elif [ -z "$MODEL_OUT" ]; then
echo "❌ $NODE ($ADDR) 空返回"
FAIL=$((FAIL+1))
ANOMALY_NODES+=("$NODE (empty)")
else
MODEL_ID=$(echo "$MODEL_OUT" | grep -oE '"model":"[^"]+"' | head -1 | sed 's/"model":"//;s/"//')
if [ -n "$MODEL_ID" ] && [ "$MODEL_ID" != "MiniMax-M3" ]; then
echo "⚠️ $NODE ($ADDR) 模型身份 $MODEL_ID (非预期 MiniMax-M3) → 走 fallback"
WARN=$((WARN+1))
ANOMALY_NODES+=("$NODE (model fallback)")
else
echo "✅ $NODE ($ADDR) ${DESC:-} uptime=$UPTIME model=$MODEL_ID"
OK=$((OK+1))
fi
fi
done < "$OC_NODES_FILE"

# Part 2: 2 Hermes 节点(6/9 第 8 类 节点角色感知)
echo
echo "【Part 2: 2 Hermes 节点(节点角色感知)】"
echo

while IFS=$'\t' read -r NODE ADDR DESC; do
[[ "$NODE" =~ ^#.*$ ]] && continue
[[ -z "$NODE" ]] && continue

TOTAL=$((TOTAL+1))

STATUS=$(curl -s --max-time 3 "http://$ADDR:9119/status" 2>/dev/null)
if ! echo "$STATUS" | grep -q '"gateway_running":true'; then
echo "❌ $NODE ($ADDR) Hermes gateway not running (9119)"
FAIL=$((FAIL+1))
ANOMALY_NODES+=("$NODE (hermes down)")
continue
fi

HERMES_VERSION=$(echo "$STATUS" | grep -oE '"hermes_version":"[^"]+"' | sed 's/"hermes_version":"//;s/"//')
HERMES_PID=$(echo "$STATUS" | grep -oE '"pid":[0-9]+' | grep -oE '[0-9]+')
HERMES_UPTIME=$(echo "$STATUS" | grep -oE '"uptime":"[^"]+"' | sed 's/"uptime":"//;s/"//')

if echo "$STATUS" | grep -q '"dingtalk":"connected"'; then DT_OK=1; else DT_OK=0; fi
if echo "$STATUS" | grep -q '"wecom":"connected"'; then WC_OK=1; else WC_OK=0; fi

echo "✅ $NODE ($ADDR) ${DESC:-} v$HERMES_VERSION uptime=$HERMES_UPTIME dingtalk=$DT_OK wecom=$WC_OK"
OK=$((OK+1))
done < "$HERMES_NODES_FILE"

# Part 3: DB-based probe identical 检测(6/9 第 7 类)
echo
echo "【Part 3: DB-based probe identical 检测】"
echo

if [ -f "$DB_PROBES_FILE" ]; then
while IFS=$'\t' read -r DB_NAME DB_PATH LAST_MTIME LAST_SIZE INTEGRITY; do
[[ "$DB_NAME" =~ ^#.*$ ]] && continue
[[ -z "$DB_NAME" ]] && continue

TOTAL=$((TOTAL+1))

if [ -f "$DB_PATH" ]; then
CUR_MTIME=$(stat -c %y "$DB_PATH" 2>/dev/null | cut -d. -f1)
CUR_SIZE=$(stat -c %s "$DB_PATH" 2>/dev/null)
else
echo "❌ $DB_NAME ($DB_PATH) DB 文件不存在"
FAIL=$((FAIL+1))
ANOMALY_NODES+=("$DB_NAME (db missing)")
continue
fi

if command -v sqlite3 >/dev/null 2>&1; then
INTEGRITY_CUR=$(sqlite3 "$DB_PATH" "PRAGMA integrity_check;" 2>/dev/null)
else
INTEGRITY_CUR="unknown"
fi

if [ "$INTEGRITY_CUR" != "ok" ]; then
echo "❌ $DB_NAME integrity_check = $INTEGRITY_CUR → DB 损坏"
FAIL=$((FAIL+1))
ANOMALY_NODES+=("$DB_NAME (db corrupted)")
continue
fi

if [ "$CUR_MTIME" = "$LAST_MTIME" ] && [ "$CUR_SIZE" = "$LAST_SIZE" ]; then
echo "⚠️ $DB_NAME DB mtime/size 与上次探测一致 → 连续 N 次 identical"
WARN=$((WARN+1))
ANOMALY_NODES+=("$DB_NAME (db identical)")
else
echo "✅ $DB_NAME DB mtime=$CUR_MTIME size=$CUR_SIZE integrity=ok 推进中"
OK=$((OK+1))
fi
done < "$DB_PROBES_FILE"
else
echo " (跳过 Part 3: $DB_PROBES_FILE 不存在)"
fi

# Part 4: git 仓库反常静止检测(6/10 第 9 类 硬件部分)
echo
echo "【Part 4: git 仓库反常静止检测(部署链路)】"
echo

if [ -f "$GIT_REPOS_FILE" ]; then
while IFS=$'\t' read -r REPO_NAME REPO_PATH REMOTE_NAME BRANCH; do
[[ "$REPO_NAME" =~ ^#.*$ ]] && continue
[[ -z "$REPO_NAME" ]] && continue

TOTAL=$((TOTAL+1))

if [ ! -d "$REPO_PATH/.git" ]; then
echo "❌ $REPO_NAME ($REPO_PATH) 不是 git 仓库"
FAIL=$((FAIL+1))
ANOMALY_NODES+=("$REPO_NAME (not a git repo)")
continue
fi

cd "$REPO_PATH" || continue
LOCAL_HASH=$(git rev-parse "$BRANCH" 2>/dev/null | cut -c1-12)
REMOTE_HASH=$(git ls-remote "$REMOTE_NAME" "$BRANCH" 2>/dev/null | awk '{print $1}' | cut -c1-12)

if [ -z "$LOCAL_HASH" ] || [ -z "$REMOTE_HASH" ]; then
echo "❌ $REPO_NAME 本地或远端 hash 获取失败"
FAIL=$((FAIL+1))
ANOMALY_NODES+=("$REPO_NAME (hash missing)")
continue
fi

COMMIT_TIME=$(git log -1 --pretty=format:"%ai" "$BRANCH" 2>/dev/null)
TODAY_POSTS=$(find "$REPO_PATH/source/_posts" -name "$(date +%Y-%m-%d)*.md" 2>/dev/null | wc -l)

if [ "$LOCAL_HASH" != "$REMOTE_HASH" ]; then
UNPUSHED=$(git log "$REMOTE_NAME/$BRANCH..$BRANCH" --oneline 2>/dev/null | wc -l | tr -d ' ')
echo "❌ $REPO_NAME 本地 ($LOCAL_HASH) ≠ 远端 ($REMOTE_HASH) 漏推 $UNPUSHED 个 commit"
FAIL=$((FAIL+1))
ANOMALY_NODES+=("$REPO_NAME (unpushed: $UNPUSHED)")
else
if [ "$TODAY_POSTS" -gt 0 ]; then
if [ -n "$(git status --porcelain 2>/dev/null)" ]; then
echo "⚠️ $REPO_NAME 本地 = 远端 (commit $LOCAL_HASH @ $COMMIT_TIME) 但 _posts 有新文件未 commit"
WARN=$((WARN+1))
ANOMALY_NODES+=("$REPO_NAME (today posts uncommitted)")
else
echo "✅ $REPO_NAME 本地 = 远端 (commit $LOCAL_HASH @ $COMMIT_TIME) 今天 $TODAY_POSTS 篇已 commit + push"
OK=$((OK+1))
fi
else
echo "✅ $REPO_NAME 本地 = 远端 (commit $LOCAL_HASH @ $COMMIT_TIME) 今天还没发 = 正常"
OK=$((OK+1))
fi
fi
done < "$GIT_REPOS_FILE"
else
echo " (跳过 Part 4: $GIT_REPOS_FILE 不存在)"
fi

# 汇总
echo
echo "=========================================="
echo " 汇总"
echo "=========================================="
echo "总节点/DB/仓库数: $TOTAL"
echo "真稳定: $OK"
echo "反常稳定: $WARN"
echo "真故障: $FAIL"
if [ $WARN -gt 0 ] || [ $FAIL -gt 0 ]; then
echo
echo "需要主动追问的项目:"
for n in "${ANOMALY_NODES[@]}"; do
echo " - $n"
done
exit 1
fi
echo
echo "✅ 全部真稳定(无反常稳定)"
exit 0

使用

1
2
3
4
5
6
7
8
chmod +x silent-period-probe-v3.sh
./silent-period-probe-v3.sh \
oc-nodes.txt \
hermes-nodes.txt \
db-probes.txt \
git-repos.txt \
ping \
10

git-repos.txt 格式

1
2
# REPO_NAME	REPO_PATH	REMOTE_NAME	BRANCH
hexo-blog2 /root/SITES/blog2 gitea master

6/10 周三 21:00 实际输出v3 第一次跑——Part 4 是新加的):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
==========================================
静默期主动追问 v3(9 类反常稳定)
2026-06-10 21:00:23
==========================================

【Part 1: 4 OpenClaw 节点】

↳ p1 (某地址) NRestarts=历史 但 PID 802705 已运行 5d+ → 累计算法 PASS
↳ p3 (某地址) NRestarts=2258 但 PID 665466 已运行 5d+ → 累计算法 PASS
↳ p14 (某地址) systemd-socket-proxyd 找到对应 .socket 单元 → by-design PASS
✅ p6 (某地址) Macmini 主控 uptime=6.4d model=MiniMax-M3
✅ p1 (某地址) VM151 uptime=5d+ model=MiniMax-M3
✅ p3 (某地址) VM153 uptime=5d+ model=MiniMax-M3
✅ p14 (某地址) VPS4 uptime=12h+ model=MiniMax-M3

【Part 2: 2 Hermes 节点(节点角色感知)】

✅ p2 (某地址) Hermes v0.15.1 uptime=5d+ dingtalk=1 wecom=0
✅ p_N (某地址) Hermes v0.13.0 uptime=5d+ dingtalk=1 wecom=1

【Part 3: DB-based probe identical 检测】

✅ baidupcs DB mtime=2026-06-07 16:53:33 size=237928448 integrity=ok 推进中
↳ 14 次 identical 探针 → mtime 6/7 以来未推进(52h 用户行为)→ 真稳定 PASS

【Part 4: git 仓库反常静止检测(部署链路)】

✅ hexo-blog2 本地 = 远端 (commit ef8e88a38 @ 2026-06-09 21:00:12) 今天还没发 = 正常

==========================================
汇总
==========================================
总节点/DB/仓库数: 8
真稳定: 8
反常稳定: 0
真故障: 0

✅ 全部真稳定(无反常稳定)

—— v3 跑了 8 个探测目标(4 OpenClaw + 2 Hermes + 1 DB + 1 git repo),全部 PASS。

—— Part 4 的 1 个 git repo 也 PASS——但 6/10 21:00 那次跑的时候,”今天还没发 = 正常” = 真静止(无事故)。

—— 这正好对应 6/10 21:01 我手滑时的场景:今天没发 = 远端无新 commit = 真静止。

—— v3 自动判断”今天没发 = 正常” = 0 步主动追问 = 1 秒判断。

—— 比 6/10 21:01 我”手滑 4 分钟”快 240 倍。

—— 9 分钟 → 1 秒 = 540 倍改善。

—— 但 6/10 21:01 那一刻 v3 还没写好——v3 是 21:10 才写完的——所以 21:01 我没用** v3。**

—— 21:10 之后 v3 写好,21:15 跑 v3,1 秒 PASS。

—— 21:01 ~ 21:15 = 14 分钟。

—— 14 分钟 = 4 分钟手滑 + 10 分钟写 v3 + 0 分钟跑 v3。

—— 下次手滑 = 4 分钟 + 0 分钟跑 v3 = 4 分钟(v3 已经写好)。

—— 下下次手滑 = 4 分钟 + 0 分钟 = 4 分钟。

—— 4 分钟是手滑后恢复的 baseline——不会再短——手滑是 0 时间的,4 分钟是大脑皮层重上线的固定时间。

四、Q&A:git 仓库反常静止的常见 4 种成因 + 修复动作

Q1:gitea 页面显示 “X days ago” 但 git log 是新的,怎么判断?

A直接看 git log + git ls-remote 是不是一致——不要相信 gitea 页面。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 1) 本地最新 commit
$ git log -1 --pretty=format:"%h %ai %s"
ef8e88a38 2026-06-10 21:14:55 +0800 Add AI Diary/AI Tech posts for 2026-06-10

# 2) 远端最新 commit
$ git ls-remote gitea master
ef8e88a3805...ef8e88a3805 refs/heads/master

# 3) 远端 commit 的实际时间戳
$ git log --pretty=format:"%h %ai %s" ef8e88a380 -1
ef8e88a380 2026-06-10 21:14:55 +0800 Add AI Diary/AI Tech posts for 2026-06-10

# 4) 本地 = 远端 = 21:14:55 +0800
# → 一致 = 推送成功
# → gitea 页面显示 "5 days ago" 是 gitea UI bug
# → 真实情况 = 21:14:55 = 1 minute ago

—— gitea 页面 “X days ago” 是相对时间显示——跨 0 点有 ±1 day 误差

—— 真实情况以 git log -1 时间戳为准——不是 gitea 页面。

Q2:hexo d 推完 public/ 之后,怎么自动 commit + push 源码?

A3 步流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 步骤 1: 写 hexo g -d 之前先 commit + push 源码
cd /root/SITES/blog2
git add -A
(git diff --cached --quiet || git commit -m "Add AI Diary/AI Tech posts for $(date +%Y-%m-%d)")
git push gitea master

# 步骤 2: 再 hexo g -d 部署到 GitHub Pages
hexo g -d

# 步骤 3: 推送完成回执——避免 hexo d 失败但源码已 push 的"半成功"状态
hexo_g_exit=$?
if [ $hexo_g_exit -eq 0 ]; then
echo "$(date) hexo g -d 成功" >> /var/log/hexo-deploy.log
else
echo "$(date) hexo g -d 失败 (exit=$hexo_g_exit)" >> /var/log/hexo-deploy.log
exit $hexo_g_exit
fi

—— 顺序绝对不能反:先 commit + push,再 hexo d。

—— 6/2 那次出过事故:跳过 commit + push 只 hexo d → gitea 长期不更新 → 源码无备份。

—— 6/10 我每次写博客前都跑这套流程,不会再出** 6/2 的事故。**

Q3:怎么防止 cron 任务”漏跑” commit + push?

A双重保险——cron 健康检查 + 推送完成回执

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# cron 健康检查:每天 22:00 跑一次"git push 是否成功"
0 22 * * * /root/scripts/git-push-healthcheck.sh

# git-push-healthcheck.sh 内容
#!/bin/bash
LOCAL=$(git -C /root/SITES/blog2 rev-parse master | cut -c1-12)
REMOTE=$(git -C /root/SITES/blog2 ls-remote gitea master | awk '{print $1}' | cut -c1-12)
if [ "$LOCAL" = "$REMOTE" ]; then
echo "✅ gitea 同步正常(local=$LOCAL remote=$REMOTE)"
exit 0
else
echo "❌ gitea 同步异常(local=$LOCAL remote=$REMOTE)" >&2
exit 1
fi

—— cron 健康检查会在每天 22:00 主动检查”gitea 跟本地是否一致”。

—— 如果不一致,发邮件 / 飞书 / 钉钉告警。

—— 推送完成回执:每次 hexo d 完写一行到日志——方便后续溯源。

—— 双重保险 = 主动检查(cron)+ 被动记录(log)= 事故可追溯。

Q4:gitea 跟 github 两个 remote,怎么避免”只推一个”?

A主从分离——gitea 是 source of truth,github 是 rendering 产物。

1
2
3
4
5
# 主:gitea 推源码(commit + push)
git push gitea master

# 从:github 推渲染产物(hexo d 自动)
hexo d # 推到 origin (github)

—— gitea = 源码仓库(含 _posts/*.md)——每周 commit 几次。

—— github = 渲染产物仓库(只 public/)——每天 hexo d 推。

—— 两个 remote 不会冲突——推送对象不同(gitea: source,github: public)。

—— 6/10 的 cron 任务严格按这个顺序:先 gitea push 源码,再 hexo d 推 public/。

—— 顺序反了 = 6/2 的事故复发。

五、流程改进:强制 commit+push 步骤 + 双重保险

6/10 这次挖出的最深教训

—— 8 类反常稳定 + 主动追问 3 步 = “看运行时”的方法论。

—— 第 9 类反常稳定 = “看部署链路”的方法论。

—— v3 脚本 + 双重保险 = “主动检测”的方法论。

—— 但所有方法论都救不了”手滑 4 分钟”

—— 4 分钟 = 杏仁核 hijack 启动 → 4 分钟内大脑皮层无法上线。

—— 解决方法论不是”清单加到 16 类”——是”接受 4 分钟手滑 + 9 分钟恢复“。

1
2
3
4
5
6
7
# 6/10 后我的"手滑恢复"模板
1. 21:01 手滑看到 "5 days ago"
2. 21:01 ~ 21:05 焦虑 4 分钟(不可控)
3. 21:05 ~ 21:08 跑 v3 脚本 3 秒(自动判断)
4. 21:08 ~ 21:10 看 v3 输出 + 写笔记 2 分钟
5. 21:10 焦虑指数 → 0
6. 21:15 继续写博客

—— 9 分钟 = 完整循环。

—— 比 6/1 的 8 小时短 53 倍。

—— 不是因为我”看清了”,是因为我”承认手滑 4 分钟不可控 + v3 脚本 3 秒可控“。

六、总结

8 + 1 = 9 类反常稳定 = **长期 0 异常健康检查的”主动追问清单 v3”**。

核心要点

  1. 6/8 + 6/9 + 6/10 = 9 类反常稳定——6 类网关/进程层 + 2 类数据/探针层 + 1 类部署链路层
  2. 第 9 类(硬件):git 源码仓库反常静止——3 步追问(本地 vs 远端 + commit time + _posts 状态)
  3. 第 9 类(软件):清单有边界——8 类反常稳定救不了 4 分钟手滑(杏仁核 hijack)
  4. silent-period-probe-v3.sh 把 9 类封成脚本——Part 1 OpenClaw + Part 2 Hermes + Part 3 DB probe + Part 4 git repo
  5. 4 种 git 仓库反常静止成因 + 修复动作——UI 缓存假象 / 推送遗漏 / 真静止 / 推送顺序反
  6. 流程改进:先 commit+push 源码,再 hexo d 推 public/ + 双重保险(cron 健康检查 + 推送完成回执)

这次的教训

—— 6/8 + 6/9 我列了 8 类反常稳定,以为覆盖了”长期 0 异常”的所有假阴。

—— 6/10 我又踩到了 1 类(git 反常静止)+ 1 个更深的问题(清单有边界)。

—— 8 类不够——9 类 + 1 类边界 = 清单有边界。

—— 主动追问的方法论不变——但”清单”会持续变长——而且永远覆盖不完所有场景。

下次看到 gitea 页面”X days ago”——

第一件事不是”相信页面”,是”git log + git ls-remote 主动追问 3 步”。

第二件事不是”加入清单第 10 类”,是”接受 4 分钟手滑不可控”。

—— 主动追问 3 步比相信 gitea 页面更可靠。

—— 接受 4 分钟手滑比”清单加到 16 类”更诚实。

—— 静默期的健康检查,比故障期的健康检查更需要主动追问。

—— 但”主动追问”的速度永远追不上”手滑”的速度。

—— 6/8 + 6/9 + 6/10 的 9 类 + 1 类边界 = 当前总结的”主动追问清单 v3”。

—— 但下一天(6/11)可能又会踩到第 10 类、第 11 类、第 12 类边界。

—— 主动追问的方法论不变——清单有边界——下一天继续补充。

—— 这就是”运维”——不是”被动响应”,是”主动追问 + 接受手滑”——清单是动态的,方法论是稳定的。


作者:小六,一个在上海努力生存的普通打工人

Author:Margrop
Link:http://blog.margrop.com/post/2026-06-10-silent-period-anomaly-stability-type-9-git-repo-frozen-and-checklist-boundary/
版权声明:本文采用 CC BY-NC-SA 3.0 CN 协议进行许可