You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
A hook write-set finding on a handler-authored hook is reported at hooks[i].body.source — a key the author never wrote — on both os build and os lint #16546
Filed unassigned from #16095 (branch claude/issue-16095-hook-rules-reach-handler-authored-hooks). Observed on origin/main @ 5d55afec4d for os build, and on that branch for os lint once it judges the lowered view.
os build (and now os lint) reports hook-api-update-readonly-field with where: hook "escalate" > body and path: hooks[0].body.source. There is no body in the source file: the body exists only inside the lowered stack the command built. An author grepping their project for body.source finds nothing.
Why it is shaped this way
lowerCallables replaces handler: fn with handler: '<ref>' plus body: { language: 'js', source }, and the validators (validate-readonly-hook-writes.ts, validate-hook-body-writes.ts) judge the stack they are given, so they cannot know the body was lowered. #16095 deliberately kept os lint's output byte-identical to os build's for the same hook (the repo's "same wording on every command" rule), so the remap belongs in one place, not in one command.
Options
A. Leave as is; document that a lowered hook's findings point at the lowered body (the hook-bodies.mdx write-set section now says lowering happens, but not where the path points).
B. Have the registry runner (runAuthoringRules) or lowerCallables carry a lowered-from marker per hook, and have the two hook validators say path: hooks[i].handler plus a message suffix ("judged on the metadata body lowered from the inline handler") when it is set — one implementation, both commands agree.
C. Emit both: keep path on the lowered body and add a sourcePath field pointing at hooks[i].handler.
B keeps the finding actionable without a second dialect; C is additive but widens the finding shape (a published contract for --json consumers).
Related: #16095 · #13653 (the readonly hook rule) · #4271 (the write-set rule's original scope).
✅ B:让 lowerCallables / runAuthoringRules 带 lowered-from 标记;两个判定器(validate-readonly-hook-writes.ts / validate-hook-body-writes.ts)在标记存在时报 path: hooks[i].handler,并加后缀说明「judged on the metadata body lowered from the inline handler」。一份实现,两个命令一致。
{
"issue": 16546,
"status": "done",
"branch": "claude/issue-16546-lowered-hook-finding-path",
"pr": "https://github.com/objectstack-ai/objectstack/pull/17438",
"premise_still_valid": true,
"summary": "lowerCallables now records which hooks[*].handler ref strings had their body minted from the author's inline handler (LoweringResult.loweredHookRefs). All four CLI lowering doors (os build union+per-package, os lint, os validate, scaffold validate) thread that set through runAuthoringRules ctx.loweredHookRefs. validateReadonlyHookWrites and validateHookBodyWrites share one new helper, hookBodyFindingLocation, which redirects a lowered hook's finding to path: hooks[i].handler plus a message suffix, leaving an author-written body's path unchanged. No verdict moved; verified byte-for-byte parity between os build --json and os lint --json on the built CLI.",
"tests": "packages/lint: vitest run of the two rule test files + reference-integrity-suite + authoring-rule-wiring + authoring-rule-input-tier -> 5 files, 179 tests, all pass. packages/cli: vitest run --project unit of lower-callables.test.ts + lint-hook-rules-reach-handler-hooks.test.ts -> 2 files, 21 tests, all pass (includes updated RED/CONTROL path assertions and a new scaffold-validate-door assertion). typecheck clean for both packages (tsc --noEmit + check:test-typecheck). Dependency-closure build (pnpm --filter '@objectstack/lint^...' build) exit 0. Manual CLI verification on built dist: os lint --json and os build --json on the card's repro both report path: hooks[0].handler with the identical message/suffix, exit 1; the body-authored control keeps path: hooks[0].body.source on both commands, exit 1. Derived-gate sweep (node scripts/pm/dispatch-gates.mjs --commands): 63 families, 60 exit 0, 3 PREREQUISITE NOT MET (exit 3: check:dual-build-cjs-loads, check:i18n-coverage, check:type-check-debt) each requiring a full whole-workspace pnpm build outside local scope -- NOT MEASURED, none name a touched file. cli integration vitest tier and the e2e spawn test not run locally (no integration-layer/bin/ files touched; e2e asserts rule ids/exit codes only, not path) -- declared to CI.",
"mcp_calls": "4",
"open_questions": [],
"out_of_scope_findings": []
}
Activity
os-zhuang commentedon Sep 8, 2026
分诊 ·
domain:devx/bug/priority:p3/pm:queue/type: Bug分诊席,session
session_01SwJQDFKe8tVit3BXQ9EfR5。本卡此前零标签 ⇒ 无车道无状态,不在任何队列。归位。车道⚠️ 若落法选在
domain:devx:两个判定器(validate-readonly-hook-writes.ts/validate-hook-body-writes.ts)与runAuthoringRules在packages/lint,车道表该行含packages/lint。lowerCallables而它不在packages/lint,那是一次声明过的跨域外溢,认领时在认领评论里申报文件面。类别 (a):工具输出了一个不存在的位置
⛔ 这不是「诊断结论错了」——结论是对的。错的是它给出的坐标:
path: hooks[0].body.source,而源文件里根本没有body。卡自己给了可执行的判据:作者 grepbody.source一无所获。⇒ 一个可复现的、可被证否的错误输出,落 (a),type: Bug。为什么 p3(以及升级触发器)
where: hook "escalate" > body里点了名字,顺着 hook 名能定位。⇒ 损害有限。--json的path是给编辑器跳转 / CI 注解用的,一个解析不出来的路径会跳到不存在的地方或静默失败。path并因此跳错/失效被测到 ⇒ 改判 p2。⛔ 在拿到那个读数之前不写成零,也不写成 p2。方向:B,按现行判据定死
path,另加sourcePath)。它让两个字段并存而其中一个已知是错的 —— 按「③ 防 AI 犯错」这是最差的一种:下一个消费者仍然可能读那个错的。而且它加宽--json的已发布形状,代价更高。lowerCallables/runAuthoringRules带lowered-from标记,两个判定器在标记存在时报path: hooks[i].handler并加后缀说明「judged on the metadata body lowered from the inline handler」。一份实现,两个命令一致 —— 这正符合 lint: everyhook-body-*/hook-api-update-readonly-*rule is unreachable for a hook authored as ahandlerfunction — the whole family is gated onbody.language === 'js', and the reference CRM has 39 hooks and 0 bodies #16095 刻意维持的「同一 hook 在每个命令上逐字同文」那条规矩。按「① 缩小特例」(一个
path字段一个含义)与「④ 不扩散」(不新增已发布字段)两条,B 胜出。边界
os build与os lint对同一个 hook 的输出逐字相同。⛔ 只修一个命令就是把 lint: everyhook-body-*/hook-api-update-readonly-*rule is unreachable for a hook authored as ahandlerfunction — the whole family is gated onbody.language === 'js', and the reference CRM has 39 hooks and 0 bodies #16095 刚统一的东西重新劈开。path改指向属于--json输出的行为变更:认领时自答一句是否构成Clause-②,判是就同笔记上并挂载体。readonly" has no author-time signal — the strip is silent, and six fields in the reference app carry a hand-written comment as the only protection against re-adding the flag #13653(readonly hook 规则)与 L2 hook body 写集不透明:把「写了目标对象上不存在的字段」从 accepted gap 变成作者时 lint 告警 #4271(write-set 规则的原始范围)的判定内容;本卡只动位置与措辞。⛔ 分诊席边界照旧:不认领、不派发、不写码、不合并。
Generated by Claude Code
baozhoutao commentedon Sep 10, 2026
Claim: session_012GKcPZbMoGq7WPzKLfRBTU · claude/issue-16546-lowered-hook-finding-path
派发(本评论来自
domain:devx执行 PM 席 · 座位贴 #6023)。assignee 与本条 claim 由本席代 dev 落;dev 继承二者,⛔ 不再发第二条 claim,⛔ 不写 assignee。方向已由分诊裁定:B,⛔ 不要重开 A/C
分诊把三个选项都判了,理由照抄(本席复核后同意):
path,另加sourcePath)——「让两个字段并存而其中一个已知是错的」,且加宽--json的已发布形状,代价更高。lowerCallables/runAuthoringRules带lowered-from标记;两个判定器(validate-readonly-hook-writes.ts/validate-hook-body-writes.ts)在标记存在时报path: hooks[i].handler,并加后缀说明「judged on the metadata body lowered from the inline handler」。一份实现,两个命令一致。判据
path指向源文件里真实存在的键。卡面给了可执行的判据:作者 grepbody.source一无所获 —— 修完之后,作者 grep 你报出的路径必须找得到。os build与os lint对同一个 hook 的输出逐字相同。⛔ 只修一个命令,就是把 lint: everyhook-body-*/hook-api-update-readonly-*rule is unreachable for a hook authored as ahandlerfunction — the whole family is gated onbody.language === 'js', and the reference CRM has 39 hooks and 0 bodies #16095 刚统一的东西重新劈开。两边的实际输出都贴进 PR body。readonly" has no author-time signal — the strip is silent, and six fields in the reference app carry a hand-written comment as the only protection against re-adding the flag #13653(readonly hook 规则)与 L2 hook body 写集不透明:把「写了目标对象上不存在的字段」从 accepted gap 变成作者时 lint 告警 #4271(write-set 规则原始范围)的结论一个字不许改 —— 卡面明写「结论是对的,错的是坐标」。body)的 hook,其path不变。⭐ 一个前后读数相同的探针不是对照 —— 这一腿要读的正是「没被 lower 的那半确实没动」。Clause-②不是默认的 no —— 你必须自答分诊点名了这一条,本席原样传下,并且不替你预判:
⇒
--json的path是已发布的消费面(编辑器跳转 / CI 注解读它)。你自己判,并把判断和依据写进 PR body。 判yes就按 clause-② 的载体规矩走(**Clause-②**: yes写进 PR body 行首,并挂needs:contract-review一类的载体);判no就写明为什么改一个已发布字段的取值不构成契约变更。⛔ 不要因为本席平常的卡都写no就照抄no。⭐ 发之前拿scripts/pm/check-clause2-carriers.mjs的readClause2Line()自验你写的那一行读得到。车道与外溢
分诊的话:两个判定器与⚠️ 若落法选在
runAuthoringRules在packages/lint,车道对。lowerCallables而它不在packages/lint,那是一次跨域外溢 —— 在 PR body 里申报你实际动的文件面,⛔ 不要默默扩。边界
content/docs/releases/、docs/adr/**、.claude/**、skills/**、AGENTS.md、CLAUDE.md。git stash;⛔ 已推的分支不 rebase / amend / force-push。packages/lint是发布包,而这是它输出形状的行为变更。⛔ 先量(看files[]与你改的路径),量完该写就写,该skip-changeset就打标签。⛔ 不要默认 skip。Closes #16546。Generated by Claude Code
fix(lint): report a hook write-set finding on a lowered hook at hooks…
baozhoutao commentedon Sep 10, 2026
os-dev-report
{ "issue": 16546, "status": "done", "branch": "claude/issue-16546-lowered-hook-finding-path", "pr": "https://github.com/objectstack-ai/objectstack/pull/17438", "premise_still_valid": true, "summary": "lowerCallables now records which hooks[*].handler ref strings had their body minted from the author's inline handler (LoweringResult.loweredHookRefs). All four CLI lowering doors (os build union+per-package, os lint, os validate, scaffold validate) thread that set through runAuthoringRules ctx.loweredHookRefs. validateReadonlyHookWrites and validateHookBodyWrites share one new helper, hookBodyFindingLocation, which redirects a lowered hook's finding to path: hooks[i].handler plus a message suffix, leaving an author-written body's path unchanged. No verdict moved; verified byte-for-byte parity between os build --json and os lint --json on the built CLI.", "tests": "packages/lint: vitest run of the two rule test files + reference-integrity-suite + authoring-rule-wiring + authoring-rule-input-tier -> 5 files, 179 tests, all pass. packages/cli: vitest run --project unit of lower-callables.test.ts + lint-hook-rules-reach-handler-hooks.test.ts -> 2 files, 21 tests, all pass (includes updated RED/CONTROL path assertions and a new scaffold-validate-door assertion). typecheck clean for both packages (tsc --noEmit + check:test-typecheck). Dependency-closure build (pnpm --filter '@objectstack/lint^...' build) exit 0. Manual CLI verification on built dist: os lint --json and os build --json on the card's repro both report path: hooks[0].handler with the identical message/suffix, exit 1; the body-authored control keeps path: hooks[0].body.source on both commands, exit 1. Derived-gate sweep (node scripts/pm/dispatch-gates.mjs --commands): 63 families, 60 exit 0, 3 PREREQUISITE NOT MET (exit 3: check:dual-build-cjs-loads, check:i18n-coverage, check:type-check-debt) each requiring a full whole-workspace pnpm build outside local scope -- NOT MEASURED, none name a touched file. cli integration vitest tier and the e2e spawn test not run locally (no integration-layer/bin/ files touched; e2e asserts rule ids/exit codes only, not path) -- declared to CI.", "mcp_calls": "4", "open_questions": [], "out_of_scope_findings": [] }Generated by Claude Code
Generated by Claude Code
fix(lint): report a hook write-set finding on a lowered hook at hooks…