SKILL.md 6.7 KB

name: root-owned-path-disposition description: Android/Termux 双 uid 环境下「路径属主 root」的判读与两向处置——同一个 root 属主文件,「要读它」⇒ su -c + 解释器全路径;「要覆写它」⇒ rm 后重跑(家目录写权足够)。含实测签名(root:644 产物静默挡住 gen 重跑、root:600 探针、root:600 SKILL.md 只能走影子副本、jobs.json 被 root 重写致 termux tick 恒 Permission denied)。触发词:Errno 13、Permission denied、root root 600、root root 644、覆写失败、属主、chown 不生效、python3 inaccessible or not found、su -c。 version: 1.0

category: android

root 属主路径的判读与两向处置

适用:本机(Pixel / Android 16 + Termux;termux 用户 u0_a329,su 可拿 root)任何一份 属主是 root 的文件出现在 termux 家目录树下(~、~/tmp、~/.hermes、$TMPDIR)时的判读与处置。 典型来源:某轮用 su -c 跑出的产物与状态戳、root 侧进程重写的共享状态文件、 随包/手工分发的 root:600 运维脚本与 SKILL.md。

一、判据:先 ls -la 看属主,再决定方向

ls -la <path>          # 只看第 3/4 列(owner/group)与 mode
你要对它做什么 属主 root 的形态 正确动作
读它 / 跑它(探针脚本、SKILL.md、日志) root:600 su -c "<解释器全路径> <脚本绝对路径> <args>" —— su 环境没有 Termux PATH ⇒ 裸 python3 回 inaccessible or not found
覆写它(重跑 gen / 重新生成产物 / 覆盖状态文件) root:644 rm -f 后重跑 —— 家目录属 u0_a329,目录写权足以 unlink,不必 su、不必 chown
只想读、不想每次 su root:600 找影子副本(§三.3),或请上游把它 chown 回 u0_a329

通则一句话:同一份 root 属主文件,要读它 ⇒ su;要覆写它 ⇒ rm。别一律 su。

二、为什么「覆写」既不能 su 也不能 chmod/chown

  • u0_a329 侧 open(...,"w") 需要文件写权 ⇒ 截断失败 [Errno 13] Permission denied; 脚本一般不会先替你删旧档 ⇒ 整步必失败,而退出码还可能被 | tail 之类包装层洗成 0 (看着像「跑了、没输出」)。
  • chmod/chown 需要 su,且 root 侧下次再写又变回 root(属主翻转会复发,见 §三.4)⇒ 是「修了这次、下次还踩」的假修。
  • rm 只要求目录写权(家目录属 u0_a329)⇒ 无副作用、不需 root,是正解。改属主不是判据,能删能写才是。

三、实测案例(本机)

  1. root:644 产物静默挡住 gen 重跑(2026-10-07 01:00 轮):~/daily_digest_tgo_all_2026-10-06.md = root root 644 8621B(前一日 10:37 某个 su 路径留下的半日残件)⇒ 重跑前 rm -f(exit 0) ⇒ gen_agg_digest.py 随即 exit 0、产物 20779B。 ⚠️ 症状静默:只看「文件在不在」会以为「已有产物、不用跑」,从而整天不补(当轮已在案才算发现)。 ⇒ 补跑/重跑类动作的前置:ls -la 目标产物,属主 root 就先删。
  2. root:600 探针 ⇒ su -c + 解释器全路径:~/tmp/digest_gap_for_date.py 为 root root 600:
    • python3 ~/tmp/digest_gap_for_date.py … → can't open file '…': [Errno 13] Permission denied (是权限,不是文件不存在 ⇒ 别去查路径拼写、别以为被清理了)
    • su -c "python3 …" → /system/bin/sh: python3: inaccessible or not found(su 环境无 Termux PATH)
    • su -c "/data/data/com.termux/files/usr/bin/python3 /data/data/com.termux/files/home/tmp/digest_gap_for_date.py tgo 2026-10-06" ✅
  3. root:600 SKILL.md ⇒ 只能走影子副本:~/.hermes/skills/cron/cron-scheduler/SKILL.md 是 root root 600 ⇒ skill_view 恒报 not found、Job prompt 每轮打 Skill(s) not found and skipped: cron-scheduler; 实务读 $TMPDIR/_sched_read_*.md(历轮拷出的全文副本,28981B)。 新鲜度核对 = su -c "stat -c '%U %G %a %s %y' <SKILL.md>" 与副本比字节数 + mtime。
  4. 被 root 侧进程重写的共享状态文件 ⇒ termux 侧恒 Permission denied:两个 gateway(root + u0_a329)并跑时, root 侧原子替换重写 ~/.hermes/cron/jobs.json ⇒ 属主变 root:600 ⇒ termux 侧每 tick 一条 Failed to read cron database: [Errno 13] Permission denied。 ⚠️ 这类别在无人值守轮内自发 kill / chown(可能是本会话的父进程、且 chown 不持久) ⇒ 升级给用户拍板,轮内只留痕。

四、同轮附带:两个「读数假象」(canonical 家在 scheduler-round-authority-caliber,该 skill 只读)

  • 审计器 ① 报 自报=无 ≠ 该轮 FAILED / 空档(2026-10-07 01:00 实测): cross-round-handoff-ledger/scripts/handoff_round_audit.sh <DATE> 把某份 output md 打成 自报=无, 而该 md 首行 = # Cron Job: 调度器(无 (FAILED))、正文确有自报行 —— 只因写成 **[HH:MM] scheduler: daily=…**(加粗),审计器的锚定 grep 不认 ** 前缀。 ⇒ 5 秒复核:看 md 首行有无 (FAILED) + search_files(pattern='scheduler: daily=') 是否命中; 别据 自报=无 补记、别判空档。想让审计器下次认得出 ⇒ 汇总行别加粗(账本条目里外层字段名加粗无妨, 被审的是 output md)。
  • executions.db 不能用 order by id 排序:id 是 UUID hex ⇒ order by id desc 排的是字典序, 读到的「最近几行」实为乱序(实测 13:01 / 02:00 / 23:00 / 10-05 混排)⇒ 误判「本轮没注册 / 有双派发」。 ⇒ 一律 order by started_at;判「每整点恰 1 轮 / 无双派发」用 where started_at like '<今天>%' + order by started_at。 实测正确读数:cd69c503fbb7 | running | 2026-10-07T01:00:34(当日首行)。

五、落点说明(为什么这份 skill 存在)

本机 cron/ meta/ wechat/ android/ 四族的带判据 skill 全部 created_by=None(手工维护) ⇒ skill_manage 的 patch / write_file 一律 Refusing … it is not agent-created(零写入,不是半成功)。 2026-10-07 实测:连自称「唯一可写落点」的两个 skill(cron/scheduler-round-authority-caliber、 cron/daily-digest-gen-window-ops)也被拒。 ⇒ 纪律:试 1 个被拒即停(别逐个试完),新实测落 agent-owned 落点(create 是本机唯一还通的写路径,本条即一处)。 ⚠️ 这些手工 skill 正文里的「本 skill = 可写落点」自述已被实测推翻,别再按它选落点。