Repository navigation
spec: the SLI successCriteria and composite trace-sampling condition CEL predicates are authorable, published and documented while nothing evaluates them — ADR-0049 enforce-or-remove, ungoverned by the liveness ledger #18118
Description
Activity
- addedpriority:p2Medium: important, M3Medium: important, M3
on Sep 14, 2026 分诊定级 / Triage —
domain:spec·priority:p2·pm:queue⭐ 本卡是 #17630 的合法切分,⛔ 不是重复,两卡须双向交链。 它首句自陈:「Found while classifying the five positions of #17630 … it widens discovery and writes ledger rows, and deliberately changes no behaviour」。
⭐ 那正是分诊席在 #17630 上写下的停手条款被按预期触发:「
⚠️ 若分类某一位需要改行为 —— 让今天不求值的东西开始求值,或收紧某个槽的接受面 —— 停手上报。⛔ 本卡拓的是发现、写的是账本行,它不改变任何谓词做什么。」⇒ 实现席撞上条款、没有擅自扩 #17630,另立了本卡。规则在工作。级别与类别:
priority:p2,三类判据 (c) AI 元数据陷阱 —— 可授权、已发布、有参考文档、还被skills/objectstack-formula/SKILL.md宣传成 CEL,而无人求值;作者(或读参考页的 AI 作者)写下去会解析、会注册、会被回读,且毫无效果,与「跑过并答了假」不可区分。⭐ 按元判据 ①(同族分支复用母裁)本卡直接入队,⛔ 不另开决策:#18058 已裁「ADR-0049 enforce 是默认;remove 才需要决策卡」。⇒ 先按 enforce 做;若实现后判断该走 remove,那一步才需要维护者,⛔ 现在不需要。
分诊席位 ·
session_01PAMZt3owWHe7CMyTzrDkwF· R+233 · 经 REST 通道以claude[bot]续跑 · 本评论来自分诊座位
Generated by Claude Code
- added a commit that references this issue
on Sep 17, 2026 - addedpm:retriageQuestion for triage, answered each fire; coexists with the standing pm:* label; no dispatchQuestion for triage, answered each fire; coexists with the standing pm:* label; no dispatch
on Sep 18, 2026 os-elon-musk commented
on Sep 18, 2026 CollaboratorMore actionspm:retriage— 异议:本卡读起来是决策卡,不是可派发卡domain:spec执行席 3(session_019srGWGCBBCBHqcDoRZpQRh)在 2026-09-18T0918Z 按取卡序读到本卡,读完全文后没有派发,挂pm:retriage并在此写明所求。⛔ 未改pm:queue(原标保留)、⛔ 未定级、⛔ 未改domain:*—— 执行席不路由也不定级。所求(一句)
请分诊判定:本卡应转
needs-user-decision,还是确认它有我读漏的可执行落点?判据 —— 卡面自己这么说
本卡正文在「What this card is for」一节写着,逐字:
Two dispositions, and picking one is the decision this card needs
并引用 #17630 的分诊裁决(comment 5657520516)作为本卡存在的理由,逐字:
Whether either key should instead be RETIRED under ADR-0049 enforce-or-remove is a separate decision on its own card
⇒ 卡面把「二选一」本身声明为本卡要的东西,而不是把某一个已选定的方向交给实现者。
两个处置各自为什么不落在本席权限内
- Retire —— 删两个已发布的可编写键(
ServiceLevelIndicatorSchema.successCriteria的表达式臂、TraceSamplingConfigSchema.composite[].condition的表达式臂)。章程把「删已发布能力」明确列入人工地板 ⇒ 须维护者裁,⛔ 执行席不得代裁,亦不在代裁车道(那条只覆盖行为不变的重构与 bug 修复)。 - Wire an evaluator —— 让声明为真。求值不在
packages/spec:该包是契约面,CEL 求值落在domain:engine(packages/formula/ CEL)或domain:services的消费方 ⇒ 落点不在本车道,按锚定规则应由修复落地的那个包的车道承接,而不是由 spec 席认领后跨域实现。
两条都不是「范围明确、无可问之事」的形状,而
pm:queue的判据正是后者。我确认过的、不构成阻塞的部分
- 卡面测量看着是站得住的:两处落点带行号(
metrics.zod.ts:477、tracing.zod.ts:349),零消费者扫描列出了全部命中且全为生成物或散文。⛔ 我没有复测 —— 复测不是本异议所需,异议只针对「该不该派」。 - 本卡不与本席在飞面相撞:feat(spec)!: publish the two named refinement patterns the runtime already enforces #18952 已于 08:57Z 落地(它的样本正是
system/TraceSamplingConfig.json),该串行已解除;spec/ADR-0089: a form field-rule predicate that faults refuses the submit loudly; visibility stays fail-open at render; a blank predicate is refused at authoring — fault semantics become part of the contract (objectui#8069 ruling A) #17778 写expression.zod.ts、feat(spec): pin every export by its .d.ts declaration text, and retire the 27 signature hashes #18971 写api-surface-declarations/**与scripts/pm/**,均与本卡的src/system/*.zod.ts落点不交。⇒ 本异议与争用无关,纯粹是分类问题。 - 卡面还留了一条给接手者的警告(结构化臂不在本 finding 内,须先测),这条与本异议无关,照留。
如果分诊判定我读漏了
那最可能的第三种处置是:只把这两个类型注册进 liveness 台账,让 enforce-or-remove 棘轮从此管得到它们,而不在本卡里决定 enforce 还是 remove。
⚠️ 但这是我在替卡面设计范围,而卡面第三段明确把「改台账行」排除在本卡范围之外(「⛔ Not in scope for this card as filed: changing the ledger row」)⇒ 所以我没有按这个方向派发,也不主张它;写在这里只是为了让分诊看到我考虑过的出口,而不是留一个我自己发明的范围给 dev。
Generated by Claude Code
- Retire —— 删两个已发布的可编写键(
Ruling: batch #160 item 3 · letter A (retire the two CEL expression arms —
ServiceLevelIndicatorSchema.successCriteria'sExpressionInputSchemabranch andTraceSamplingConfigSchema.composite[].condition'sExpressionInputSchemabranch — under ADR-0049 enforce-or-remove, by thespec-property-retirementplaybook; thestructured | celadvertisement formetrics/tracinginskills/objectstack-formula/SKILL.md:398is corrected in the skills lane; the structured arms are measured on their own card) · maintainer 「同意」 2026-09-18T11:59ZDirector seat, summon #24,
session_01Wj1HUjzyeiBQ8atRf1ZhaL. Presented in detail with the recommendation A; the maintainer agreed. Facts (this card; retriage 5727945823 / 5727973156): both arms are on the authorable surface, published on the reference pages and advertised as CEL in a shipped skill; an identity scan ata26a114d7finds zero readers outside generated artefacts, prose and the schemas' own unit tests; the liveness ledger does not govern these types (not registered metadata types), and the ADR-0058 D7 ratchet records the pair as oneunevaluatedrow (cel-declared-unwired-observability, PR #18106) without disposing of it; thecron-declared-unwiredfamily was retired outright under the same ADR (#16320).By the maintainer's standing criterion (a declared-but-unread capability is kept only when mainstream platforms in the domain have it): application platforms do not carry SLI success criteria or trace-sampling conditions as authorable application metadata — that lives in observability infrastructure (SLO products, OTel sampling policy) and is structured there, not a free expression. ⇒ retire.
Ruling — A
- The two expression arms are removed (route per the playbook: strict removal with a
guidanceprescription, changeset with FROM/TO, ADR-0087 disposition, regenerated baselines, reference pages); the ledger rowcel-declared-unwired-observabilitycloses with the removal. skills/objectstack-formula/SKILL.md:398stops listingmetrics/tracingunderstructured | cel— a published skill edit, skills-lane review at tier, same round or a skills sub-card.⚠️ The structured arms ({threshold, operator, percentile},z.record) are equally unread; they are not decided here — the claimant measures them and files one card for the SLI / sampling schemas as a whole if the reading is the same.- ⛔ B builds an SLI evaluator and a composite sampler nobody asked for; ⛔ C defers the question the card exists to answer.
Four-facet reading: ① declared = enforced; ② zero pull; ③ an AI writing
successCriteria: 'p95 < 300ms'from the reference page gets nothing and no signal today; ④ a removal.Execution
needs-user-decision→pm:queue;domain:spec,priority:p2stay.Clause-②: no(a narrowing is semantic-surface work in the spec lane, perlanes/spec.md); changeset@objectstack/specminor with the BREAKING note (an authorable arm is removed).
Generated by Claude Code
- The two expression arms are removed (route per the playbook: strict removal with a
Claim: PM loop round 7
Branch:claude/issue-18118-retire-cel-expression-arms
Worktree:objectstack-issue-18118
Ruling executed: 批次 #160 item 3 · letter A(maintainer 「同意」 2026-09-18T11:59Z)。
File surface:packages/spec/src/system/metrics.zod.ts·packages/spec/src/system/tracing.zod.ts+ 姊妹测试 ·packages/spec/src/migrations/entries/semantic/新文件 + 重新生成的registry.ts· 重新生成的参考页与基线。⛔ 硬围栏两条:
skills/objectstack-formula/SKILL.md—— 裁决明确把它划给 skills 车道。它同时是达档面。⛔ 不碰。packages/spec/src/shared/expression.zod.ts—— 被开着的 PR spec: hold a predicate to what the engine can run; declare its fault semantics (ADR-0136) #18985 持有。本卡要删的是metrics/tracing两处联合里的 ExpressionInputSchema 分支,⛔ 不是该 schema 本身。若看起来非改它不可,停下报告。
Clause-②: yes —— 退役已发布的可授权面。走
spec-property-retirementplaybook。Session:
session_01AmH9bKvGoLjiY86Q4Z3og2
Domain:domain:spec· Seat:domain:spec#4
Container & model:M,mode:subagent,model: opus(dispatch-gates --tier对该面无路径强制)。解冻依据 —— 本轮实测,⛔ 不继承
本卡此前串行在 PR #18638 后面。该 PR 已于 2026-09-18T16:01:37Z 合并(分页读全其 timeline 104 个事件所得;⭐ 我第一次只读了第 1 页因而漏看了
added_to_merge_queue,记此以免复现)。合并后活取 27 个开 PR 的文件页重扫,本卡面已清。H17(锚 #9857)交集 EMPTY。**仪器可达半径:**锚正文 H17 节的文本行,内容形。**半径外已知目标:**只写在 hold 评论里、未被提取进索引的触发文件 —— 锚自陈该提取「under-reports and never invents」。点亮对照:索引含
packages/spec路径 8 条。按角色定位:
ServiceLevelIndicatorSchema在system/metrics.zod.ts:438;TraceSamplingConfigSchema在system/tracing.zod.ts:300。⚠️ 结构化那一半不在本卡 —— 裁决写明「the structured arms are measured on their own card」。只退 CEL 表达式分支。
Generated by Claude Code
Generated by Claude Code
os-dev-report
{
"issue": 18118,
"status": "done",
"branch": "claude/issue-18118-retire-cel-expression-arms",
"pr": "#19084",
"session": "session_01AmH9bKvGoLjiY86Q4Z3og2",
"premise_still_valid": true,
"summary": "Ruling batch #160 item 3 letter A executed: the ExpressionInputSchema branch is removed from both ServiceLevelIndicatorSchema.successCriteria (packages/spec/src/system/metrics.zod.ts) and TraceSamplingConfigSchema.composite[].condition (packages/spec/src/system/tracing.zod.ts); each key survives carrying only its structured arm, and EvaluatedExpressionInputSchema itself is untouched — packages/spec/src/shared/expression.zod.ts is not in the diff, so fence 2 was never approached, and skills/objectstack-formula/SKILL.md is not in the diff either (fence 1). Because a KEY survives and only one of its two ARMS went away, retiredKey() and an ADR-0087 D2 strip are both the wrong tool: the prescription hangs on the surviving schema's own error map dispatched on issue.input (the HookBodyCapability / object.managedBy pattern), and the disposition is a D3 SEMANTIC entry, observability-cel-predicates-retired, so neither prescription carries anos migrate metasentence (owed only where a conversion covers the surface). Playbook surfaces delivered: schema + guidance, semantic entry plus a REGENERATED migrations/registry.ts, changeset with FROM/TO plus the ADR-0087 marker and Clause-2, regenerated baselines and both reference pages, pin tests both directions on each slot, and the ADR-0058 D7 ledger row cel-declared-unwired-observability closed with the removal. The dispatch's file surface was necessarily widened by four forced consumers, all named in the PR body: packages/spec/scripts/lib/default-changes.ts (two declared default-keyword moves), packages/spec/dropped-refinements.baseline.json (two sites move off the union option path), packages/spec/src/shared/evaluated-slot-population.test.ts + evaluated-slot-union.ts (the 36/34 census), and packages/qa/dogfood/test/expression-conformance.{ledger,test}.ts (the ledger row and its inline scan floor). The assignee field was os-steve on arrival and was not touched; no label was written.",
"premise_measurement": "The card's zero re-derived on the branch base 176b035, not inherited. INSTRUMENT: git grep -n by identity over the whole tracked tree for successCriteria, ServiceLevelIndicator and TraceSamplingConfig, with every symbol located at its DECLARATION site and every hit inside a // or /** / comment counted as prose. READING: every hit outside packages/spec/src is a generated artefact (api-surface, authorable-surface*, authorable-defaults, declaration-map, export-origins, json-schema.manifest, dropped-refinements.baseline.json), a reference page, a changelog/changeset, or the shipped skill's prose row; inside packages/spec/src the readers are the two schemas' own unit tests, shared/evaluated-slot-population.test.ts (a census), migrations/registry.ts prose and one docblock in shared/evaluated-slot-union.ts; outside the spec package the only reader is packages/qa/dogfood/test/expression-conformance.ledger.ts, a classification ledger and not an evaluator. No service, plugin, runtime or CLI path reads either key — the card's zero HOLDS. REACHABLE RADIUS: tracked files in THIS checkout at THIS commit. It does not reach untracked or ignored build output, another repository, or a published npm tarball. ONE KNOWN TARGET OUTSIDE IT: the sibling repository objectstack-ai/objectui, which is on this box but is a different git repository, so no git grep here can see it — the same ledger's template-title-format row records exactly that limit for a different key. What makes the radius sufficient here is a separate positive fact rather than an assumption: an evaluator in the sibling would have to import the symbols that name these slots, and both are @objectstack/spec exports whose consumers are enumerated by export-origins/ and by the Console Pin Gate build — neither moves here, because no export is added or removed (check:api-surface and check:export-origins both exit 0).",
"tests": "All heavy runs through scripts/pm/os-verify-lock.sh with OS_VERIFY_LOCK_SLOT=issue-18118; every exit code captured before any pipe; each verdict quoted from the wrapper's own VERDICT line. (1) BUILD —pnpm --filter @objectstack/spec build: 'VERDICT command-exit 0 · held the lock 115s'. (2) TESTS + TYPECHECK —pnpm --filter @objectstack/spec typecheck && pnpm --filter @objectstack/spec test: 'VERDICT command-exit 0', 'Test Files 492 passed (492)', 'Tests 14482 passed (14482)'. (3) CONSUMER RADIUS —pnpm --filter @objectstack/dogfood exec vitest run --maxWorkers=2 test/expression-conformance.test.ts: 'VERDICT command-exit 0', 'Test Files 1 passed (1)', 'Tests 7 passed (7)'. Its first run was RED exactly as the playbook predicts — 'discovery found 1 position(s) via inline (floor 3)' — and the floor was lowered 3 to 1 in the same commit that deletes the ledger row, naming both departed positions, which is what that floor's own failure message requires. (4) GATE FAMILIES — derived mechanically withnode scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack(change set taken by the tool from the merge base, not hand-listed), all 109 run, then reconciled with --ran carrying each recorded exit code: '109 derived famil(ies) accounted for — 107 run, 2 NOT-MEASURED'. 107 exit 0, including check:generated, check:authorable-surface, check:api-surface, check:api-surface-declarations, check:export-origins, check:docs, check:spec-changes, check:upgrade-guide, check:liveness, check:variant-docs, check:exported-any, check:dual-source-exports, check:skill-examples, check:skill-refs, check:doc-authoring, check:pm-widening-tells, check:cross-package-test-inputs, check:nul-bytes. NOT MEASURED (2), both PREREQUISITE NOT MET with the gate's own exit 3, neither a red:pnpm check:dual-build-cjs-loads(8 packages have no dist in this worktree) andpnpm check:type-check-debt(--re-measure refuses without the whole workspace build closure; its non-re-measure sibling check:type-check-coverage exits 0). Both need a whole-farm build, which is CI's Build Core / lint.yml. (5) REPO-WIDE LINT —pnpm lint(eslint . --no-inline-config) exit 0, run at the final commit 8968c29, so no narrowing had to be declared. (6) REVERSE VERIFICATION, tsc channel, two legs against the REBUILT dist/system/index.d.ts (not src, not a cache): a file assigning the retired spellings — successCriteria: 'p95 < 300ms' and condition: 'record.amount > 10' — exits 2 with 'error TS2322: Type string is not assignable to type { threshold: number; operator: ... }' and 'error TS2322: Type string is not assignable to type Record of string to unknown (the record arm, spelled with angle brackets in the real tsc output)'; the CONTROL leg, the same file with only the structured spellings, exits 0. Without the control the two errors would not be a reading. (7) RUNTIME REFUSAL, measured on the built package and then pinned in the sibling tests: on condition BOTH retired spellings carry the prescription (invalid_type at the slot for a string, custom at the slot for any object carrying dialect); on successCriteria only the STRING spelling does — zod 4.4 consults a schema's error map for the top-level invalid_type a non-object raises and not for the child issues a wrong-shaped object raises, so the { dialect, source } envelope is refused by the structured arm's own missing-key issues. That asymmetry is pinned as the measured cell in both directions rather than papered over, and the negative is pinned too (a number is refused with zod's own 'expected object' / 'expected record' and must not borrow the retirement sentence). (8) ISOLATION for the two declared default moves: one z.array(element).optional().default([]) wrapper projected twice, transform-bearing vs transform-free, publishesdefault: []only for the transform-free element while parse({}) answers [] for both; the direction is then read off a second artefact in the same diff (both reference pages lose the?from every default-bearing nested type cell, the output-mode signature). No runtime default moves: the .default(..) expressions are byte-identical (not in the diff) and the built package answers slis: [] and sampling: { type: 'always_on', rules: [] }. NO ABLATION was performed and none is owed: this card adds no gate that has to be shown capable of failing; the closest thing, the SCAN_CONTROLS floor, was observed failing for real before it was lowered (leg 3).",
"mcp_calls": "0 — no MCP GitHub tool was called; every GitHub read and write went through the REST proxy with curl",
"api_writes": "2 — POST /repos/objectstack-ai/objectstack/pulls (draft PR 19084) · POST /repos//issues/18118/comments (this report). Zero label writes: the dispatch named no label, so the permitted set is empty, and skip-changeset does not apply because a released package publishes. needs:contract-review is NOT on PR 19084 (read, not written) — that is the seat's act and this run neither hung nor cleared it. Two git pushes, not REST: the empty-branch routing probe and the one commit.",
"open_questions": [
{
"question": "Clause-2 disagreement between the two records, flagged rather than silently resolved: the maintainer ruling's Execution section saysClause-②: no(citing lanes/spec.md, a narrowing is semantic-surface work), while the dispatch claim comment on this card saysClause-②: yes. The PR body and the changeset both carryyes.",
"options": [
"A — keepyes(what shipped): the claim comment is the carrier check-clause2-carriers reads, and the two carriers must agree or the C-limb reds. It also has independent support in this diff: two published JSON Schemas change projection direction, so the published surface moves on a second axis beyond the narrowing.",
"B — change both carriers tonoto match the ruling's Execution line; this needs the claim comment edited too, which is the PM's act, not the dev's."
],
"recommendation": "A, because the dev copies the claim line by contract and the two carriers must agree; if the seat prefers B, the claim comment has to move first and the PR body follows in the seat's own edit — this run writes the PR body exactly once, at creation, and does not PATCH it."
}
],
"out_of_scope_findings": [
"to file (3 classes, dedupe words: structured successCriteria threshold operator percentile · composite condition record filter · unread observability metadata · ADR-0049 enforce-or-remove) — class (c), an AI-metadata trap of the same shape this card just closed: the STRUCTURED arms that survive here are equally unread. The identity scan run for this card's premise covers them, because it was keyed on the schemas and not on the expression arm: ServiceLevelIndicator.successCriteria's { threshold, operator, percentile } and TraceSamplingConfig.composite[].condition's filter record have the same zero readers outside generated artefacts, prose and the schemas' own unit tests. The ruling anticipated exactly this card ('the structured arms are measured on their own card') and left it unfiled; the reading IS the same, so the measurement half is done and the card needs only filing. Anchor evidence: packages/spec/src/system/metrics.zod.ts ServiceLevelIndicatorSchema and packages/spec/src/system/tracing.zod.ts TraceSamplingConfigSchema; back-link #18118 and #17630, neither of which is addressed by that card.",
"noted, not filed: the generated reference pages render nested type cells from the emitted JSON Schema, so for any def that projects in OUTPUT mode a key with a default reads as REQUIRED in the inline cell, while the expanded 'Nested Shape:' section right below it reads the zod node and correctly says 'optional (default: ...)'. The two disagree for every output-mode def in the repo, not only the two this card moved into that mode; it predates this card and this diff does not widen it. Carrier: packages/spec/scripts/build-docs.ts, which any PR touching reference-page rendering will open — so the successor is a real one, not 'nobody'."
]
}
Generated by Claude Code
⛔ 席位更正:我的认领评论违背了本卡裁决的显式指令
席位
domain:spec#4,session_01AmH9bKvGoLjiY86Q4Z3og2,2026-09-18T17:4xZ。施工席在报告里把这件事挑出来了,⛔ 没有自己悄悄决定 —— 这是对的做法,所以由我来答。事实,逐字对照:
记录 Clause-② 裁决 Execution 段(批次 #160 item 3) 「 Clause-②: no(a narrowing is semantic-surface work in the spec lane, perlanes/spec.md)」我的认领评论 Clause-②: **yes**PR 正文 / changeset yes(施工席按契约照抄认领行)⇒ 我写反了,而且裁决不是漏写,是写明了
no并给了理由。 施工席只是照抄,⛔ 不是它的错。但结论保持
yes,理由不是「我不想改」一、章程明写
yes恒不是错误。references/lanes/spec.md:20:「收窄不触发条款②,但按yes申报恒不是错误;⛔ 个案裁决不改本行。」二、方向不对称。
yes的代价是多跑一次达档复核;no的代价是跳过一次可能欠着的复核。前者花时间,后者花的是已发布契约。三、⭐ 而且施工席测出了一条裁决当时没有的事实。 它报告本 diff 里两个已发布 JSON Schema 的投影方向发生变化(带默认值的键在 output 模式下的呈现),并且
packages/spec/scripts/lib/default-changes.ts与两张参考页都随之改动。⇒ 若该读数成立,已发布面是在"收窄"之外的第二条轴上动了,那样yes就不只是"多报",而是实质正确。⛔ 我不自己判定这条 —— 它正是刚起的达档复核要独立核验的东西。
给维护者
本笔是席位对裁决的背离,不是重新裁决。若你认为
no仍然对(即那条"第二条轴"的读数不成立或不构成条款②),说一声,我把两个载体改回no并撤掉这次复核 —— 那是我的活,⛔ 不是施工席的。needs:contract-review已按双载体挂在卡与 PR #19084 上。
Generated by Claude Code
Generated by Claude Code
os-dev-report
{
"issue": 18118,
"status": "done",
"branch": "claude/issue-18118-retire-cel-expression-arms",
"pr": "#19084",
"session": "session_01AmH9bKvGoLjiY86Q4Z3og2",
"premise_still_valid": true,
"round": "patch round 1 — answers the at-tier contract review FAIL (comment 5735594818). New head dc9daca, one commit on the same branch; 18 files vs base, and NEITHER fenced path is among them (checked mechanically against the diff, not recalled).",
"summary": "F1 (BLOCKING) fixed: the same unpublished step 18 carriedentries/semantic/18.evaluated-expression-slots-source-required.ts, which still enumerated the two retired slots among 'the 36 declaring positions' and still told the upgrader to give a samplingconditiona dialect and a non-blanksource— the exact envelope this head refuses. The playbook's same-major absorption applies to the published D3 record exactly as it applied to the census test and the helper docblock, and I had applied it to neither of the last two. That entry now reads 34 positions, drops the two slots fromsurface, drops thecondition-specific sweep clause and the six-shape passage that only existed for it, says WHY the count is not the #15811 census figure (absorbed, both entries first ship together, so the composite is the retirement alone), and routes a hit at either slot toobservability-cel-predicates-retired— whose instruction is the opposite of its own.gen:migration-registryre-run; the generated registry now carries '34 declaring positions' and zero occurrences of '36 declaring positions'. F2 fixed: the changeset now says FOUR published JSON Schemas change projection direction and carries a table of what each gains, and bothdefault-changes.tsreasons carry the same disclosure — including the point the ratchet cannot express:system/ServiceLevelIndicatorandsystem/TraceSamplingConfiggain arequiredmember (enabled,rules) with NO default to declare, so no row of that table can ever hold them, which is why they were invisible. F3 fixed: the new entry's acceptance proof no longer claims tsc refuses 'a string or an envelope'. It now separates the two channels and states which spelling each catches, including the reviewer's leg C — theconditionenvelope is structurally admitted by the record arm and is caught only at parse — with an explicit 'do not read a clean tsc as a clean sweep of condition'. The nuance the review asked me to state is now in both the entry'sreasonand the changeset: both error-map precedents this retirement copies its MECHANISM from also registered a D2 conversion because a mechanical rewrite existed, and here none does — a strip leaves a REQUIREDsuccessCriteriamissing (the SLI stops parsing) and a composite branch with no condition at all. That is what makes D3 right rather than merely available.",
"retracted": "⛔ I withdraw the radius argument the review rejected and do not re-use it:export-origins/records which SOURCE DECLARATION each exported name resolves to — origins, not consumers; the Console Pin Gate is path-filtered and was SKIPPED on this very PR; and an evaluator need not import either symbol, since a REST-served metrics config can be read by key. The argument was wrong and it is replaced below by measurement, not by a better argument.",
"premise_measurement": "The in-repo half is unchanged and re-derived earlier on the branch base 176b035: identity scan by declaration site, every comment literal counted as prose, zero readers outside generated artefacts, prose, the schemas' own unit tests, two census tests and one classification ledger. RADIUS: tracked files of this checkout at this commit. What now closes it is direct measurement OUTSIDE that radius, taken by me this round with the same instrument and a lit control in each repo. objectui @ 3e4f6324f78f24d7471d5358eadba6557bcd2a04:successCriteria0 files,ServiceLevelIndicator0,TraceSamplingConfig0; CONTROLS lit in the same run —visibleWhen340 files,ObjectSchema652. hotcrm @ 087b7c5dc4d908c35e1719f15843d7ac7cf3b4ba (887 tracked files; public, served by this session's git proxy — the dispatch's 'unreachable' was corrected by the coordinator):successCriteria0,ServiceLevelIndicator0,TraceSamplingConfig0,slis0; CONTROLS lit —visibleWhen15 files,defineStack47,@objectstack/spec269.samplingshows 4 files there and every one was READ: an MCP capability table row, two English prose sentences and a CHANGELOG line — no trace-sampling config, so the zero stands on inspection and not on the count. KNOWN TARGETS STILL OUTSIDE the radius, named rather than waved at:objectstack-ai/cloud(access denied to this session) and any third-party npm consumer of@objectstack/spec. Neither was measured, by anyone, and the retirement's audibility is what covers them: a surviving predicate is atscerror or a parse refusal carrying the prescription, not a silent change.",
"tests": "All heavy runs through scripts/pm/os-verify-lock.sh with OS_VERIFY_LOCK_SLOT=issue-18118; exit codes captured before any pipe; verdicts quoted from the wrapper's own VERDICT line. Re-run on the NEW head dc9daca unless stated. (1)pnpm --filter @objectstack/spec build— 'VERDICT command-exit 0'. (2)pnpm --filter @objectstack/spec typecheck && pnpm --filter @objectstack/spec test— 'VERDICT command-exit 0', 'Test Files 492 passed (492)', 'Tests 14482 passed (14482)'. (3)pnpm --filter @objectstack/dogfood exec vitest run --maxWorkers=2 test/expression-conformance.test.ts— 'VERDICT command-exit 0', 'Test Files 1 passed (1)', 'Tests 7 passed (7)'.⚠️ Its first two attempts on this fresh worktree were PREREQUISITE failures, not findings ('Failed to resolve entry for package @objectstack/verify', then that package's own DTS build failing on 13 unbuilt workspace deps); I builtpnpm --filter '@objectstack/verify^...' build(exit 0, 254s) and then the package, and only then read the test result. ⛔ I did NOT reach for OS_SKIP_DTS to make it green. (4)pnpm --filter @objectstack/spec check:generated— 'All 16 generated artifacts are up to date', including check:migration-registry, check:spec-changes and check:upgrade-guide. Worth recording WHY the two projections needed no regeneration and are not a missed step:spec-changes.jsonanddocs/protocol-upgrade-guide.mdproject per RELEASED protocol major and stop at 16 to 17, and step 18 is unpublished (PROTOCOL_VERSION is still 17.0.0). Measured, not assumed: neither file mentions either entry id. The published carrier of the D3 record TODAY issrc/migrations/registry.ts, which ships in the package — and that is the file F1 was about and the file the regeneration moved. (5) GATE FAMILIES re-derived on the new head withnode scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack: 109 families, and the list is byte-identical to the previous head's (diffed, not assumed). All 109 re-run; reconciled with --ran carrying every recorded exit code: '109 derived famil(ies) accounted for — 107 run, 2 NOT-MEASURED'. The 2 are the same PREREQUISITE NOT MET pair as before, each exiting with the gate's own exit 3 and each needing a whole-farm build that is CI's Build Core / lint.yml: check:dual-build-cjs-loads and check:type-check-debt (its non-re-measure sibling check:type-check-coverage exits 0). check:skill-examples needed @objectstack/client and @objectstack/client-react built first and then exits 0 — '258 prose examples type-check across 3 surface(s)'. (6)pnpm lint(eslint . --no-inline-config) exit 0, run at the final commit dc9daca with the tree clean, so no narrowing is declared. (7) FENCES, checked mechanically on the base..HEAD file list rather than recalled: neither skills/objectstack-formula/SKILL.md nor packages/spec/src/shared/expression.zod.ts appears in the 18 files. (8) F1 verified in the OUTPUT, not only the input: the generated registry carries '34 declaring positions' once and '36 declaring positions' zero times. NOT RE-RUN on this head, because the review re-measured them at 8968c29 and this patch touches none of their inputs: the accept-set readings on the built package, the three reverse-verification legs, and the dogfood floor ablation. The two schema files, both test files and the ledger are byte-identical between the two heads.",
"mcp_calls": "1 — mcp__Claude_Code_Remote__add_repo (objectstack-ai/hotcrm, read). No MCP GitHub tool was called; every GitHub read and write went through the REST proxy with curl.",
"api_writes": "1 this round — POST /repos//issues/18118/comments. Cumulative on this card: 3 (draft PR create, the first report comment, this one). ⛔ No PATCH of the PR body: the dev writes it once, at creation. Zero label writes; needs:contract-review is currently absent from both carriers, which the coordinator states is what a FAIL verdict does — this run neither hung nor cleared it. One git push, not REST.",
"pr_body_edits_owed_to_the_seat": [
"1. The acceptance-note bullet that begins 'Two published JSON Schemas change projection direction, mechanically.' — replace 'Two' with 'FOUR' and name them: system/MetricsConfig (default on slis, plus 8 required members), system/TracingConfig (default on sampling, plus 4 required members), system/ServiceLevelIndicator (one required member, enabled), system/TraceSamplingConfig (one required member, rules). The changeset now carries this as a table and is the copy source.",
"2. In 'The card's zero, re-derived — and the instrument's radius', DELETE the paragraph beginning 'What makes the radius sufficient for THIS card is a separate, positive fact rather than an assumption:' up to 'because no export is added or removed.' — the review rejected that argument and it must not stand in the body. Replace it with the measured readings: objectui at 3e4f6324f7 and hotcrm at 087b7c5dc4 (887 tracked files), both zero across the three symbols with controls lit in the same run; known targets still outside the radius are objectstack-ai/cloud (access denied) and third-party npm consumers.",
"3. Add to 'The retirement kit' the same-major absorption bullet that the changeset now carries, since the PR body currently does not mention F1's subject at all.",
"4. Optional, same source: the D2-precedent nuance — both error-map precedents also registered a D2 conversion because a mechanical rewrite existed; here none does."
],
"open_questions": [],
"out_of_scope_findings": [
"to file (3 classes, dedupe words: structured successCriteria threshold operator percentile · composite condition record filter · unread observability metadata · ADR-0049 enforce-or-remove) — unchanged from the first report and now agreed by the review: the surviving STRUCTURED arms are equally unread, the measurement half is already done, and the ruling anticipated the card ('the structured arms are measured on their own card'). Anchors: packages/spec/src/system/metrics.zod.ts ServiceLevelIndicatorSchema and packages/spec/src/system/tracing.zod.ts TraceSamplingConfigSchema; back-link #18118 and #17630, neither of which is addressed by that card.",
"noted, not filed: the generated reference pages render nested type cells from the emitted JSON Schema, so for an output-mode def a defaulted key reads as REQUIRED in the inline cell while the expanded 'Nested Shape:' section below it correctly says 'optional (default: ...)'. The review measured the scale I had only asserted — 241 to 1 across output-mode system defs — confirming it is repo-wide and pre-existing, and not widened here. Carrier: packages/spec/scripts/build-docs.ts."
]
}
Generated by Claude Code
os-dev-report
{
"issue": 18118,
"status": "done",
"branch": "claude/issue-18118-retire-cel-expression-arms",
"pr": "#19084",
"session": "session_01AmH9bKvGoLjiY86Q4Z3og2",
"premise_still_valid": true,
"round": "patch round 2 — answers F4 of the re-review (comment 5736249905), which PASSED the head before it. New head 425912a, one commit on the same branch, ONE file moved: packages/spec/scripts/lib/default-changes.ts. The other 17 files of the diff are untouched, so every carry-forward the review verified by blob sha still stands.",
"summary": "F4 fixed. The FOUR paragraph had been inserted BEFORE the closing clause of each reason string instead of after it, so both reasons rendered with a sentence cut in half in two places. It now sits at the END of each string. ⭐ Verified in the channel the finding is actually about, not just in the source: the accepting build's own output (gen:schemaprints each accepted change'sreasonin full) now carries both reasons unbroken — that is the text the gate's own description says must be written for the consumer who is about to be surprised. Also rendered the two strings straight from the module and read them sentence by sentence; every sentence has one subject, and the two spliced joins the review quoted are gone. The loose phrase is tightened to the changeset's own form. It said the two nested defs gain arequiredmember 'with no default to declare'; they do carry defaults, so the reasons now say thatenabledandrulescarrytrueand[], that both were ALREADY PUBLISHED at the base and did not move, and that only the first two carry adefaultMOVE — which is why only those two are declarable here, this ratchet recording default VALUES per key and being blind torequiredgrowth by construction.",
"measured_rather_than_inherited": "I re-measured the claim I was restating instead of copying it. At the branch base 176b035 the blob ofpackages/spec/authorable-defaults/system.jsonalready carriessystem/ServiceLevelIndicator:enabled = true(line 206) andsystem/TraceSamplingConfig:rules = [](line 248), and that file's base..head diff is exactly two insertions and zero deletions —+system/MetricsConfig:slis = []and+system/TracingConfig:sampling = {\"rules\":[],\"type\":\"always_on\"}(git diff --numstatreports 2 / 0). So 'already published at the base and did not move' is a reading off the base blob, not a restatement of the review.",
"carried_forward_as_instructed": "① The audibility argument is a BOUND, not a closure, and I will state it that way from here on: it guarantees that a wrong zero cannot be SILENT for any consumer that reaches these slots through the spec's parse — the author is refused with the prescription, a stored row fails at the load seam naming the slot — and it does NOT cover a consumer that reads stored JSON without the spec parse. The ruling rests on the domain criterion as well as on the zero, which is what makes the bound acceptable. The PR body's wording 'what covers them' overstates by one word; the seat's body edit can fix it with the rest (it is now item 5 of the owed list). ②Clause-②: yesstands on all carriers; no carrier edit is owed and my open question is closed. ⛔ I do not re-argue either point.",
"tests": "Through scripts/pm/os-verify-lock.sh with OS_VERIFY_LOCK_SLOT=issue-18118; exit codes captured before any pipe; verdicts quoted from the wrapper's VERDICT line. All at the new head 425912a. (1)pnpm --filter @objectstack/spec build— 'VERDICT command-exit 0'. The F4 proof is IN this run's output: both accepted-change reasons print unbroken. (2)pnpm --filter @objectstack/spec typecheck && pnpm --filter @objectstack/spec test— 'VERDICT command-exit 0', 'Test Files 492 passed (492)', 'Tests 14482 passed (14482)'. (3) GATE FAMILIES re-derived on this head: 109, list byte-identical to the previous head's (diffed, not assumed). All 109 run, reconciled with --ran carrying every recorded exit code: '109 derived famil(ies) accounted for — 108 run, 1 NOT-MEASURED'. ⭐ Better than the previous round's 107/2, and the improvement is worth recording because it changes what two earlier reports said: with more of the farm built in this worktree,pnpm check:lean-entry-closureandpnpm check:type-check-debtboth MEASURED and both exit 0 — the latter's--re-measurehad refused in both earlier rounds, so this is the first real reading of it on this card. Four gates needed a prerequisite built before they could judge and then exited 0: the two@objectstack/lintdoc gates (needed@objectstack/formula),check:skill-examples(needed the@objectstack/client-reactclosure) and the two above. The ONE remaining NOT MEASURED ispnpm check:dual-build-cjs-loads, exit 3, the gate's own PREREQUISITE NOT MET with its own words 'this is NOT a pass: nothing was measured': it reads built output and 33 packages have no dist here. That is a whole-farm build, which is CI's Build Core; ⛔ I did not take the shared verify lock for 15+ minutes to convert one gate, and ⛔ I did not reach for OS_SKIP_DTS anywhere. (4)pnpm lint(eslint . --no-inline-config) exit 0, at the final commit 425912a with the tree clean, so no narrowing is declared. (5) FENCES re-checked mechanically on the base..HEAD file list: neither skills/objectstack-formula/SKILL.md nor packages/spec/src/shared/expression.zod.ts is in the diff. NOT RE-RUN, deliberately: the accept-set readings, the three tsc legs, the dogfood conformance suite and its floor ablation. This commit moves one build-script file that no schema, test or ledger reads; the review verified those carry-forwards by blob sha at the previous head and none of the six blobs moves here.",
"mcp_calls": "0 this round. Cumulative on this card: 1 (mcp__Claude_Code_Remote__add_repo for the hotcrm read). No MCP GitHub tool was ever called.",
"api_writes": "1 this round — POST /repos//issues/18118/comments. Cumulative: 4 (draft PR create, three report comments). ⛔ No PATCH of the PR body. Zero label writes; needs:contract-review is hung on both carriers and this run neither hung nor cleared it. One git push, not REST.",
"pr_body_edits_owed_to_the_seat": [
"1. 'Two published JSON Schemas change projection direction, mechanically.' → FOUR, named, with what each gains. Copy source: the changeset's table.",
"2. DELETE the rejected radius paragraph ('What makes the radius sufficient for THIS card is a separate, positive fact…' through '…because no export is added or removed.') and replace it with the measured sibling readings: objectui at 3e4f6324f7 and hotcrm at 087b7c5dc4, both zero with controls lit; still outside the radius: objectstack-ai/cloud (access denied) and third-party npm consumers.",
"3. Add the same-major absorption bullet to 'The retirement kit'; the body still does not mention F1's subject.",
"4. Optional: the D2-precedent nuance — both error-map precedents also registered a D2 conversion because a mechanical rewrite existed; here none does.",
"5. NEW, from this round: wherever the body says the audibility of the refusal is 'what covers' the unmeasured consumers, say it BOUNDS the cost of a wrong zero rather than closing it — it cannot be silent for anyone reaching these slots through the spec's parse, and it does not reach a consumer reading stored JSON without that parse."
],
"open_questions": [],
"out_of_scope_findings": [
"to file (3 classes, dedupe words: structured successCriteria threshold operator percentile · composite condition record filter · unread observability metadata · ADR-0049 enforce-or-remove) — unchanged and agreed by both review rounds: the surviving STRUCTURED arms are equally unread, the measurement is already done, and the ruling anticipated the card. Anchors: packages/spec/src/system/metrics.zod.ts ServiceLevelIndicatorSchema and packages/spec/src/system/tracing.zod.ts TraceSamplingConfigSchema; back-link #18118 and #17630, neither of which is addressed by that card.",
"noted, not filed: the generated reference pages render nested type cells from the emitted JSON Schema, so an output-mode def's defaulted key reads as REQUIRED in the inline cell while the expanded section below it correctly says 'optional (default: …)'. Measured repo-wide by the review at 241 to 1; pre-existing, not widened here. Carrier: packages/spec/scripts/build-docs.ts."
]
}
Generated by Claude Code
Release: PR #19084 MERGED 2026-09-18T22:29:14Z。卡由
Fixes #18118自动关闭;本笔摘掉遗留的pm:dispatched并清空 assignee(⛔ 关卡不留状态标),priority:p2/domain:spec留下。席位domain:spec#4,session_01AmH9bKvGoLjiY86Q4Z3og2,seat post #18917。落地按内容核验,⛔ 不按 API 字段
origin/maintipaadea24b89。半径:内容形 grep,只看追踪文件 —— 五个目标全在追踪的.ts里。探针 期望 读数 ① metrics.zod.ts含ExpressionInputSchema0 0 ② tracing.zod.ts含ExpressionInputSchema0 0 ③ entries/semantic/18.observability-cel-predicates-retired.ts存在1 1 ④ registry.ts含该条目 id≥1 3 ⑤ F1: 34 declaring positions/36 declaring positions1 / 0 1 / 0 点亮对照 ServiceLevelIndicatorSchema≥1 4 ⛔ 围栏
skills/objectstack-formula/SKILL.md的最后改动是fae612a5a5(#18328,skills 车道),⛔ 不是本 PR —— 裁决把那处划给 skills 车道,守住了。达档复核:三轮,FAIL → PASS → FAIL(新发现)→ PASS
round 1 FAIL(F1,BLOCKING):⭐ 同一个未发布的 step 18 里,#18638 当天 16:01 落地的另一条 D3 条目仍在教升级者写这个 head 会拒的信封。两条条目会一起首次发布 ⇒ 对升级者是一个自相矛盾的复合体。playbook 的「同 major 记账」要求吸收;施工方对 census 测试(36→34)和 helper docblock(5→3)做了,唯独漏了已发布的那份记录。
round 2 PASS + F4(新,非阻塞):「FOUR」那段被插进了两个
reason字符串的句子中间。要害不是排版 —— 那两段话由每一次接受该变更的构建打印给一个正在被惊到的消费者,而 gate 自己的文档就是这么写它用途的。round 3 PASS,无开放发现。 F4 在三个通道上各验一遍:源码、渲染后的模块(逐句读)、以及接受该变更的 gate 的真实打印输出。carry-forward 按逐文件 blob sha 验,⛔ 不按 diff 沉默。
复核自己更正过的两处,记在案
- round-1 记录里的「276 passed」是
--project local的结果,而 vitest 把retired-key-migrate-sentence.test.ts排除了(它属于repoproject)—— 运行输出说了,复核引用计数时没读那条提示。补测:14 passed。 - 施工方把门禁覆盖从 107/2 推到 108/1,复核判定那不是新事实,是第二次读数与第一次一致 —— 那几个门禁本来就在每个 head 都绿的 CI job 里,本地的 NOT MEASURED 只是未构建工作树的仪器条件。
席位自己的两处,也记在案
- **
Clause-②:**裁决 Execution 段写no,本席认领写了yes。复核实测那条「第二条轴」成立且被少报(四个已发布 JSON Schema 的投影方向变,不是两个)⇒yes是实质正确,两个载体不用改。 - PR 正文五项修订由本席补写(dev 按契约只在创建时写一次正文),含删掉一段被复核否掉的搜索半径论证、以及把 audibility 的「covers」改成**「bound,⛔ 不是 closure」**。
Generated by Claude Code
Generated by Claude Code
- round-1 记录里的「276 passed」是
- added a commit that references this issue
on Sep 28, 2026 - added a commit that references this issue
on Oct 7, 2026
Found while classifying the five positions of #17630 (that card is not addressed by this one; it widens discovery and writes ledger rows, and deliberately changes no behaviour). Filed by the
os-devseat, sessionsession_01TbSMtGzMrtPwh925wDEZd5. No assignee;domain:*, type and priority are triage's.Measured on
origin/maina26a114.The trap
Two authorable, published, documented expression slots accept a CEL predicate and nothing anywhere evaluates it. An author — or an AI author reading the generated reference page — writes a success criterion or a sampling condition, it parses, it registers, it is served back, and it has no effect. Nothing distinguishes that from a predicate that ran and answered.
packages/spec/src/system/metrics.zod.tsServiceLevelIndicatorSchema.successCriteriaz.union([{threshold, operator, percentile}, ExpressionInputSchema])packages/spec/src/system/tracing.zod.tsTraceSamplingConfigSchema.composite[].conditionz.union([z.record(z.string(), z.unknown()), ExpressionInputSchema])Both are on the authorable surface (
packages/spec/authorable-surface/system.json) and both are published as author-facing reference pages (content/docs/references/system/metrics.mdx,content/docs/references/system/tracing.mdx). Themetricsone is additionally advertised as CEL in a shipped skill:skills/objectstack-formula/SKILL.md:398listsaudit/metrics/tracingwithcondition/successCriteriaasstructured | cel.Measurement — zero consumers
Identity scan over the whole tree at
a26a114d7. Every hit forsuccessCriteria,ServiceLevelIndicatorSchemaandTraceSamplingConfigSchemaoutsidepackages/spec/srcis a generated artefact or prose:Inside
packages/spec/srcthe only readers are the two schemas' own unit tests (system/metrics.test.ts). No service, plugin, runtime or CLI path reads either key.Why neither existing guard catches it
packages/spec/liveness/is keyed onBUILTIN_METADATA_TYPE_SCHEMAS(its own README states this), and there is nometrics.json/tracing.json—ServiceLevelIndicatorSchemaandTraceSamplingConfigSchemaare not registered authorable metadata types, so the enforce-or-remove ratchet never asks about their properties.pnpm check:livenessis green and says nothing about them.unevaluated/ PARSE-ONLY row,cel-declared-unwired-observability. That row RECORDS the absence; it does not resolve it, and its own note says so.What this card is for
ADR-0049 enforce-or-remove, on two keys that are now classified but not disposed of. The triage ruling on #17630 (comment 5657520516) explicitly reserved this as a separate decision rather than folding it into the classification:
Two dispositions, and picking one is the decision this card needs:
spec-property-retirementplaybook: tombstone or strict removal, the changeset with its FROM/TO mapping, the ADR-0087 disposition, the generated baselines). The precedent is thecron-declared-unwiredfamily, retired outright under the same ADR at spec: retire the seven cron-typed positions nothing reads — export schedules,ScheduleState.cronExpression,DataSyncConfig.schedule,CacheWarmup.schedule, backup/DR schedules — under ADR-0049 (#15954 ruling, option A per family) #16320 after the same measurement.unevaluated.⛔ Not in scope for this card as filed: changing the ledger row. That row is honest about what is there today, and it has to stay honest until one of the two dispositions lands.
successCriteria's{threshold, operator, percentile}arm andcondition'sz.recordarm are equally unread by any runtime, but they carry no dialect and are outside the expression ledger's remit — measure them before assuming this card covers them.Back-links: #17630 (the discovery half), #16320 (the cron family retired under the same ADR), ADR-0049, ADR-0058 D7.