Repository navigation
[Decision] §09 要「各段时长」和「超 SLA」,但语义层算不出时间差——两问共用一个补法 #31
Description
Ruled: 6396322580 · letter C · 2026-10-25T10:07Z (C = keep the goal, platform card for a datetime-difference measure; 1C + 2B of 2026-09-09 stand)
Blocked-by: objectstack-ai/objectstack#26601 (per-type over-SLA: the $now offset across contract_type; #25778 shipped in 18.2.0 and the durations landed as PR #168)
⬆️ 2026-10-25 第 3 批裁定 C:等待对象从 objectstack#16737(已关闭,修法是拒绝而非提供)换成 objectstack#25778(分析层提供「两个 datetime 列之差」的度量原语)。解锁判据不变:消费方可安装,且本仓能写出 §09 的那两个度量。
⬆️ 裁定 1C(维护者,2026-09-09,第 1 批):阻塞源是平台缺陷 ⇒ 去平台修。解锁判据是消费方可安装(本仓升到含该修复的版本后那条错路确实报错),⛔ 不是「上游 PR 已合并」。详见本卡 2026-09-09 的裁定评论,含一条对 1C 的公开更正:平台在飞的 PR #16778 选的补法是拒绝而非提供,所以修完之后各段时长仍然算不出来。
维护者速读
事情:设计方案第 9 章要两样东西,卡 10(#29 / PR #30)照着做时发现平台这一版给不了:
- 平均周转 / 各段时长——需要「两个时间戳相减」。语义层不收 SQL 也不收表达式(ADR-0021),没有地方做减法。
- 超 SLA——需要拿合同的
review_started_at去比另一个对象的列(clm_contract_type.review_sla_days,九种类型分别是 2/3/5/10 天)。分析层既不能按公式过滤(§12 缺口 [Decision] Two states inDESIGN.md§03 are dead ends: a started obligation cannot go overdue, a partly paid instalment cannot be completed #10),跨对象过滤也被直接拒绝。
两问的补法是同一个:让日任务把结果盖成合同上的持久字段,之后分析层直接读那个数就行。这正是 §12 缺口 #7 已经为另一个日期运算问题选定的形状(「到期、逾期由日任务盖戳字段」),所以不是新机制。
为什么必须你拍板:字段叫什么、语义是什么,是公开契约;而且它落在卡 09(日任务那张卡)的地盘上,不是卡 10 能自己加的。
你要做的:回两个字母,比如「1A 2A」。
第一问 —— 各段时长
这里最值得你看一眼的,是它差点怎么错
开发 agent 先按直觉写了 derived: { op: 'difference', of: [avg_activated, avg_submitted] }。它不报错。它解析通过、运行成功、渲染出一个数:-0.849999999999909。
原因实测如下:
sqlite> select typeof(submitted_at), submitted_at from clm_contract limit 1;
text|2026-05-19T00:00:00.000Z
sqlite> select avg(submitted_at) from clm_contract;
2025.9166666666667 ← 平均「年份」,不是平均日期
Field.datetime 在 SQLite 上存的是 ISO 文本,AVG() 触发 SQLite 的文本→数字强制转换,取到开头的年。于是那个 -0.85 是两个平均年份之差——一个干净、合理、可以直接进仪表盘的错数。
这就是本来会发布出去的形态。 卡 10 因此改为交付「各阶段覆盖数」(已提交 60 · 已开始审查 41 · 已批准 34 · 已签署 72 · 已生效 72)和一块标明为「审批吞吐」的砖,§09 要的「本月 vs 上月」对比则用平台自己的 compareTo: { kind: 'previousPeriod' } 原语实现,没有硬编码差值。
选项
| 做法 | 代价 / 收益 | |
|---|---|---|
| 1A(推荐) | 在 clm_contract 上加持久的时长字段,由日任务盖戳——与 §12 缺口 #7 同形 |
需要 §03 加字段 + 卡 09 加一次盖戳;字段名与语义要你定。收益:§09 字面兑现,且时长可被分析层过滤与排序 |
| 1B | 接受卡 10 的替代物,改 §09 的措辞为「阶段覆盖与吞吐」 | 零工作量,今天的板子如实。但让出一项真实的分析能力,且要改 §09 |
| 1C | 向平台提需求(datediff 类度量,或 Field.datetime 改存数值时间戳),在它落地前维持替代物 |
一次修好所有应用;但时间不可控,期间两块砖一直是近似 |
| 1D | 砍掉 contract_cycle_time 数据集 |
⛔ §09 点名四个数据集,静默砍掉一个正是卡片明令禁止的。列出仅为明确否决 |
第二问 —— 超 SLA
卡 10 交付的是固定 30 天阈值,并把砖的标题写成「审查超 30 天」而不是「超 SLA」——因为九种类型的 SLA 是 2/3/5/10 天,30 天高于全部,且标题自己说明了阈值,读者不会误以为它是按类型的违约。
| 做法 | 代价 / 收益 | |
|---|---|---|
| 2A(推荐) | 并入 1A:同一个日任务顺手按类型 SLA 盖一个 review_due_at(或违约布尔),砖改为过滤持久列 |
一个任务、一次写入,两块砖同时不再是近似 |
| 2B | 保留固定阈值,改 §09 措辞 | 零工作量,但「超 SLA」这项能力就没有了 |
| 2C | 固定阈值作为过渡,§09 保持目标,等卡 09 落地再回头 | 拖延,且过渡期无期限 |
四棱分析(两问共用)
实际业务需求 — 偏 A。「合同平均要走多久」和「哪些审查压过了 SLA」是 CLM 买家评估流程效率时最先问的两个问题,也是对标 Ironclad/Agiloft 时销售会被追问的两个数。1B/2B 等于承认这个产品答不出来。
项目长远合理性 — 明确偏 A,理由是一致性:这个设计对完全相同的问题(日期运算算不了)已经在 §12 缺口 #7 选过一次答案,就是「日任务盖戳字段」。同一个问题给两种答案,是下一个作者最容易踩空的地方。而且持久数值列可过滤、可排序、可聚合——这正是 clm_contract 那五个汇总字段被做成 summary 而不是公式的同一个理由。
防 AI 写错 — 这一棱最偏 A,而且差距最大。今天的状态是:语义层里存在一条能跑通、能渲染、结果错的路径(op: 'difference' 那个 -0.85)。只要这条路还开着,下一个作者——人或 AI——照直觉写就会写出它,而且没有任何闸门会拦。1A 把正确答案变成一个现成的持久字段,等于把那条错路的诱因移走。1B 反而更危险:它把「这里算不出时长」写进文档,却不改变「随手一写就能得到一个假时长」这个事实。
创业阶段不扩散 — 略偏 B。1A 要加字段、改 §03、给卡 09 加活。但卡 09 本来就要建一个跑在这些对象上的日任务,所以边际成本是「一个字段加一次盖戳」,不是一套新机制。2A 更是搭同一趟车。
结论:三棱同向 A,第四棱的成本因为卡 09 已有日任务而被大幅摊薄。推荐 1A 2A。 若你更想省,1B 2B 也自洽,但必须写进 §09——沉默不可接受,理由同 #10:把矛盾留给下游卡片,就是把它推给一个更没权限、也更有压力悄悄编一个答案的位置。
⛔ 字段名与语义我没有替你定,那是公开契约。
影响
不阻塞。PR #30 本身不声称任何时长,可以按现状合并(正在做第一轮 rework,原因与本卡无关)。这两问的落地在卡 09——那张卡同时还等着 #6、#10、#14 三张决策卡,所以一并回答可以让卡 09 一次派发到位。
关联
#29 / PR #30(来源)· 卡 09(落地处,尚未派发)· #6 #10 #14(同样卡住卡 09 的三张)· DESIGN.md §09、§12 缺口 #7 与 #10。
Activity
Four datasets and three dashboards (#30)
zhuangjianguo commentedon Sep 9, 2026
前提刷新 —— 平台侧根因已有卡,且在等维护者动作。 ⛔ 不是裁决,不动标签。
本卡第一问的实测(
avg(submitted_at)返回2025.9166666666667,derived: { op: 'difference' }渲染出-0.849999999999909)在上游已经立案:objectstack-ai/objectstack#16737— service-analytics:AVG()over aField.datetimemeasure returns SQLite's text→numeric coercion (an average YEAR) with no error, andderived: { op: 'difference' }renders the difference of two of them as a clean plausible number —— open,且带pm:awaiting-maintainer(决定已做,只剩一次 GitHub 之外的人工动作)。同族另有两张 open:#16099(无任何层拒绝不自洽的聚合/字段类型配对,
avgover datetime 在 SQLite 能跑、在 Postgres 报错)与 #15768(datetime 度量在分析响应里被定型为number)。这对本卡的意义:选项 1C(向平台提需求,等它落地前维持替代物)在写这张卡时是「时间不可控」的假设面,现在它有具体载体和状态了。⇒ 裁本卡之前应先读 #16737 的现状 —— 如果平台给出日期差度量或修掉静默强制转换,1A 的「日任务盖戳字段」就不是唯一出路,1C 的成本估计也要重算。
另:本卡与 #11 #19 #20 #28 同属前提取自
@objectstack/*17.3.0 的一批。#35 已把本仓推到 17.4.0 并于今日合并(PR #37,origin/main@5d5a254,锁文件零 17.3.0 残留)。⇒ 这一批的平台能力断言现在都该在 17.4 上重跑再定,本卡的语义层限制(ADR-0021 不收 SQL/表达式、跨对象过滤被拒)尤其需要重验。取数时刻 2026-09-09T14:5xZ。
Generated by Claude Code
zhuangjianguo commentedon Sep 9, 2026
裁定:1C + 2B —— 去平台修;本仓维持替代物并把措辞改成如实
维护者裁决,2026-09-09,常设决裁批第 1 批,逐字:
本卡的阻塞源是平台缺陷(语义层不做日期减法;
AVG()打在 datetime 上返回平均年份)⇒ 走「去平台修」。这也与决策分析写法的应用仓推荐序特例一致:阻塞源是平台缺陷 ⇒ 恒荐等待;⛔ 不荐绕行(形状容错、复刻平台规则)、不荐劈半落地。⇒ 1C(等平台)+ 2B(保留固定阈值,措辞如实)。⛔ 因此 1A / 2A 明确不采纳:用每日任务在应用里补出分析层该有的日期运算,正是该特例点名的「复刻平台规则」。
呈报时我把 1C 写成「一次修好所有应用」。录裁前重读平台侧,这句话不成立:
objectstack-ai/objectstack#16737— open,bug·priority:p2·domain:services·pm:awaiting-maintainer,assigneeos-trump。objectstack-ai/objectstack#16778,标题逐字:⇒ 平台选定的补法是拒绝,不是提供。#16778 合并后,本仓依然算不出各段时长 —— 变化的是:那条「能跑通、能渲染、结果错」的路径会开始响亮报错,而不是给出
-0.849999999999909这种可信的假数。洞被堵上了,能力没拿到。 这对本卡的意义:
⛔ 我不静默重裁。裁定按字面执行为 1C + 2B。但若你看了上面这段觉得「等一个只会报错的修复」不是你要的意思,说一句,本卡回决策箱重开选项(那时真正的选项是:向平台立一张日期差度量原语的能力卡,还是接受第 9 章永久少这两个数)。
裁后执行
needs-user-decision→pm:blocked,正文加机器可读行Blocked-by: objectstack-ai/objectstack#16737(同一笔标签写入)。DESIGN.md§03 requires a termination reason but declares no field for it #6,本卡为Refs #31的搭车项,⛔ 非Fixes):第 9 章不再无条件承诺「各段时长」与「超 SLA」,改为记明现状 —— 交付的是阶段覆盖数与审批吞吐,两个原指标等平台的日期能力,并点名 #16737。沉默地留着一个兑现不了的承诺,正是本卡自己反对的形状。Generated by Claude Code
docs(design): record four maintainer rulings in DESIGN.md §03, §06 an…
docs(design): make §09 self-consistent and stop §02 promising turnaround
WIP: card 09 scheduled jobs — INCOMPLETE AND UNREVIEWED
Card 09 — F9 activation · F3/F4/F10–F14 scheduled jobs · F16 executed…
docs: reframe five DESIGN.md 09 comments as agreement, not shortfall
zhuangjianguo commentedon Sep 10, 2026
席位裁定 —— 解锁判据未满足,
pm:blocked保持#80 在本仓实际安装的版本上重跑了这张卡的探针。判据没有满足,而且这张卡的措辞刚好证明了它自己的价值。
实测(17.4.0,54 个
@objectstack/*包全部 17.4.0,better-sqlite313.0.3,从node_modules读出)那条错路三层都不拦:
validate/lint/typecheck全部退出 0,derived原样编进dist/objectstack.jsonPOST /api/v1/analytics/dataset/query→ HTTP 200,回显AVG(submitted_at)cycle_days = -0.8555555555556111,avg_submitted = 2025.8833333333334操作员看到的是两块普通指标砖:
-0.86 / Avg Cycle (days?)和2,025.88 / Avg Submitted—— 没有报错横幅、没有空态、没有校验提示、控制台无错。这是本卡列出的三种结局里的第三种,也是本卡认定最危险的那种:它照样撒谎。 数值与本卡原记录的
-0.849999999999909不同,只因演示数据的日期随启动日漂移;机制与行数(120 行 / 60 已提交 / 72 已生效)完全一致。⇒ 本卡「⛔ 不得用应用侧日任务盖戳」与 §09 已记录的交付面全部不变。
pm:blocked保持,⛔ 不进决策箱,⛔ 不需要维护者现在做任何事。这是本轮最该记下的一件事:
objectstack-ai/objectstack#16737已于 2026-09-10 06:13Z 关闭(completed)。@objectstack/service-analytics的latest就是 17.4.0 —— npm 上 17.x 之后没有更新的发布版。而 17.4.0 自己的
CHANGELOG.md白纸黑字写着这个洞是开的(我自己拉的包,逐字核过):这正是本卡当初把判据写成「消费方可安装」而不是「上游 PR 已合并」的理由。 那句 ⛔ 今天兑现了:上游卡关了,本仓装的版本行为一个字节没变。判据措辞是对的,⛔ 不要改它。
一条上游发版说明里的新事实,比本卡原有的更细
洞是按后端分岔的:SQLite 上是 epoch 均值(静默假数),Postgres 上是报错。 也就是说这个陷阱是演示环境(SQLite)专属的 —— 真正跑在 Postgres 上的部署会直接报错。本卡原文只写了「⛔ 未在第二种方言上复核」,现在平台自己的发版说明把答案写出来了。这对将来落地 1A/2A 时怎么描述这个字段有用,⛔ 但不改变今天的裁定。
17.4.0 确实发了这一族的两个兄弟修复(min/max 的类型描述),但没发「拒绝」那一半:装好的包里搜不到
16737也搜不到16778。skim 一眼 temporal 相关的发版说明,很容易把兄弟当成本体。 下一次读 #16737 状态的人请先看latest的实际行为,不要只看 issue 是不是 closed。已做
上游 #16737 上补了一条消费方读数(
latest上行为未变 + 它自己 CHANGELOG 的原话),⛔ 措辞是「请问哪个发布版会带上」,不是「你关错了」—— 修复很可能已在main上,只是还没发版。附:本卡引用的 17.3.0 测量已刷新
src/datasets/cycle-time.dataset.ts注释里那段标着 17.3.0 的测量,现在有了 17.4.0 的同形读数:形状相同、数字更新、结论不变。⛔ 那条注释本身不改(#80 明令禁止),刷新后的读数落在 #80 的报告里。顺带一提:正因为那条注释老老实实标了版本号,这个缺口才看得见 —— 一条不标版本的测量会一直被当成当前事实。Generated by Claude Code
objectstack-fleet commentedon Oct 9, 2026
Seat note · 2026-10-09T14:41Z — waiting state, written on the card.
-0.86with no refusal at any layer.objectstack-ai/objectstack#16737closed completed 2026-09-10, after that reading. Closure is not the criterion; this card's own criterion is that the wrong path errors in this repo, on the version it has installed.@objectstack/*dependencies from 17.4.0 to 17.7.0, walking the official upgrade checklists of 17.5, 17.6 and 17.7 #82 (@objectstack/*17.4.0 → 17.7.0, in flight since 2026-10-09). Its dispatch carries Re-run #31's unlock probe on 17.4.0 — its criterion may already be met and nobody has looked #80's probe, re-run on 17.7.0, as a report-only increment. When that reading lands, the seat decides this card's state from it.Generated by Claude Code
objectstack-fleet commentedon Oct 9, 2026
Seat note · 2026-10-09T16:08Z — the 17.7.0 reading exists.
#82's dev re-ran #80's probe on 17.7.0 (PR #85, section #31's unlock probe on 17.7.0): the wrong path now errors at every door —
os validate/os buildexit 1 (measure-aggregate-field-type-refused×2),PUT /api/v1/meta/dataset/probe31_cycle→422 INVALID_METADATAwith nothing stored,POST /api/v1/analytics/queryavg(submitted_at)→400 INVALID_FIELD(query was NOT run). Probe never committed; tree restored and proved by state.This card's criterion is 「本仓升到含该修复的版本后那条错路确实报错」 — it is met on the branch, and becomes this repo's state when PR #85 merges (
mainis still on 17.4.0 until then). At that merge the seat brings this card back to the maintainer: 1C's wait is over, and as this card's own correction recorded, the platform fixed by refusing, not by providing — the per-stage durations remain uncomputable on 17.7.0.Generated by Claude Code
objectstack-fleet commentedon Oct 10, 2026
解锁判据已成立——请决定本卡怎么收尾 · 2026-10-10T00:17Z
Governing text: 本卡裁定 1C(维护者,2026-09-09,第 1 批)「阻塞源是平台缺陷 ⇒ 去平台修。解锁判据是消费方可安装(本仓升到含该修复的版本后那条错路确实报错)」·
DESIGN.md§09维护者速读
事情:PR #85 已合并(
c31c7e2),本仓main现在是 17.7.0。上一条评论里的探针在 17.7.0 上测得:那条错路(对Field.datetime求avg再相减)在每一道门都报错——作者时os validate拒绝、保存门422、查询门400。本卡自己的解锁判据成立了,「渲染出一个干净的错数」这个风险已经消失。但要知道:平台的修法是拒绝而不是提供(本卡 2026-09-09 的更正里已说明),所以 §09 原本想要的「各段时长 / 超 SLA」在 17.7.0 上仍然算不出来。现在的替代物(阶段覆盖数、审批吞吐、固定 30 天阈值且标题写明阈值)照 1C + 2B 继续有效。
你要做的:回一个字母。
四棱分析
Generated by Claude Code
6 remaining items
objectstack-fleet commentedon Oct 27, 2026
Unblocked — 2026-10-27
closedd1663bais an ancestor of tag@objectstack/spec@18.2.0mainnow pins@objectstack/*18.2.0 (PR #155, merge21f2319, push CIValidatesuccess), so the fix is installable in this repo. The ruling recorded in the body stands; this card returns to the queue for dispatch. The dev re-measures the premise onmainbefore writing code.State:
pm:blocked→pm:queue.Generated by Claude Code
objectstack-fleet commentedon Oct 27, 2026
Claim: PM loop round 17
Ruling-ref: 6396322580
Session:
session_017nzWFRUo2U7evMkPdVniAnAccount:
zhuangjianguo(the seat's linked user asGET /useranswers it; the card's assignee)Branch:
claude/issue-31-cycle-time-measuresWorktree:
hotclm-issue-31Domain:
repo:hotclm(single-lane sister repo — nodomain:*)Seat:
repo:hotclm#1File surface:
src/datasets/**·src/dashboards/**· their translations — the two §09 measures (stage durations, over-SLA) on the 18.2.0 datetime-difference primitive; governed §09 text rides the seat's design-doc PR #120 — stop on breach; explain in the reportContainer & model:
S,mode:subagent,model: opus(TIER_DEFAULT; hand-derived for a sister repo),effort: mediumClause-②: no
Metadata-protocol: none
Responsibility:
this app's analytics (how long a contract takes, which ones broke SLA) | objectstack-ai/objectstack#25778 shipped in 18.2.0 | every legal head and admin reading the CLM dashboardsThread-read: 6437400862
Premise: re-measured by the dev on
origin/mainbc5c3e8(@objectstack 18.2.0) with README personas firstSerial constraints cleared: Unblocked 2026-10-27 (18.2.0 on
main); in flight #127 (profiles), #158 (lifecycle actions) — disjoint files.Generated by Claude Code
objectstack-fleet commentedon Oct 27, 2026
os-dev-report
Written 2026-10-27T09:49Z · PR #168 · head cfb92e3 · branch claude/issue-31-cycle-time-measures
{ "issue": 31, "status": "done", "branch": "claude/issue-31-cycle-time-measures", "pr": "https://github.com/objectstack-ai/hotclm/pull/168", "head": "cfb92e3", "session": "session_017nzWFRUo2U7evMkPdVniAn (parent seat's; this run is an os-dev subagent)", "premise_still_valid": true, "summary": "Half delivered, half re-measured as still blocked; body line 1 is 'Part of #31'. (1) Stage durations: 18.2.0's `duration` measure (objectstack#25778, d1663ba, an ancestor of tag @objectstack/spec@18.2.0, exit 0) now powers contract_cycle_time. It carries 5 avg-days measures: submitted→review started, review started→approved, approved→signed, signed→active, submitted→approved. They replace the 5 stage-coverage counts. On the legal workbench, approval_throughput (a count) becomes approval_turnaround (avg days submitted→approved, this month, compareTo previousPeriod), and a new row of 4 per-stage tiles is added. Per 1C, the stand-ins are replaced once the measure exists. (2) Per-type over-SLA: still NOT expressible. 18.2.0's $now anchor (objectstack#25808, ruling A) reads its addDays offset from a SAME-record column only. `addDays: { $field: 'contract_type.review_sla_days' }` answers 400 INVALID_FILTER at both doors, while `os validate` passes it. The fixed 30-day tile stays, per 2B. No app-side SLA column was stamped or mirrored, because the ruling forbids it. Also: the executive-board comment that said durations are inexpressible is corrected; the turnaround tile's description says 'equal-length period just before it' (measured: October was compared with [08-31, 09-30]), not 'last month'; and its success colour is dropped.", "tests": "Independent SQL on the throwaway DB (SQLite julianday(to)-julianday(from), rows with both stamps) against Chromium zh-CN dashboard reads, signed in as README personas created on 18.2.0: Legal Head (create-user + sys_user_position clm_legal_head + change-password) and Business Requester 1 (create-user + set-user-manager + change-password; pnpm demo:zh re-run hands over 43 contracts). Legal Head: SQL 2.0/49.0/8.0/3.0 (n=35/17/24/72) matches rendered 2.0/49.0/8.0/3.0; turnaround SQL 65.14 (n=7) vs compare 53.17 (n=6) matches rendered 65.1 ↑23%. Requester 1: SQL 2.0/59.5/8.0/3.0 matches rendered 2.0/59.5/8.0/3.0; turnaround 76.0 vs 47.0 matches rendered 76.0 ↑62%. Units are days: the service SQL is AVG(((ROUND((julianday(b)-julianday(a))*86400000.0)/1000.0)/86400.0)). No -0.85 artefact: the old avg-stamp + derived-difference spelling answers 400 DATASET_INVALID (raw SQL on this DB would have given -0.786). Native-SQL strategy served both personas, with the sharing scope in the same statement. category=nda filter: 75.5 = SQL 75.5 (n=4). By contract type: every row equals SQL. Monthly bucket (dimension approved_month): 400 INVALID_FIELD naming the measure. Baseline on origin/main bc5c3e8, as Legal Head: 6/12/6/2, 本月已批准 7 ↑17%. Console before and after: the same one 404 resource and one 401 get-session pre-login. Boot: no degraded/no-such-table/failed plugin.", "gates": "Head cfb92e3, each exit captured before any pipe: pnpm install EXIT=0; pnpm validate EXIT=0 (✓ Validation passed); pnpm lint EXIT=0 (6 suggestions, 0 warnings; origin/main bc5c3e8 measured identically, 6 suggestions and 0 warnings, so the #64 warning common.md expects is no longer on main and no warning was added); pnpm typecheck EXIT=0; pnpm lint:i18n-gate EXIT=0 (COVERAGE: 0 missing keys across 2 locales); CLM_DEMO_SEED=1 pnpm validate EXIT=0. CI check-runs on PR #168: not read (report delivered at local verification end; CI convergence is the seat's).", "dogfood_battery": "n/a — app repo, no `!` title, no Clause-② line", "mcp_calls": "0", "api_writes": "2 — fleet-write relay: pr_create objectstack-ai/hotclm#168 (read-back identical, 8871 bytes, assignee zhuangjianguo); this os-dev-report comment on hotclm#31 via post-stamped. git push x2 (not REST).", "open_questions": [ { "question": "The per-type over-SLA half of #31 has no platform card that will deliver it. objectstack#25808 closed with ruling A (same-record offset only) and explicitly did not take B′ (a one-hop path offset). What should #31 be blocked by now?", "options": [ "A: the seat files an objectstack card for a now-anchored comparand whose addDays offset reads a declared to-one path (B′). This amends the 2026-08-06 or 2026-08-12 platform ruling, so it is a platform maintainer decision. #31 stays open, Blocked-by that card, with the 30-day tile kept (2B).", "B: rule 2B permanent. §09 states that the per-type over-SLA is not in the product, and #31 closes when PR #168 merges (Fixes on a follow-up body edit by the seat).", "C: a per-contract negative SLA-days column stamped by the app, so the same-record anchor can read it. ⛔ Listed only to reject: §09 and the 2026-09-09 ruling forbid app-side stamping, and ruling 6396322580 reaffirmed it." ], "recommendation": "A, because ruling 6396322580 (C) chose to keep the goal and file the capability rather than make a platform gap a permanent product boundary, and the same reasoning applies to the half #25808 did not cover. The card's cost line must say it reopens B′." } ], "out_of_scope_findings": [ "class: c · reach: `pnpm validate` (os validate 18.2.0) exit 0 for a dataset measure filter `review_started_at: { $lt: { $now: true, addDays: { $field: 'contract_type.review_sla_days' } } }` on a dataset with include ['contract_type']. At runtime the same spelling answers 400 INVALID_FILTER at POST /api/v1/analytics/dataset/query as a runtime filter (\"the addDays offset … is a dotted path, and SQL push-down compiles same-table column references only\") and as a measure filter (reason withheld). An AI that writes the per-type SLA count gets green gates and a tile that always errors. Seam: spec:NowAnchorSchema.addDays.$field (packages/spec/src/data/filter.zod.ts) → runtime: driver-sql crossFieldOffsetExpr same-table rule; lint: no rule judges the offset path at author time · dedupe words: \"$now addDays dotted path validate\", \"now anchor offset relationship path lint\", \"measure filter cross-object offset author time\", \"INVALID_FILTER addDays dotted\"", "class: a · reach: browser, Chromium zh-CN as Legal Head on the hotclm legal workbench: the turnaround tile reads \"↑ 23% 较上期\" drawn emerald for a turnaround that got 23% LONGER (worse). objectui packages/plugin-dashboard/src/DatasetWidget.tsx:1325 colours delta.direction 'up' emerald for every measure, and the spec has no key to declare lower-is-better on a metric tile's compareTo. Seam: spec:DashboardWidget.compareTo (no polarity) → renderer: objectui DatasetWidget compare-trend span · dedupe words: \"compareTo delta colour lower is better\", \"metric trend arrow polarity duration\", \"previousPeriod delta direction good bad\"", "carrier: #156 (src/flows this round) · noted, not filed: src/flows/legal-review-sla.flow.ts and renewal-notice.flow.ts still name objectstack#16737 as the tracker for the per-type SLA and durations. The durations now exist, and the SLA's tracker is the open question above.", "carrier: none · noted, not filed: one 404 resource appears in the browser console on the legal workbench both before and after this change, and was not identified (not captured by the page's response listener)." ], "governed_text_proposal": { "DESIGN.md §02 table row Insights (line 80)": "| Insights | 周转分析 | 阶段时间戳 → dataset(平台 `duration` 度量)→ 看板:各段平均时长与平均审批周转;按类型的「超 SLA」等平台跨对象比较(§09) |", "DESIGN.md §09 dataset bullet": "- `contract_cycle_time` — `submitted_at → review_started_at → approved_at → signed_at → activated_at` 各段平均时长,外加提交 → 批准(平均审批周转);单位天,平台 `duration` 度量(`objectstack-ai/objectstack#25778`,18.2.0);按类型与法务经办。每个平均值只计入两个时间戳都已记录的合同;不出按月趋势(平台拒绝带粒度分桶的时长度量)", "DESIGN.md §09 法务工作台 bullet": "- **法务工作台** — 待受理数 · 审查中 · 审查超 30 天(固定阈值,写进砖的标题)· 谈判停滞 · 平均审批周转(提交 → 批准的平均天数,本月对比紧挨在前的等长时段,用平台 `compareTo: previousPeriod`)· 各阶段合同数(横向条形图,按合同数从多到少排;只画在办的六个阶段)· 各段时长(四块砖,每段一块,单位天)", "DESIGN.md §09 paragraph replacing line 308's first half (the 16737 / 25778 pointer)": "**各段时长已交付;按类型的「超 SLA」仍不在交付面内**(维护者裁定 2026-09-09 1C + 2B,2026-10-25 C)。平台 18.2.0 提供两个 datetime 列之差的聚合度量(`duration: { from, to, unit }`,`objectstack-ai/objectstack#25778`),各段时长与平均审批周转都建在它上面;「`AVG()` 打在 datetime 上再相减」那条错路自 17.7.0 起在作者期与查询期都被拒绝(`objectstack-ai/objectstack#16737`)。「超 SLA」要拿 `review_started_at` 比「现在减去该合同类型的 `review_sla_days`」:18.2.0 的 `$now` 锚点(`objectstack-ai/objectstack#25808`,裁定 A)只读同一条记录上的数值列,经 `contract_type` 的点路径在查询期被拒绝(`400 INVALID_FILTER`),跟踪于 〔open question 的新平台卡号〕。在那之前 SLA 以固定 30 天阈值呈现且阈值写进砖的标题(九种类型的真实 SLA 是 2/3/5/10 天,30 天高于全部)。〔「审批瓶颈」一段原样保留〕⛔ 不得用应用侧日任务盖戳字段,也不得在合同上镜像一列 SLA 天数,把「超 SLA」补出来 —— 那是在应用里复刻平台规则。", "DESIGN.md §12 table, new row": "| 按类型超 SLA | objectstack#25808(同记录已交付;跨对象偏移待新卡) | 固定 30 天阈值,阈值写进砖的标题 | 按 `clm_contract_type.review_sla_days` 计数 |" } }Generated by Claude Code
The legal workbench shows how long each contract stage takes (#168)
objectstack-fleet commentedon Oct 27, 2026
Ruling-ref: 6396322580
Landed (part) — PR #168 · 2026-10-27, and re-blocked
MERGED(squash onmain) at its ACCEPT headcfb92e3;Part of #31durationmeasure. The numbers match independent SQL$nowanchor reads its offset from the same record only (objectstack#25808 ruling A). The fixed 30-day tile stays (2B), and nothing is stamped in the appNext: objectstack-ai/objectstack#26601 asks the platform to let the offset follow
contract_type(reopening B′). If the platform says no for good, that answer comes back to the maintainer, since ruling C chose to keep the goal. Also filed: objectstack-ai/objectstack#26602 (a rising turnaround is drawn green). §09 text rides seat PR #120. State:pm:dispatched→pm:blocked, assignee cleared; the body'sBlocked-by:now names #26601.Generated by Claude Code
DESIGN §02/§09/§12: stage durations delivered on the duration measure…