Repository navigation
v17 verification — rollup of all 17 defects found working the #3909 checklist #4482
Description
Rollup of every defect found while working the v17 verification checklist on #3909. 17 issues, all open. Filed across four verification runs against main @ 1ee48bc60 (post-17.0.0-rc.1), driving a real showcase boot plus purpose-built probe stacks as two live principals.
This issue exists so the RC train has one place to see the damage and decide what blocks exit. Each item below is independently reproducible — the linked issue carries the exact requests and responses.
(Correction to the run-4 comment on #3909, which said "13 defects" — the count is 17. The grouped list there was right; the total was a miscount.)
1 — Security & data integrity (8)
These change who can read or write what, or leave the audit record untrue.
- analytics: /analytics/query ignores record-level scoping — a member counts and reads dimension values of records they cannot read #4467 —
/analytics/queryignores record-level scoping. A member with shares on 2 of 5 private notes counts 5, anddimensions:["title"]returns the titles of the 3 they cannot read. Any authenticated caller who reaches/analyticscan enumerate field values of every row of any cube-exposed object, past OWD, sharing rules and RLS. The most serious finding of the verification. - sharing: deactivating a rule never withdraws its materialized grants — not on touch, not at boot #4433 — A sharing rule's materialized grants are never withdrawn. Deactivating the rule does not revoke them — not on the next record touch, not after a full restart.
- sharing: DELETE /sharing/rules/:idOrName answers 500 for both address forms — rules cannot be deleted over REST #4434 —
DELETE /sharing/rules/:idOrName500s for both address forms, so the rule cannot be deleted either. Compounds sharing: deactivating a rule never withdraws its materialized grants — not on touch, not at boot #4433: an over-granting rule is unrecoverable from the API surface. - data: created_at is client-writable on an ordinary PATCH — the audit anchor can be forged silently #4447 —
created_atis client-writable on an ordinary PATCH and persists.updated_atandcreated_byare correctly protected; the audit anchor is the one that is not. - data: a lookup accepts an id that does not exist in the referenced object — including the RBAC permission-set link tables #4441 — A
lookupaccepts an id that exists nowhere, includingsys_position_permission_set.permission_set_id— a grant row can point at nothing while the audience-anchor gate has to resolve that set to judge it. - datasource: an object mapped to an unreachable datasource silently reads/writes the DEFAULT store — no boot failure, /ready still 200 #4462 — An object mapped to an unreachable datasource silently reads/writes the DEFAULT store. Boot succeeds, the datasource name never appears in the log,
/readystays 200, and the data lands in a different database than the one declared. - screen flows: resume performs no server-side validation — missing required fields and undeclared keys all complete the run #4477 — Screen-flow
resumeperforms no server-side validation. Missing required fields, and undeclared keys, all complete the run. A client posting straight toresumebypasses everyrequiredthe flow author declared. - approvals: an admin override of a staffed approver slate is recorded as an ordinary approval — no override marker anywhere #4466 — An admin override of a staffed approver slate is recorded as an ordinary approval.
sys_approval_actionhas no override column, so bypassing designated approvers is byte-identical to being one.
2 — Contract & envelope (6)
Declared contracts that do not hold, or refusals that break the ADR-0112 envelope.
- actions: sandbox capability denial answers 400 as a deliberate rejection, not the 500 the crash contract promises #4431 — A sandbox capability denial answers 400 as a deliberate rejection; the crash contract (fix(actions): an action that CRASHED is a 500, not a 200 reporting success:false (#3913 follow-up) #3951) says 500. The
SandboxError:debug prefix also reaches the client. - data: PATCH/DELETE of a nonexistent record answer 200 success instead of RECORD_NOT_FOUND #4435 — PATCH/DELETE of a nonexistent record answer 200 success (
record: null/success: true) where GET correctly 404s.deleteManyreports every typo'd id as deleted. - data: unsupported-filter-operator refusal ships without error.code and leaks the [sql-driver] prefix #4436 — The unsupported-filter-operator refusal ships without
error.codeand leaks the[sql-driver]prefix. - analytics: a measure naming a missing field 500s with SQLITE_ERROR instead of a 400 naming the field #4437 — An analytics measure naming a missing field 500s with
SQLITE_ERRORinstead of a 400 naming the field. - meta overlays: unnormalized type segment creates phantom rows that shadow the code-authored listing and cannot be deleted #4432 — Meta overlay writes do not normalize the type segment: plural and singular land in different namespaces, one overlay row shadows the entire code-authored listing, and the row cannot be deleted by either spelling.
- migrate value-shapes: a lookup holding an embedded record object passes the scan — the exact case the ADR-0104 D1 gate exists to find #4455 —
os migrate value-shapesmisses alookupholding an embedded record object — the exact case its own docstring names — because the reference shape isz.string().min(1). The gate then closes on evidence it never gathered.
3 — UI & docs (3)
- Setup System Overview: every KPI tile reads 0 — the dataset path lowers an all-time range to WHERE created_at = $1 #4475 — Every Setup System Overview KPI tile reads 0 (Users 0 vs 4, Sessions 0 vs 16). The dataset path lowers an all-time range to
WHERE created_at = $1. First screen an operator sees. - Console approvals: quorum progress and per-group chips do not render, though the server sends minApprovals / group / __nodeLabel #4478 — Quorum progress and per-group chips do not render, though the server sends
minApprovals, per-approvergroup, and a pre-rendered"Committee Sign-off (2 of 3)". Fix belongs in objectui docs(auth): 手机号登录、管理员用户管理与批量导入的文档更新 #2811; no framework change needed. - docs: nine passages date the #4286 query-surface removals to spec 18 — they ship in 17 #4476 — Nine doc passages date the [P2] data:
QueryASTdeclares 12 members no executor runs — the liveness ledger governs metadata types, not the request surface #4286 query-surface removals to spec 18 — they ship in 17. A reader on 16 plans the break a major too late.
Suggested triage for RC exit
Should block: #4467 (aggregate disclosure past every access control), and the #4433 + #4434 pair (an over-granting sharing rule with no in-product way to revoke it). Both are security properties the v17 release page actively advertises as fixed.
Strong candidates: #4447 and #4477 — both let a caller do something the declared contract forbids, and both are one-line-of-defence gaps rather than degraded UX. #4462 lands data in the wrong database silently.
Can ship with, worth fixing soon: the envelope group (#4431, #4435, #4436, #4437) — wrong status codes and missing error.code, all mechanical. #4432 and #4455 are narrower but both let a surface report success it did not achieve.
Docs/UI: #4476 is cheap and should land before RC exit because it misinforms upgraders. #4475 and #4478 are visible but non-destructive.
Two things that are not defects, listed so they are not re-litigated
/graphqldiscovery slot. The route correctly 404s, but discovery still reportsservices.graphqlas an unavailable slot whose message invites someone to implement it — for a surface v17 deliberately removed, and contradicting the docs sweep's own reduction to 16 services. Whether to drop the slot or reword the remedy is a design call, so no issue was filed. Raised here for a decision.- Two stale checklist claims on v17 verification tracker — browser-verify every user-facing change in the 17.0.0 train #3909. The seeds item promises a
Seeds: X inserted · Y updated · Z skippedbanner that does not exist (the code deliberately collapses those into one count and uses·to separate sources); theos migrate metaitem's "rewrites all three" reads as an in-place codemod, but it rewrites the stack snapshot (--out), not TypeScript sources. Both are checklist-text errors, not code defects.
Tracker: #3909 — 48 of 59 items verified; the 11 remaining are annotated inline with the issue blocking each.
Activity
os-zhuang commentedon Aug 1, 2026
认领与排期
这 17 项已全部认领给 @os-zhuang,按子系统拆成 7 条分支并行推进,每条一个草稿 PR,独立过 CI、独立评审、独立合并——一条红了不阻塞其余六条。
-analytics-sharing-data-envelope-datasource-flow-approval-docs分支前缀统一为
claude/v17-verification-defects-gnf9e6。三处与本 rollup 之外的今日新 issue 合并同卡,因为它们与卡内缺陷同属一条代码路径:#4475 归入 analytics(同为 dataset 查询构造)、#4449 归入 #4455(同形:闸门在自己从未采集到的证据上关闭)、#4470/#4469 归入 approvals/resume 子系统。#4485 先修,避免它的绝对 50ms 墙钟断言把后面几条 PR 无端染红。
每张卡的验收都要求真实启动复现:按 issue 原文的请求打一遍,确认从坏结果变成对的结果,而不是只看单测转绿。
RC 退出的三个阻塞项(#4467、#4433+#4434)在第一波。
Generated by Claude Code
os-zhuang commentedon Aug 2, 2026
17 项全部修复并合入
main顺带修复:#4483
$search主键泄漏 · #4485 ReDoS 断言负载敏感 · #4486 IDataEngine 文档 · #4452 flow DSL README · #4449 未接线的 lint 规则 · #4469 僵尸审批请求 · #4470 验证 harness 绕开持久化。「两件不是缺陷」的处置
/graphql发现槽 —— 已在 #4451 解决,无需决策。 本 issue 记录该观察时基于main @ 1ee48bc60;#4451(经 #4473 合入)已移除CORE_SERVICE_PROVIDER的graphql条目与 metadata-protocol discovery 表的graphql: { route: '/graphql' },原地留下说明性墓碑注释。当前main上graphql仅剩两类出现:external-lookup.zod.ts的协议类型枚举(连接外部 GraphQL API,与是否提供该面无关),以及记录本次移除的注释。发现面不再报告该槽位。两条陈旧的清单声明 —— 属 #3909 的文本错误,未在本轮改动。
三处 issue 自身的归因不成立,已在对应 PR 更正
修复过程中实测推翻了三条原始归因。照原文修改会改错位置:
AUDIT_FIELD_DEFS.created_at一直带readonly: true,只是从未生效——构建产物中的物化字段遮蔽了引擎定义。四个缺陷曾带绿灯发布,根因同属测试替身漂移
FK enforcement is off in this harness,依赖着平台要禁止的缺陷已单独立项跟踪该模式(见下方新立 issue)。
本轮新立的 issue(均未认领)
#4513
/meta/objects读构建产物而非注册表,与实际强制行为公开矛盾 · #4515 重启后挂起 run 取不到 screen(修复涉及协议破坏性变更)· #4518bootStack({ databaseFile })数据不跨进程内重启 · #4521 overlay 保存与派发解析间的缓存滞后 · objectui#3151 非预设日期字符串静默降级 · objectui#3152 被取消的运行发出指标为空的 FAIL 评论仍需维护者决策
isSystem调用方仍可写入悬空引用,系种子排序豁免的代价,已记录未静默接受。/ready转 503」,并据此推翻 ADR-0062 D2 的 phase-1 note(ADR 内已附修订说明)。本 rollup 的 17 项已全部关闭,随此评论一并关闭。
Generated by Claude Code
fix(ci): an already-closed cross-repo issue gets the PR backlink inst…
fix(ci): an already-closed cross-repo issue gets the PR backlink inst…