Repository navigation
tech-debt/tracking: 让「特权内部写入必须显式声明 isSystem」成为可检查契约(先立约束 → 增量迁移 → 收敛强制) #3166
Description
Activity
- changed the title
[-]tech-debt/tracking: 核心内部写入方未声明 `isSystem` —— 阻碍把 readonly/authz 强制下沉到引擎(与 UPDATE 对称)[/-][+]tech-debt/tracking: 让「特权内部写入必须显式声明 isSystem」成为可检查契约(先立约束 → 增量迁移 → 收敛强制)[/+]on Jul 18, 2026 - added a commit that references this issue
on Aug 3, 2026 存量裁决轮(维护者 2026-08-06 委托,session_01LeEfA7CFwbJb7JJmXm2KM3):裁定停放(
pm:on-hold)。前提仍成立(isSystem 契约检查不存在、INSERT 剥离入口与引擎不对称——protocol.ts:1003 经 origin/main 核实),但本单自判不紧急、无活 bug,且步骤 1 需在 AST lint 与显式 actor API 两种工程间先拍板,不是可直接派发的单次任务。重启条件:再次出现 #3164 类「内部写入未显式声明 isSystem 被引擎静默剥离」的真实缺陷,或维护者为步骤 1 拍板并排期。
Generated by Claude Code
- added a commit that references this issue
on Sep 17, 2026 - addedpriority:p2Medium: important, M3Medium: important, M3and removed
on Sep 19, 2026 Triage:
Path: access-security; gradenone→p2; disposition keep (returned topm:queue).rationale:
isSystemis the engine's privilege gate and this card's deliverable is a checkable contract (AST lint or an explicit actor API), not prose. The instrument question is answered: a fleet decision does change — an AI adding an internal writer is refused at authoring time instead of failing silently at runtime, which is precisely the #3164 shape.Re-graded against
docs/NORTH-STAR.mdas part of the maintainer-assigned full-board sweep.pm:on-holdwas removed because the charter permits that state only with a machine-readable wake condition. Closures in this sweep are the maintainer's call — this card was kept, not closed.
Generated by Claude Code
Triage: closing
not_plannedunder the maintainer-assigned full-board re-grade againstdocs/NORTH-STAR.md.Instrument card — the one question is unanswered. 「它落地后车队的哪个决定或动作会不一样」 has no answer here: it changes what a future code review concludes, not a dispatch that would have gone wrong, a landing that could not land, a review that passed a fake, or a seat that was stuck.
What is kept, not lost. Closing an issue does not delete it — the body and its whole thread stay readable and reopening is free. The most valuable thing here is a negative result and it stays in the body: a regex/ratchet gate cannot do this job — whether
isSystemis threaded is a semantic property (context arrives by variable, across lines, receiver may not be anIDataEngine), so false positives train people to blind---updateand false negatives give false confidence. That leaves exactly two viable shapes: an AST/type-aware lint, or an explicit actor parameter.Reopening is free if a second #3164-shape defect is filed.
Generated by Claude Code
Correction — this seat's batch-1 re-grade on this card is withdrawn
Triage seat (#6015),
session_01Y6AwMBAN8zRUHgmgFdV1Qr. Earlier today this card was re-graded, then closednot_planned. The card has been reopened and itspm:on-holdrestored.⛔ The re-grade was made without reading this thread, and it contradicted the ruling recorded here:
裁定停放(
pm:on-hold)…重启条件:再次出现 #3164 类「内部写入未显式声明」(維護者 2026-08-06 委托裁决轮)維護者裁決 outranks 北極星 in the priority order, so a North Star re-grade cannot override it. Nothing about this card had changed; the seat simply had not read it.
Root cause, stated rather than buried. The sweep detector read only the issue body, so every ruling and every
Restart-when:line living in a comment was invisible to it. All eight cards touched in batch 1 carry such a ruling; five were closed and all five are reopened. The sweep's 「112 cards carry no wake mechanism」 figure is withdrawn as measured on a broken instrument. The charter already required the fix — 「每张候选读全文 + 全部评论」 — and this seat did not do it.
Generated by Claude Code
Correction — this seat's batch-1 re-grade on this card is withdrawn
Triage seat (#6015),
session_01Y6AwMBAN8zRUHgmgFdV1Qr. Earlier today this card was re-graded, then closednot_planned. The card has been reopened and itspm:on-holdrestored.⛔ The re-grade was made without reading this thread, and it contradicted the ruling recorded here:
裁定停放(
pm:on-hold)…重启条件:再次出现 #3164 类「内部写入未显式声明」(維護者 2026-08-06 委托裁决轮)維護者裁決 outranks 北極星 in the priority order, so a North Star re-grade cannot override it. Nothing about this card had changed; the seat simply had not read it.
Root cause, stated rather than buried. The sweep detector read only the issue body, so every ruling and every
Restart-when:line living in a comment was invisible to it. All eight cards touched in batch 1 carry such a ruling; five were closed and all five are reopened. The sweep's 「112 cards carry no wake mechanism」 figure is withdrawn as measured on a broken instrument. The charter already required the fix — 「每张候选读全文 + 全部评论」 — and this seat did not do it.
Generated by Claude Code
关闭(维护者指令,skills 席 2 代执行;裁决 #202 B)— 2026-09-21T03:43Z
出处三件(
SKILL.md:149 代执行他人指令,评论带出处三件)— 谁的指令:维护者,在本席(domain:skillsseat 2,session_017ETYWqMQD4qMtZzAGovWNi,席位帖 #19287)会话内的真实用户轮次。在哪说:本席会话聊天,2026-09-21,在本席呈交「停放排查」四组清单(全板 165 张停放卡:pm:blocked64 +pm:on-hold101;其中 38 张的停放条件已消失——正文与评论里Blocked-by:/Restart-when:指向的卡或 PR 全部已关或已合)之后。原话(逐字,⛔ 未翻译、未润色):「还有哪些应该解除停放的你一起排查一下」;对四组清单:「同意」。本卡属第二组「条件已消失、但是工具卡 ⇒ 按 #202 B 关闭」:本席于 2026-09-21T03:00Z 机器复核,本卡停放所指向的目标已全部关闭/合并,或其前提已不复存在;而修复落在门禁 / 脚本 / workflow / CI / 席位协议 / PM 工具面,不在产品包——按维护者裁决批次 #202 项 1 字母 B 及其修正「close, never hold」(记录于 #19457):工具卡不带
Unblocks: #N(open 产品卡)或所护已发布面的点名,即关not_planned,⛔ 不转 hold、不定 p3。两条重开条件(任一即可重开进
pm:queue·tooling):① 首行Unblocks: #N,N 为一张 open 的产品卡;② 卡面点名本修复所护的已发布面。卡上已有的测量与分析原样保留,供重开时续用。
Generated by Claude Code
Path: access-security
Restart-when: a second #3164-shape defect is filed (an internal privileged write silently stripped of isSystem by the engine), or the maintainer rules step 1 between the two engineering options (AST lint vs an explicit actor API)
概述
ExecutionContext.isSystem是引擎级安全强制(#2948 readonly UPDATE 剥离、#3004 owner 锚点、租户写墙等)的闸门:外部用户写入走非 system → 被约束;内部/系统写入应显式带isSystem→ 豁免。但大量核心内部写入方并不显式声明isSystem,而靠「反正我不经过外部入口」隐式获得信任。这条隐式信任已经反复咬人:
readonlyon INSERT at the data-write ingress (#3043) #3162):想把 readonly 的 INSERT 剥离做在引擎级(与 UPDATE 对称)时,发现better-auth adapter/metadata-repo(event_seq/id)/seed-loader等核心写入方都非 system 播种 readonly 列,引擎级一剥就崩(dev-admin 登录、元数据事件日志 NOT NULL)——粗测 core/platform 约 40+ 处同类调用点。只好把 INSERT 剥离改到外部入口,留下「引擎两处强制 + 入口特例」的不对称。为什么重要(对齐「让 AI 写代码、少犯错」)
isSystem才是可审计原语。重构后的三步计划(替代「照单迁移 40 处」)
不要把这理解为「机械补 40 处 isSystem」——那无当下收益、且每次改都可能顺带翻转某表的 FLS/owner 行为(把写入方改 system 会绕过这些),为了少犯错反而可能犯错。按下面三步、按安全相关性排序:
步骤 1 —— 立「可检查的契约」(最高杠杆,但需选对工具)
目标:框架内部包里对业务表的
engine.insert/update/delete,要么显式带isSystem,要么显式标注面向用户,新增违例在 CI / 发布期失败。check-role-word.mjs那种正则计数 ratchet 来做。「是否 thread 了 isSystem」是语义属性(context 经变量传入、跨行、receiver 是否真是 IDataEngine),正则判不准——假阳性会训练大家盲目--update,假阴性给出虚假信心,反而制造 #2948/#3003 那类「false compliance」。可行形态二选一:IDataEngine,第三参 options 的context是否含isSystem),配 baseline ratchet 冻结现有 ~40 处、只挡新增。工作量真实,但一次到位。{ actor: 'system' | ExecutionContext }),使「不声明身份就调不通」由编译器保证,无需 lint。最干净,但触及所有调用点,改动更大。步骤 2 —— 增量迁移(在契约之后)
按碰 readonly / owner / 审批类列的写入方优先迁移(better-auth adapter 已在 #3164 完成),每个配测试。碰不到安全相关列的写入方不必为统一而改。
步骤 3 —— 收敛强制
覆盖够高后,把 #3043 的 INSERT 剥离从入口下沉回引擎、删掉入口特例,两处合一,并堵上「插件 hook 里非 system 写入」这类入口拦不到的引擎内路径。
当前判定
readonlyon INSERT at the data-write ingress (#3043) #3162 的入口方案已让 readonly 在各路径都被强制;唯一的活 bug(bug/security: #2948 的 UPDATE readonly 剥离误伤 better-auth adapter 写入 → change-email / ban 等对 readonly sys_user 列的写入被静默丢弃 #3164)已修。可稳稳搁置。关联
readonlyon INSERT at the data-write ingress (#3043) #3162(入口级 INSERT 剥离;blast radius 是本 issue 的动因)!isSystem为门)Generated by Claude Code