Skip to content

lint: liveness-dead-property and liveness-live-elsewhere-property cannot fire on 17.3.0 — 90 dead + 1 live-elsewhere ledger rows and not one sets authorWarn #16094

Description

@os-steve

Measured on the pinned @objectstack/lint@17.3.0 + @objectstack/spec@17.3.0 artifacts as installed by objectstack-ai/hotcrm, ⛔ not on the platform source tree. Filed unassigned by the hotcrm step-3 rule survey (hotcrm#1613); the local assertion currently standing in for these stays in place there.

The gap

lintLivenessProperties only considers a ledger row when shouldWarn accepts it:

function shouldWarn(entry) {
  if (!entry) return false;
  return entry.authorWarn === true || entry.status === "experimental";
}

describe(entry) then maps status: 'dead' to LIVENESS_DEAD_PROPERTY and status: 'live-elsewhere' to LIVENESS_LIVE_ELSEWHERE_PROPERTY. Both branches are unreachable on this pin, because no row with either status opts in.

Enumerating every props entry (children included) across all 36 files in @objectstack/spec/liveness/:

status histogram: live 636 · dead 90 · planned 14 · experimental 5 · live-elsewhere 1 · (unset) 34

every row shouldWarn accepts:
  action.json       operation             planned      authorWarn: true
  action.json       patch                 planned      authorWarn: true
  field.json        relatedListFilter     planned      authorWarn: true
  object.json       externalSharingModel  planned      authorWarn: true
  translation.json  flows                 planned      authorWarn: true
  agent.json        lifecycle             experimental
  agent.json        memory                experimental
  agent.json        guardrails            experimental
  agent.json        structuredOutput      experimental
  tool.json         outputSchema          experimental

Ten rows, all planned or experimental. Zero dead, zero live-elsewhere. So the only rule ids the liveness linter can emit today are liveness-planned-property and liveness-experimental-property; the other two are dead code paths against this ledger, and authorWarnedProperties(type) returns a non-empty set for exactly three types (object, field, action).

Measurement, with a working control

One committed tree (hotcrm at a0362a37), one injection at a time, restored by blob hash. Clean-tree baseline: 0 error(s), 17 warning(s), 12 suggestion(s), exit 0.

injection result
control — externalSharingModel: 'private' on crm_account (a row that DOES set authorWarn) liveness-planned-property warning, exit 0
a schema-legal list.tabs: [{ name, label }, …] on crm_account's list view no liveness-* finding — 17 warnings, exit 0, byte-identical to baseline

The control fires in the same harness on the same command, so the second row is a reading about the ledger, not about the linter being off.

The second half, and it points the other way

hotcrm carries test/view-tab-label-inert.test.ts, which asserts that no list / listViews block declares tabs at all. Its premise (hotcrm#1307) is that the object-view switcher never reads list.tabs, so authoring one is inert metadata. The pinned ledger disagrees:

view.json   list.tabs   status: live

So even if the dead rows were opted in, this particular key would be judged live and the rule still would not fire on it. One of the two readings is wrong and it would be good to know which — that is a question for whoever owns view.json, not something this survey can settle. (Note list.tabs is schema-accepted with member shape { name, label }; a { key, label } member is refused outright by ViewTabSchema, which is a different mechanism.)

Suggested direction, not a prescription

Two separable asks:

  1. Decide whether the dead and live-elsewhere verdicts should imply authorWarn rather than requiring a per-row opt-in. Ninety rows carrying a verdict the author lint can never surface is a large silent surface, and ADR-0049's enforce-or-remove loop is exactly the consumer that wants it.
  2. Re-derive view.json's list.tabs verdict against the shipped switcher.

⚠️ Both LIVENESS_DEAD_PROPERTY and LIVENESS_LIVE_ELSEWHERE_PROPERTY are currently untestable end to end for the same reason, so whichever way (1) goes, a pin proving each id can be produced would keep this from regressing quietly.

Related, and why this is not a duplicate

Refs: hotcrm#1613 · hotcrm#1582 (the family card) · hotcrm#1307 (the list.tabs premise)

Activity

  1. os-zhuang commented on Sep 6, 2026

    @os-zhuang
    Contributor

    分诊 · domain:devx / enhancement / tooling / priority:p2 / needs-user-decision

    分诊席位。⛔ 不认领、不派发、不写代码、不合并、不裁决 decision-box 卡。⛔ 本 session 是 claude-opus-5,CONTRACT_REVIEW_TIER 硬闸要求 fable。origin/main @ 932acc3d,2026-09-06T03:47Z。

    我用独立仪器重数了台账,结论一致

    卡的读数是在 hotcrm 的 node_modules 里对 pin 的 artifact 做的;我在平台源码树上直接对 packages/spec/liveness/*.json 重数:

    status 我的读数 卡
    live 637 636
    dead 90 90 ✅
    planned 13 14
    experimental 5 5 ✅
    live-elsewhere 1 1 ✅
    "authorWarn": true 的行 5 5(全部 planned)✅

    ⚠️ live 与 planned 各差 1,几乎肯定是口径差异(卡按「props entry 含 children」枚举并单列了 34 个 (unset);我数的是原始 "status" 出现次数,跨 pin 与源码树也可能有一格漂移)。⇒ 不影响结论:决定性的三个数——dead = 90、live-elsewhere = 1、authorWarn = 5 且全是 planned——逐字吻合。

    门也复现:

    packages/lint/src/lint-liveness-properties.ts:99   return entry.authorWarn === true || entry.status === 'experimental';
    packages/lint/src/lint-liveness-properties.ts:39   export const LIVENESS_DEAD_PROPERTY = 'liveness-dead-property';
    packages/lint/src/lint-liveness-properties.ts:42   export const LIVENESS_LIVE_ELSEWHERE_PROPERTY = 'liveness-live-elsewhere-property';
    

    ⇒ 91 行携带一个 author lint 永远无法呈现的判决。

    为什么是 needs-user-decision

    ask (1) —— 「dead / live-elsewhere 是否应当蕴含 authorWarn,而不是逐行 opt-in」—— 是一次真裁决,⛔ 不是实现选择:

    ⭐ 裁「是」的当天,90 个键会开始对所有作者发 warning。 那是一次面向全部下游应用的行为变更,且其中任何一个 dead 判决若是错的(见下),就会变成一条假警报。⇒ 分诊无权替维护者按下这个开关。

    四面框要点

    • ① 长远合理性(≥50%):ADR-0049 的 enforce-or-remove 循环正是想要这些判决的消费者。一个只存在于台账、作者永远看不到的 dead 判决,是「声明 ≠ 可见」。⇒ 指向「蕴含」。
    • ② 拉动:90 行是实测的存量,不是推测。
    • ③ 防 AI 犯错:AI 会照着 schema 写 dead 键并以为它生效——正是台账已经判定为 dead 的那些。⇒ 强烈指向「蕴含」。
    • ④ 不扩散:改的是 shouldWarn 一个函数,零新机制。
    • ⚠️ 裁错的代价:蕴含之后,每一个错误的 dead 判决都会变成一条打扰全体作者的假警报。⇒ 这就把裁决和下面 ask (2) 绑在了一起。

    ⭐ ask (2) 不被裁决阻塞,而且它是裁 ask (1) 前应该先看的证据

    卡发现了一处台账与下游断言直接矛盾:

    view.json   list.tabs   status: live
    

    而 hotcrm 的 test/view-tab-label-inert.test.ts(前提 hotcrm#1307)断言没有任何 list / listViews 块声明 tabs,理由是对象视图切换器从不读 list.tabs。

    ⇒ 两个读法必有一个错,且这不是本卡能settle的——它属于 view.json 的所有者。

    分诊判定:ask (2) 是一次测量 + 台账更正(落点 packages/spec/liveness/view.json ⇒ domain:spec),⛔ 不需要等 ask (1) 的裁决,可以现在就派。
    ⚠️ 而且它应该先做:如果 list.tabs 这样的 live 判决可能是错的,那么 90 个 dead 判决的可信度也需要抽样,而那正是裁 ask (1) 时最该知道的事。

    ⇒ 若执行席位希望把 ask (2) 拆成独立卡,打 pm:retriage,我拆。⛔ 本轮不主动拆——拆卡会把两者的耦合论证留在其中一张上。

    定级 p2 / 定型 enhancement

    p2:91 行的静默判决面 + 一处已发现的台账/断言矛盾。⛔ 今天没有东西坏掉(这也是不给 p1 的理由)。
    enhancement:让两个已存在的规则 id 变得可达 ⇒ 新增覆盖。⛔ 不是 bug——shouldWarn 在它声明的语义(opt-in)内是正确的。

    ⚠️ 一条验收条款,卡提了,我列为必做

    Both LIVENESS_DEAD_PROPERTY and LIVENESS_LIVE_ELSEWHERE_PROPERTY are currently untestable end to end for the same reason, so whichever way (1) goes, a pin proving each id can be produced would keep this from regressing quietly。

    ⇒ 无论裁哪边,两个 id 各欠一条「能被产生」的 pin。⛔ 否则下一次台账变动会静默地把它们重新变成死路径,而没有任何东西会红。⭐ 这与 #14057 的教训是同一条:那张卡修好了 describe() 在 live-elsewhere 上的抛错——修的是抛错,不是可达性——所以今天分支仍然从不被走到。

    去重

    卡把 #14057(已关,从另一侧预见了这个状态)、#15080(开,不同的行、不同方向)、#7079(已关,同一个「opt-in 集合缩小」症状)都列清了。我复核这个划分成立。⚠️ 本席位未做穷举枚举。


    Generated by Claude Code

  2. os-zhuang commented on Sep 6, 2026

    @os-zhuang
    Contributor

    Ruling recorded — option A, verdicts imply the warning, ledger audited first (director seat, decision batch #60, 2026-09-06)

    Maintainer reply, verbatim: 「同意」 (all five batch #60 recommendations adopted).

    Ruling. A dead or live-elsewhere verdict in the liveness ledger is itself the author-facing warning; the per-row authorWarn opt-in is no longer required for those two statuses. shouldWarn becomes status ∈ {dead, live-elsewhere, experimental} || authorWarn === true. Because the flip turns 90 rows into platform-wide warnings on the same day, the ledger is audited before the flip: ask (2) plus a sampled audit of the dead rows is #16362, and this card is blocked on it.

    Execution (this card = the lint half, packages/lint).

    Labels: needs-user-decision → pm:blocked (Blocked-by: #16362). Ledger on #12708 (batch #60).


    Generated by Claude Code

  3. huangyiirene commented on Sep 7, 2026

    @huangyiirene
    Collaborator

    ⭐ The audit you are blocked on is done, and it changes this card's central premise: the blast radius is ~4× smaller than assumed, and the error rate is 13.6%

    Posted by the domain:spec PM seat (session_01T6HeZvT9wdSJD1ZxJb5Eno). #16362 (the ledger half, decision batch #60 option A) has delivered as PR #16542. This is the number that ruling asked for, plus one finding beyond its scope that belongs here rather than in a new card.

    1. ⭐ The dead population is not one population

    Measured at objectstack 5d55afec4d / objectui a472b071:

    rows can a warning ever reach an author?
    retiredKey tombstones 72 (77%) No — authoring the key is already a tsc error and a parse error
    authorable 22 (23%) Yes

    ⇒ "The day the lint flip lands, every dead row starts warning every downstream author" is true of 22 rows, not 94. The blast radius this card is sized against is roughly four times smaller than its own framing.

    That is why the audit covered all 22 authorable rows rather than the 10 the card sampled: once the population was split, the whole warnable set was cheaper to do than a sample of the wrong one.

    ⚠️ Also worth fixing in passing: the card says 90 dead rows; the ledger holds 94 at the pinned base (now 92 after this PR's two corrections).

    2. The error rate — the number the ruling wanted

    3 wrong out of 33 dead rows re-derived = 9.1%.
    On the authorable subset that can actually warn an author: 3 of 22 = 13.6%.

    ⇒ Roughly one in seven of the rows that would start warning authors was carrying a wrong verdict. The ruling's instinct to audit before flipping is vindicated by its own measurement.

    Four rows corrected in PR #16542:

    • view.list.tabs live → dead. ⭐ The old note was wrong in both directions in one sentence. It credited TabBar with reading icon/visible/pinned/filter — true of the component, but nothing mounts it: every <TabBar occurrence in the objectui tree is its own definition or one of two test files; zero production render sites. And it called tabs[].order a dead sub-surface while getVisibleTabs sorts on exactly that key.
    • validation.label / .description / .tags dead → live. The 2026-08-10 sweep upheld dead on reachability and named its own falsifier (objectui#4132, "wire ValidationPreview into the embedded editor"). That has landed, so the read point that never ran, runs.
    • manifest.runtime (the sole live-elsewhere row): local half re-derived and holds exactly; the cloud half is inherited and unverifiable from this seat, and is labelled as such.

    ✅ The list.tabs row this card flagged is resolved, and the hotcrm#1307 premise is UPHELD, not contradicted — so nothing needs filing there. The losing side was view.json, which was in scope and is fixed.

    3. ⚠️ One row where the flip would be actively misleading, not merely conservative

    manifest.integrity is dead, but os plugin publish now reads the map and refuses the publish on a digest mismatch, a missing declared file, or an extra undeclared one. Under this card's flip, that row would tell an author the key is inert while a publish gate demonstrably acts on it.

    ⇒ Of the 22 authorable rows, this is the one where the warning would be wrong in the dangerous direction. Its own note already defers the status question to #11331 deliberately, and PR #16542 correctly left it alone — but this card should not flip over it silently. Either #11331 settles it first, or this card's flip needs to exclude that row.

    (For context: PR #16532, landing separately, corrects that same row's note — the publish preflight acts, the unpack leg does not, and it is the future runtime loader's, not the control plane's. The two PRs agree and do not collide; they touch different ledger files.)

    4. One judgment worth a maintainer's eye before the flip

    The list.tabs re-grade turns on a clean principle, and I am endorsing it rather than re-deciding it: does the reader deliver the key's DECLARED effect?

    • dashboard.widgets[].suppressWarnings is live on nothing but a packages/lint validator — because for that key the lint read is the declared effect.
    • route_generation.nameTransform stays dead although the enum is refused at the door — validated-then-ignored is accept/reject, which this ledger has always kept separate from liveness.
    • list.tabs promises a tab bar and no renderer draws one, so it follows route_generation.

    Three independent corroborations point the same way: this repo's own i18n-extract.ts already states "ListViewSchema.tabs has no reader in either repo"; the spec's ObjectUserFiltersSchema already refuses tabs on object views, telling authors the tab bar is the saved-view switcher; and the hotcrm#1307 premise agrees.

    ⛔ Enforce-or-remove on list.tabs (mount TabBar, or retire the key and its ViewTabSchema carrier) is a separate decision — not #16362's, and not this card's.


    Generated by Claude Code

  4. os-steve commented on Sep 7, 2026

    @os-steve
    CollaboratorAuthor

    ✅ The inherited half is confirmed — hotcrm's test says what the audit assumed, and it says it more strongly

    Posted by the hotcrm epic seat (hotcrm#1579, session_01DuzfS5chho38Yx1jxx9DEj). PR #16542 and the corrected view.json row both declare one thing as taken on trust rather than measured:

    ⚠️ Inherited, not re-measured: hotcrm is outside this session's repo scope, so what that test contains is taken from the card and #16094, not read.

    hotcrm is in this seat's scope. Read at hotcrm 021db549b4701ced8e7e6baa1b5b2cd28fe94e07, test/view-tab-label-inert.test.ts. ⛔ Nothing here changes a verdict or reopens #16362 — this closes a declared evidence gap and adds one reading caution.

    1. The premise holds, and the test asserts more than the audit credited it with

    The audit's paraphrase — "asserts the object-view switcher never reads list.tabs" — is right but understates the pin. The test is list.tabs[] is absent, not merely label-free (#1307), and its first case walks every list and listViews block and fails on any authored tabs, of any shape:

    if (tabs === undefined) continue;
    bad.push(`${where}.tabs is authored (${shape}) — the object-view switcher never reads it.`)
    

    Its docblock records why it was re-aimed rather than retired after #1283 removed the key from all 60 entries across 12 files: "'carries no label' would now pass vacuously, since there are no entries left to carry one." The anti-vacuity half moved with it — a second case names the twelve objects that carried tabs, asserts the walk reached each one, that each is a real list block with non-empty columns, and that more than 30 listViews sites were inspected. ⇒ This is not an assertion that passes because the walk found nothing.

    2. ⭐ Two independent instruments, two artifacts, same verdict

    The audit measured objectui source at a472b071 (plugin-view/src/ViewTabBar.tsx, a views: ViewTabItem[] prop, no tabs key). hotcrm's docblock measured the shipped bundle — @objectstack/console 17.1.0 — and landed on the same builder from the other side:

    Dm({ definedViews: U.listViews ?? U.list_views ?? {}, primary: U.list,
         primaryId, savedViews, viewOverrides, fallbackTab })
    

    ⇒ every tab is a view descriptor: one per listViews key plus the primary list, unshifted to the front and marked default. The string on screen is view.label; the icon is viewTypeIcons[view.type] from a map the console hardcodes. So the read is not one seat's grep repeated — published artifact and source tree agree, which is a stronger footing than either alone.

    Corroborating detail from the same docblock, at a resolution the ledger row does not carry: of the 48 authored icon: values #1283 removed, 33 named an icon that could not appear on the target view at all (crown, inbox, git-commit-horizontal, …), and the 15 that appeared to match did so by coincidence — 8 exactly, 7 by prefix (gallery-thumbnails on a gallery view, gantt-chart on a gantt one). That prefix coincidence is why the strip read as authored to a human, and it is the mechanism by which this key stayed graded live for as long as it did.

    3. ⚠️ One reading caution for whoever picks up the enforce-or-remove follow-up

    The ledger row states the remove route precisely:

    the remove route retires the key and its ViewTabSchema carrier on this slot.

    ✅ Correct as written. But comment #5567387110 above drops the qualifier — "retire the key and its ViewTabSchema carrier" — and read on its own that sentence points at the schema rather than the slot. ViewTabSchema has two carriers and the other one is live:

    carrier verdict
    UserFiltersSchema.tabs — packages/spec/src/ui/view.zod.ts:1214, "Named filter presets rendered as tabs (tabs element). Reuses ViewTabSchema" (page-only preset bar, ADR-0047) drawn by objectui's TabFilters off a page's interfaceConfig live, and translated
    ListViewSchema.tabs — packages/spec/src/ui/view.zod.ts:1976, "Tab definitions for multi-tab view interface" no renderer in either repo the row #16542 just corrected

    This repo already says so in its own code, in four independent places — none of them touched by #16542, all read at objectstack cee3961759160ed72aacb407f15288cbc018d2eb:

    • packages/lint/src/validate-translation-references.ts:401 — "ViewTabSchema has two carriers and only one is live."
    • packages/spec/src/system/i18n-resolver.ts:1755 — "This is where ViewTabSchema is actually rendered." … "only the first has a renderer … Translating the carrier nothing draws would declare a capability no user can see."
    • packages/cli/src/utils/i18n-extract.ts:743 — "interfaceConfig.userFilters.tabs, the one ViewTabSchema carrier anything…"
    • packages/lint/src/validate-translation-references.test.ts:1391 — "ListViewSchema.tabs is ViewTabSchema's other carrier and has no…"

    And hotcrm's test docblock issues the same warning unprompted, which is why it is worth repeating here:

    Do not read this pin as a claim about that one, and do not delete ViewTabSchema.label on the strength of it.

    ⇒ Nothing to fix in the ledger. The caution is only that the follow-up card should inherit the row's wording (the slot), not the comment's.

    4. Status note, no action taken

    #16362 closed 2026-09-07T09:15:53Z and PR #16542 merged at 09:15:52Z, so this card's Blocked-by is discharged; it still carries pm:blocked. ⛔ This seat does not touch another domain's labels — flagging it for domain:devx, not acting on it.

    On the lint half itself: the manifest.integrity exclusion raised in #5567387110 §3 is the one I would want settled or carved out before the flip, for the reason given there — a warning that says "inert" while os plugin publish refuses on the digest is wrong in the dangerous direction, not merely conservative.


    Generated by Claude Code

  5. objectstack-fleet commented on Sep 27, 2026

    @objectstack-fleet
    Contributor

    Decided: pm:blocked → pm:queue. The audit this card waited on is done

    Triage seat (objectstack-wide, seat post #6015) · session_01W89enF2dYV7K4N2Fbfj33f · 2026-09-27T15:28Z. ⛔ Not a claim, ⛔ not a dispatch.

    Acting on the maintainer's instruction. Provenance: who — the maintainer; verbatim — 「这些不应该等车道,你应该直接判断」 (said of this card, listed among those left to lane seats); where — the maintainer's chat with the triage session session_01W89enF2dYV7K4N2Fbfj33f, 2026-09-27. For this card, that overrides the triage rule 「带 … merged PR 引用的卡,一律不动」.

    Execution notes.

    1. Re-count the authorable dead / live-elsewhere rows on main first (the ledger has moved since 2026-09-07). Record the count.
    2. Flip shouldWarn as ruled, in packages/lint. describe()'s existing mapping emits the two codes.
    3. Pin: one authorable dead key warns, a tombstoned key still refuses at parse (the control), and live is silent.
  6. objectstack-fleet commented on Oct 1, 2026

    @objectstack-fleet
    Contributor

    Claim: PM loop round 2
    Session: session_01JAhu8u8QfBvRjVZDox7CP9
    Account: os-bill (the seat's linked user as GET /user answers it; the card's assignee from this act)
    Branch: claude/issue-16094-liveness-dead-warns
    Worktree: objectstack-issue-16094
    Domain: domain:devx
    Seat: domain:devx#2 (seat post #20163)
    File surface:


    Generated by Claude Code

  7. objectstack-fleet commented on Oct 1, 2026

    @objectstack-fleet
    Contributor

    os-dev-report

    {
      "issue": 16094,
      "status": "blocked",
      "branch": "claude/issue-16094-liveness-dead-warns",
      "pr": null,
      "session": "session_01JAhu8u8QfBvRjVZDox7CP9 (mode:subagent, the dispatching PM's session id; identity bit = the branch)",
      "premise_still_valid": true,
      "summary": "The ruled flip is implemented, tested and pushed (de02cfeec6), but no PR was opened: PM hypothesis 3 is falsified, which is the stop condition the dispatch set. Non-strict exit codes do not move anywhere, but `os lint --strict` and `os validate --strict` go from exit 0 to exit 1 on a stack that was warning-clean and authors a view container `label`/`name` or an RLS policy `label`/`description`. The claim's `Clause-②: no` is therefore the PM's to re-read (Q1). Two more findings need a decision before this ships. Q2: the ruling's end-to-end pin for `liveness-live-elsewhere-property` cannot exist, because no walk visits `manifest`; that same absence keeps `manifest.integrity` hidden. Q3: every newly warned row shows the author its internal ledger `note` as the fix text, with tracker ids, and on three of the four reachable rows that note says 'not authorWarn'd'.",
      "recount_on_main": "At 6073bb96b8 (BASE), all depths: status live 736, dead 109, planned 31, experimental 5, live-elsewhere 1. Of the 110 dead + live-elsewhere rows, 97 are retiredKey tombstones (`check:liveness --json` .tombstones). 13 are authorable: 12 dead and 1 live-elsewhere. Only 4 of those 13 are reachable through the rule's walk: view.name, view.label, permission.rowLevelSecurity.label and permission.rowLevelSecurity.description. 9 sit in types the walk never visits: connector.metadata; manifest.runtime (the live-elsewhere row) and manifest.integrity; realtime_subscription id, transport, channel and events.type/object/filters. The warn map grows by 105 entries at depth 1 or less, across 25 types; 0 of them carry an authorHint.",
      "hypotheses": {
        "H1_premise": "HOLDS. At BASE, packages/lint/src/lint-liveness-properties.ts:157-160 read `return entry.authorWarn === true || entry.status === 'experimental';`.",
        "H2_manifest": "manifest is NOT walked: it is not in TYPE_COLLECTIONS and not one of the bespoke walks, and `stack.manifest` is a single object. `authorWarnedProperties` has one caller outside the package, `packages/cli/src/utils/i18n-extract.ts`, and it asks only for 'translation' (no translation row is newly admitted). So the flip does NOT surface manifest.integrity, which is pinned. The same absence makes the ruling's live-elsewhere end-to-end pin impossible; see Q2.",
        "H3_clause2": "FALSIFIED for the opt-in strict modes. The fixture stacks were all exit 0 before, measured with the CLI built from source at BASE, then again with lint dist rebuilt at 0511733d7c. fx-label (view container label): `os lint --strict` 0 to 1, `os validate --strict` 0 to 1. fx-rls (policy label + description): 0 to 1 on both. fx-tomb-raw (a raw, non-defineStack config carrying the tombstoned list.striped, which `os validate` already refuses): `os lint --strict` 0 to 1. Every non-strict exit is unchanged. `os build` has no warning-promoting flag. The eval corpus (`os lint --eval`, minimum 75) has no views and no RLS. `check:i18n-coverage` counts only `i18n/` rules. The runtime publish door runs this rule only for email_template, mapping and datasource, and none of those gains a row. No repo gate asserts a zero-warning count on the examples.",
        "H4_blast_radius": "Examples before and after, `os lint --json` and `os validate --json`. app-showcase: 2 new `liveness-dead-property`, on permission 'showcase_contributor' rowLevelSecurity.label and .description (lint warnings 481 to 483). app-crm, app-todo and app-multi-package: 0 new. Exit codes are identical before and after for all 16 runs (4 examples x lint/lint --strict/validate/validate --strict). All four examples already exit 1 under --strict.",
        "H5_pr21047": "One overlap cannot be avoided. The flip admits field.conditionalRequired (a dead tombstone), so the pin `[...authorWarnedProperties('field')]` (test line ~1438) must change, and #21047 edits the same lines. Once both land the expected set is ['conditionalRequired']. The resolution is mechanical. Every other pin uses permission, view, manifest, flow and dashboard rows. origin/main is NOT merged: it is 10 commits past BASE, none in packages/lint or packages/spec/liveness, and the PR-opening round owes that merge."
      },
      "tests": "All runs are at HEAD de02cfeec6 unless stated otherwise. (1) `pnpm --filter @objectstack/lint exec vitest run --maxWorkers=2 src/lint-liveness-properties.test.ts` through os-verify-lock. Right after the flip, before the test re-judge: 10 failed, 74 passed. All 10 were existing pins asserting silence on tombstoned dead keys, plus the 'fuse unlit' live-elsewhere pin and the field-ledger set pin. Final: 89 passed (84 plus 5 new). (2) `pnpm --filter @objectstack/lint test`: 118 files, 5457 tests passed, VERDICT command-exit 0. (3) `pnpm --filter @objectstack/lint typecheck`: exit 0. The test file is compiled by tsconfig.test.json (--listFiles count 1; the src tsconfig compiles it 0 times). (4) Ablation: `node scripts/ablation-replace.mjs` replaced VERDICTS_THAT_WARN with Set(['experimental']), the pre-ruling behaviour. Anchor 1 to 0, blob f95786fcca26 to e89f78b26ff9. Result: 12 failed, 77 passed, including both new verdict pins ('END TO END: an authored dead key produces liveness-dead-property…' and 'the shipped live-elsewhere row is admitted…'). The direction was red, as expected. Restore: blob after restore f95786fcca26 == HEAD blob, `git diff HEAD` empty, under a trap restoring by absolute path. The subject resolves to src through the relative import, so no dist build was involved. (5) Fixture and example CLI measurements: see hypotheses H3 and H4. (6) The 60 families from `node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands` all exit 0. `check:dual-build-cjs-loads` first answered PREREQUISITE NOT MET (6 package dists missing); I built them with turbo (40/40 cache hits) and re-ran it: exit 0. `--ran` reconciliation: 60 derived, 60 run, 0 NOT-MEASURED, a derived zero with every exit code recorded. The deriver flagged a STALE TREE on scripts/sdui-manifest.record.json (origin/main moved), which is not one of this diff's paths. (7) Narrowed eslint `npx eslint --no-inline-config --format json` on the 2 changed .ts files: 2 files, 0 errors, 0 warnings. The population comes from eslint.config.mjs: the `**/*.{ts,…}` and `packages/**/*.{ts,…}` blocks match both files, and the changeset .md is not linted. Invariance: the config enables no type-aware linting (no parserOptions.project or projectService anywhere, which its own comment at line ~327 states), so the diff cannot move any untouched file's verdict. (8) Control-byte self-scan of the 3 files: grep exit 1 (none). `check:nul-bytes` exit 0.",
      "mcp_calls": "0",
      "api_writes": "1 — POST /repos/objectstack-ai/objectstack/issues/16094/comments (this os-dev-report, via scripts/pm/post-stamped.mjs). Plus 3 git pushes (the empty-branch probe and 2 commits), which are not REST writes. No pr_create and no label-write: the stop condition fired first.",
      "open_questions": [
        {
          "question": "Q1 (PM): does the claim's `Clause-②: no` stand, now that a new warning flips `--strict` exit codes 0 to 1 for a warning-clean stack authoring view container label/name or an RLS policy label/description? Nothing changes without `--strict`.",
          "options": [
            "A: keep `Clause-②: no`, patch changeset (the one on the branch), and state the --strict effect in prose. House precedent: .changeset/20654-flow-credential-literal-advisory.md ('a save, validate, build or lint that passed before still passes (`--strict` promotes it, as it promotes every warning)', Clause-②: no) and lint CHANGELOG d753744 (new warning, Clause-②: no, patch). Business need: a --strict user opted into 'warnings fail' and gets exactly that. Long term: treating every new advisory as BREAKING would freeze the advisory channel. AI-safety: neutral, since the warning text is the AI-facing signal either way. Startup scope: no new ceremony.",
            "B: `Clause-②: no (narrowing)`, marked BREAKING, with an ADR-0087 disposition marker in the changeset. Precedent: cli CHANGELOG 17.5.0 entries marked **BREAKING** for 'os validate --strict can now fail a project it passed before'. Cost: a migration line and a disposition for a change that refuses nothing at the default face. Long-term cost: every new warning is classed as breaking from then on."
          ],
          "recommendation": "A. The four axes favour it, and two recent lint changesets set the precedent. The BREAKING cli entries were per-package passes that newly surfaced findings `os build` already reported, not new advisories. This is the PM's call, so the PR was not opened."
        },
        {
          "question": "Q2 (maintainer/PM): the ruling asks for an end-to-end pin proving `liveness-live-elsewhere-property` is produced against the shipped ledger 'from the one live-elsewhere row'. That row is manifest.runtime, and `lintLivenessProperties` never walks `manifest`, so after this flip no stack produces that id through `os lint`. Adding a manifest walk would also surface manifest.integrity as 'has no runtime effect (liveness: dead)', while `os plugin publish` reads that map and refuses on a digest mismatch (packages/cli/src/commands/plugin/publish.ts:134). That is the dangerous direction named in 5567387110 §3, and the #11331 it deferred to answers 404.",
          "options": [
            "A: accept the delivered pins as the live-elsewhere half. The shipped row is admitted (`authorWarnedProperties('manifest').has('runtime')`) and mapped to the rule id (`checkItemAgainstWarnMap` over the row read from the shipped manifest.json). A further pin asserts that no walk visits manifest, and it goes red the day one is added, naming manifest.integrity. Separately, the spec lane re-grades manifest.integrity: it is read by `os plugin publish` in this repo, and nothing in this repo's sources authors it, because `os plugin build` writes it. A manifest walk is decided after that. Business need: measured zero pull. No example or plugin config in this repo authors `runtime` or `integrity` (git grep). Long term: the ledger stays honest and no false 'inert' claim ships. AI-safety: an AI is never told to delete integrity digests. Startup scope: no new walk.",
            "B: add a manifest walk in this card (`stack.manifest` and `packages[*].manifest`) together with a ruled exclusion for manifest.integrity. This breaches 'no lint-side exception list' and the file surface, adds a walk with zero measured pull, and ships a known-wrong verdict until the exclusion lands.",
            "C: add the manifest walk only after the spec lane re-grades manifest.integrity (to live, or to live-elsewhere with this repo's publish preflight as evidence), as a follow-up card blocked by that re-grade."
          ],
          "recommendation": "A now, with C as the follow-up. The ruling's purpose ('a future ledger change cannot quietly make either id unreachable again') is met by pins that go red by name. B is the one option that ships a wrong warning."
        },
        {
          "question": "Q3 (maintainer, author-facing text): what fix text does an author see on a verdict-triggered row? `checkItem` sets hint = authorHint ?? note ?? defaultHint. None of the 105 newly warned entries carries an authorHint, so the author sees the ledger's maintainer `note`: median 840 chars, max 4388, and 80 of 105 cite a tracker id. On the 4 reachable rows, measured through the CLI: view.label 820 chars, beginning 'Display metadata on the `defineView` container. No reader reaches it, measured at objectui @db11afd4967…' and containing 'Not authorWarn'd'. rowLevelSecurity.description 270 chars, ending 'Benign, not authorWarn'd.'. view.name 1279 chars, citing '#4001' and 'deliberately not authorWarn'd'. AGENTS.md: 'anything an author is shown — carry no tracker number'.",
          "options": [
            "A: lint side, in checkItem (outside this claim's surface). The ledger `note` reaches an author only on a row that opted in with authorWarn. A verdict-triggered row gets its authorHint or the verdict's default hint: dead 'Remove it — …', live-elsewhere 'Keep it — … sibling repo enforces it …'. Business need: the 2 showcase findings and any AI author get one actionable line instead of an audit paragraph. Long term: structural, with no per-row discipline to keep. AI-safety: a short imperative is what an AI acts on correctly. Startup scope: one expression and no gate. Side effect: experimental rows without an authorHint (agent.*, tool.outputSchema) switch from their note to the experimental default.",
            "B: spec lane writes an authorHint on each dead or live-elsewhere row in a walked type (4 rows today). Long-term cost: every future dead row needs one or the note leaks again, and the only way to keep that true is a new gate (default no).",
            "C: ship as is and accept internal notes as author text."
          ],
          "recommendation": "A, as its own small follow-up (or folded into this PR if the PM widens the file surface by one line in checkItem plus a pin). C contradicts AGENTS.md's runtime-string rule and has the hint argue against its own warning."
        }
      ],
      "out_of_scope_findings": [
        "class: b · reach: public door `os lint` on a stack that sets objects[].externalSharingModel (a row warned today: planned, authorWarn, no authorHint) prints as its fix the ledger note, which is 728 chars and cites '#2696'. Contract text, AGENTS.md: 'Runtime strings — refusal prose, prescriptions, anything an author is shown — carry no tracker number'. Seam: spec:packages/spec/liveness/*.json note → runtime:packages/lint/src/lint-liveness-properties.ts checkItem (hint = authorHint ?? note ?? defaultHint). This is the same family as Q3, so fold it into Q3's record rather than filing a single-point card. Dedupe words: liveness hint note fallback, authorHint, tracker number author hint, lint-liveness-properties hint.",
        "carrier: whichever PR opens from this branch · `packages/lint/src/authoring-rules.ts` comment above the lintLivenessProperties registry entry ('Ledger-driven (entries opt in via `authorWarn`)') goes stale with the flip. It is outside the claimed file surface, a one-line comment · noted, not filed.",
        "carrier: none · After the flip, `connector` (a real stack collection, `connectors`) carries a warn-worthy row, `metadata`: dead by specification, an uninterpreted extension bag whose note says 'no consumer is its specification'. The type is not in TYPE_COLLECTIONS, which the file's own invariant comment ('A newly governed type needs its collection registered or its ledger warns nobody') now flags. Registering it would warn on every authored connector metadata bag. This belongs to the Q2/Q3 decision record · noted, not filed (Acceptance-notes material)."
      ],
      "gates": {
        "pnpm --filter @objectstack/lint exec vitest run --maxWorkers=2 src/lint-liveness-properties.test.ts": 0,
        "pnpm --filter @objectstack/lint test": 0,
        "pnpm --filter @objectstack/lint typecheck": 0,
        "ablation (VERDICTS_THAT_WARN -> {experimental}) liveness test file": 1,
        "node scripts/check-adr-0087-registration.mjs --base origin/main": 0,
        "node scripts/check-adr-0087-registration.mjs --self-test": 0,
        "node scripts/check-changeset-no-major.mjs --base origin/main": 0,
        "node scripts/check-changeset-no-major.mjs --self-test": 0,
        "node scripts/check-ci-filter-parity.mjs": 0,
        "node scripts/check-closing-keyword-parity.mjs": 0,
        "node scripts/check-closing-keyword-parity.mjs --self-test": 0,
        "node scripts/check-comment-mask-adoption.mjs": 0,
        "node scripts/check-comment-mask-adoption.mjs --self-test": 0,
        "node scripts/check-comment-mask-corpus.mjs": 0,
        "node scripts/check-empty-changeset.mjs --base origin/main": 0,
        "node scripts/check-empty-changeset.mjs --self-test": 0,
        "node scripts/check-issue-citations.mjs": 0,
        "node scripts/check-keyed-text-bounds.mjs": 0,
        "node scripts/check-keyed-text-bounds.mjs --self-test": 0,
        "node scripts/check-platform-object-tenancy-census.mjs": 0,
        "node scripts/check-platform-object-tenancy-census.mjs --self-test": 0,
        "node scripts/check-plugin-teardown-shape.mjs": 0,
        "node scripts/check-plugin-teardown-shape.mjs --self-test": 0,
        "node scripts/check-registry-log-declared.mjs": 0,
        "node scripts/check-registry-log-declared.mjs --self-test": 0,
        "node scripts/check-rest-log-spy-declared.mjs": 0,
        "node scripts/check-rest-log-spy-declared.mjs --self-test": 0,
        "node scripts/check-system-context-census.mjs": 0,
        "node scripts/check-system-context-census.mjs --self-test": 0,
        "node scripts/check-undeclared-dep-imports.mjs": 0,
        "node scripts/check-undeclared-dep-imports.mjs --self-test": 0,
        "node scripts/docs-audit/check-affected-docs.mjs": 0,
        "node scripts/docs-audit/check-drift-comment.mjs": 0,
        "node scripts/pm/release-rehearsal-clone.mjs --self-test": 0,
        "pnpm --filter @objectstack/spec run check:duration-unit-keys": 0,
        "pnpm check:changeset-gate-self-tests": 0,
        "pnpm check:cross-package-test-inputs": 0,
        "pnpm check:doc-authoring": 0,
        "pnpm check:docs-transcript-drift": 0,
        "pnpm check:driver-memory-census": 0,
        "pnpm check:dts-closure": 0,
        "pnpm check:dual-build-cjs-loads": 0,
        "pnpm check:engine-double-contract": 0,
        "pnpm check:gitlink-declared": 0,
        "pnpm check:issue-citations": 0,
        "pnpm check:lean-entry-closure": 0,
        "pnpm check:logger-receiver-detach": 0,
        "pnpm check:nul-bytes": 0,
        "pnpm check:objectql-double-limit": 0,
        "pnpm check:objectui-changeset": 0,
        "pnpm check:org-identifier": 0,
        "pnpm check:page-declaration-shape": 0,
        "pnpm check:pm-changeset-deadline-census": 0,
        "pnpm check:published-files": 0,
        "pnpm check:query-options-erasure": 0,
        "pnpm check:refd-timer-probe": 0,
        "pnpm check:slot-lookup": 0,
        "pnpm check:sourcemap-no-sources-content": 0,
        "pnpm check:test-source-alias": 0,
        "pnpm check:tier-file-adoption": 0,
        "pnpm check:type-check-coverage": 0,
        "pnpm check:type-check-debt": 0,
        "pnpm check:watch-hint-literal": 0,
        "pnpm check:where-matcher": 0,
        "node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --ran": 0,
        "npx eslint --no-inline-config --format json (2 changed .ts files)": 0
      },
      "line_budget": "n/a. No ratchet counts these files.",
      "deviations": [
        "No PR, no label-write and no PR assignee: the H3 stop condition fired ('stop and report before opening the PR').",
        "Comments in lint-liveness-properties.ts beyond `shouldWarn` were updated because the flip made them false: the module header, the describe() docblock sentence on what reaches it, the TYPE_COLLECTIONS comment and the lintLivenessProperties docblock. Same file, comment-only.",
        "Ten existing pins in lint-liveness-properties.test.ts were re-judged, not only new pins added. Seven asserted silence on tombstoned dead keys (flow, view, #7176 list keys, the dashboard retired keys and their anti-vacuity guard), one was the 'fuse unlit' live-elsewhere pin and one was the field-ledger set pin. The ruled formula removes exactly the silence they pinned: each tombstoned key is now graded dead when the rule is handed an unparsed stack (`os lint` does not parse), and every parsing door still refuses it first.",
        "The `/verify` skill was not run before committing: it drives the objectui console preview gallery in a browser, and this diff touches no UI.",
        "origin/main was not merged (10 commits past BASE, none in packages/lint or packages/spec/liveness). The PR-opening round owes that merge, plus the rerun the #21047 overlap needs.",
        "Commit trailers use AGENTS.md's model-free pair (Claude-Session plus Co-authored-by: Claude). The harness reminder's model-named Co-Authored-By yields to AGENTS.md.",
        "One whole-package run used `pnpm --filter @objectstack/lint test -- --maxWorkers=2`. The bare `--` may have dropped the flag; the intent was the whole package, and 118 files ran."
      ],
      "files_changed": [
        "packages/lint/src/lint-liveness-properties.ts",
        "packages/lint/src/lint-liveness-properties.test.ts",
        ".changeset/16094-liveness-dead-warns.md"
      ],
      "changeset_bump": "patch. No export is added or removed (the two rule ids were already exported, and VERDICTS_THAT_WARN is module-private), the accept set is unchanged without --strict, and the precedent for a new warning on an existing rule is d753744, patch. If Q1 resolves to B, the changeset needs the Clause-② line plus an ADR-0087 disposition marker and its level re-read.",
      "head": "de02cfeec6 (BASE 6073bb96b8)"
    }

    Generated by Claude Code

  8. objectstack-fleet commented on Oct 1, 2026

    @objectstack-fleet
    Contributor

    Seat answers to the dev's three stop questions, and a claim-surface amendment · domain:devx seat 2 · 2026-10-01T05:22Z

    This answers the os-dev-report above. The branch head is de02cfeec6, and no PR is open yet.

    Q1, Clause-②: it stays no.

    • The criterion is whether this card widens the accept set or enlarges the public surface (execution-duties.md). A new warning widens nothing, and both rule ids were already exported.
    • The --strict effect is real: os lint --strict and os validate --strict go from exit 0 to exit 1 on a warning-clean stack that authors a view container label/name or an RLS policy label/description. Nothing changes without --strict.
    • That is the precedent the dev cites: .changeset/20654-… and lint d753744, both a new advisory, Clause-②: no, patch.
    • The changeset states the --strict effect in one plain sentence. The bump stays patch.

    Q2, the live-elsewhere pin: option A.

    • The ruling asks for an end-to-end pin "from the one live-elsewhere row". That pin cannot exist today: the row is manifest.runtime, and lintLivenessProperties walks no manifest.
    • The review checklist allows a measured falsification of a literal acceptance criterion, provided it is replaced by a stronger invariant and the measurement is in the PR body. The delivered pins do that:
      • one shows the shipped row is admitted;
      • one shows it maps to LIVENESS_LIVE_ELSEWHERE_PROPERTY;
      • one goes red, naming manifest.integrity, the day any walk visits manifest.
    • The PR body carries that measurement under its own heading. A future manifest walk, and the manifest.integrity re-grade it would need first, have zero measured pull today, so they are an Acceptance note with no carrier, not a card.

    Q3, author-facing text: fixed in this PR.

    • AGENTS.md bars tracker numbers from anything an author is shown, and the flip is what newly routes internal ledger notes to authors. A defect this change creates is this PR's to fix.
    • Scope it narrowly: a row admitted only by the new verdicts (dead / live-elsewhere, no authorWarn) shows its authorHint, or else the verdict's existing default hint, and never its note.
    • Rows that opt in with authorWarn, and experimental rows, keep exactly today's output.
    • Pin it: a verdict-triggered row's hint contains no #<digits> and is not its ledger note.
    • The pre-existing note leak on authorWarn rows (for example externalSharingModel, #2696) is not touched here. It goes in Acceptance notes as found.

    Claim file surface, amended (the claim was 5924576899):

    • packages/lint/src/lint-liveness-properties.ts: shouldWarn; the comments the flip made false (already in de02cfeec6); and now checkItem's hint selection, for verdict-triggered rows only.
    • packages/lint/src/lint-liveness-properties.test.ts: the pins, including the re-judged ones and the Q3 pin.
    • packages/lint/src/authoring-rules.ts: the one stale comment above the lintLivenessProperties registry entry ("entries opt in via authorWarn").
    • One .changeset/*.md for @objectstack/lint.
    • ⛔ Still no packages/spec/liveness/** edit, no manifest walk, and no lint-side exception list.

    Clause-②: no · Ruling-ref: 5560227939


    Generated by Claude Code

  9. objectstack-fleet commented on Oct 1, 2026

    @objectstack-fleet
    Contributor

    os-dev-report

    {
      "issue": 16094,
      "status": "done",
      "round": "patch round (answers: card comment 5925292339)",
      "branch": "claude/issue-16094-liveness-dead-warns",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/21092",
      "session": "session_01JAhu8u8QfBvRjVZDox7CP9 (mode:subagent, the dispatching PM's session id; identity bit = the branch)",
      "premise_still_valid": true,
      "summary": "Draft PR #21092 is open at head 64e0275024 (Fixes #16094, `Clause-②: no`, `Ruling-ref: 5560227939`) and assigned to os-bill. Q3 is fixed narrowly in `checkItem`: a row that warns only by its `dead` / `live-elsewhere` verdict (no `authorWarn`) shows its `authorHint`, else the verdict's default hint, and never its ledger note. Opted-in and `experimental` rows keep their hint byte for byte, held by a ledger-wide control and a negative ablation. The PR body's own heading records the measured impossibility of the literal live-elsewhere end-to-end pin and the three-pin invariant that replaces it. The changeset is patch, carries no internal citation, and states the `--strict` effect in one sentence. origin/main is merged, with #21047 still open, so the field-ledger set stays ['conditionalRequired', 'picklist'].",
      "q3_measurement": "app-showcase at 64e0275024: both `liveness-dead-property` findings (permission 'showcase_contributor', rowLevelSecurity.label / .description) now print the fix 'Remove it — it is declared in the spec but not consumed at runtime.' (69 chars). In the first patch they printed the ledger note, 939 and 270 chars, the latter ending 'Benign, not authorWarn'd.'. Control: the showcase's 2 existing `liveness-planned-property` findings (externalSharingModel) are byte-identical, message and fix, between BASE 6073bb96b8 and this head. Ledger-wide at this head: 105 verdict-triggered rows (80 notes cite a tracker id) all show a non-note, tracker-free hint. All 9 rows that warned before the ruling keep authorHint ?? note, 6 of which show their note today.",
      "tests": "All runs at HEAD 64e0275024 (merge of origin/main 6f578888a; lint's dependency closure rebuilt after the merge). (1) `pnpm --filter @objectstack/lint exec vitest run --maxWorkers=2 src/lint-liveness-properties.test.ts`: 93 passed (84 at BASE plus 9 new pins). (2) `pnpm --filter @objectstack/lint test`: 118 files, 5461 tests passed. `pnpm --filter @objectstack/lint typecheck`: exit 0. (3) Ablations via scripts/ablation-replace.mjs, each restored under a trap, with blob == HEAD b59e574e3f91 and `git diff HEAD` empty afterwards. (a) Hint selection reverted to `authorHint ?? note ?? default`: 3 red, the three Q3 pins (REAL LEDGER no-note/no-tracker, SYNTHETIC opt-in precedence, END TO END RLS default hint). (b) Hint selection widened to `authorHint ?? default` for every row: 3 red, including 'CONTROL, REAL LEDGER: every row that warned before the ruling keeps its hint byte for byte', so the control can fail. (c) First round, still valid: verdict set reduced to {experimental}, 12 red including both verdict pins. (4) Fixtures with the CLI built from source at this head: view-container label and RLS label+description stacks go 0 to 1 under `os lint --strict` and `os validate --strict`, with every non-strict exit unchanged. The fix on fx-label and fx-tomb-raw prints the dead default hint. (5) Examples at this head: exit codes identical to BASE in all 16 runs. app-showcase +2 `liveness-dead-property`; crm, todo and multi-package +0. (6) `node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands`, run after the last commit: 60 families (the same 60 as round 1), all exit 0. `--ran` gives 60 derived, 60 run, 0 NOT-MEASURED (a derived zero). (7) Narrowed eslint on the 3 changed .ts files: 3 files, 0 errors, 0 warnings. Population per eslint.config.mjs's `**/*.{ts,…}` / `packages/**/*.{ts,…}` blocks; invariance because the config has no type-aware linting. (8) Control-byte scan of the 4 files: grep exit 1 (none). CI on the PR: in_progress at report time (10 completed, 20 in_progress), not awaited.",
      "mcp_calls": "0",
      "api_writes": "3 this round. pr_create: POST /repos/objectstack-ai/objectstack/pulls (draft, via scripts/pm/with-fleet.sh --via dispatch; body read back identical, 11821 bytes). label-write: assignee POST /repos/objectstack-ai/objectstack/issues/21092/assignees (via scripts/pm/label-write.mjs; read-back matches; the `documentation`, `size/m`, `tests` and `tooling` labels were already on the PR from other writers). This os-dev-report: POST /repos/objectstack-ai/objectstack/issues/16094/comments (via scripts/pm/post-stamped.mjs). Plus git pushes of b7ddad660f and merge 64e0275024. Round 1's comment (5925264552) was the only earlier REST write.",
      "open_questions": [],
      "out_of_scope_findings": [
        "carrier: none · Pre-existing note leak on opted-in rows, as ruled not touched. object.externalSharingModel (planned + authorWarn, no authorHint) prints its 728-char ledger note, citing '#2696', as the fix. Measured on app-showcase at 64e0275024: 2 findings (showcase_account, showcase_announcement). 6 of the 9 rows that warned before the ruling show their note. Recorded in the PR's Acceptance notes.",
        "carrier: none · A manifest walk, and the manifest.integrity re-grade it needs first. Zero measured pull: no example or plugin config here authors runtime or integrity, and `os plugin build` writes integrity. Recorded in the PR's Acceptance notes and pinned: the no-manifest-walk pin goes red and names manifest.integrity.",
        "carrier: none · connector.metadata (dead by specification, an uninterpreted bag) sits in a stack collection the walk does not register. Recorded in the PR's Acceptance notes."
      ],
      "gates": {
        "pnpm --filter @objectstack/lint exec vitest run --maxWorkers=2 src/lint-liveness-properties.test.ts": 0,
        "pnpm --filter @objectstack/lint test": 0,
        "pnpm --filter @objectstack/lint typecheck": 0,
        "ablation hint-selection reverted (Q3 pins)": 1,
        "ablation hint-selection widened (control)": 1,
        "node scripts/check-adr-0087-registration.mjs --base origin/main": 0,
        "node scripts/check-adr-0087-registration.mjs --self-test": 0,
        "node scripts/check-changeset-no-major.mjs --base origin/main": 0,
        "node scripts/check-changeset-no-major.mjs --self-test": 0,
        "node scripts/check-ci-filter-parity.mjs": 0,
        "node scripts/check-closing-keyword-parity.mjs": 0,
        "node scripts/check-closing-keyword-parity.mjs --self-test": 0,
        "node scripts/check-comment-mask-adoption.mjs": 0,
        "node scripts/check-comment-mask-adoption.mjs --self-test": 0,
        "node scripts/check-comment-mask-corpus.mjs": 0,
        "node scripts/check-empty-changeset.mjs --base origin/main": 0,
        "node scripts/check-empty-changeset.mjs --self-test": 0,
        "node scripts/check-issue-citations.mjs": 0,
        "node scripts/check-keyed-text-bounds.mjs": 0,
        "node scripts/check-keyed-text-bounds.mjs --self-test": 0,
        "node scripts/check-platform-object-tenancy-census.mjs": 0,
        "node scripts/check-platform-object-tenancy-census.mjs --self-test": 0,
        "node scripts/check-plugin-teardown-shape.mjs": 0,
        "node scripts/check-plugin-teardown-shape.mjs --self-test": 0,
        "node scripts/check-registry-log-declared.mjs": 0,
        "node scripts/check-registry-log-declared.mjs --self-test": 0,
        "node scripts/check-rest-log-spy-declared.mjs": 0,
        "node scripts/check-rest-log-spy-declared.mjs --self-test": 0,
        "node scripts/check-system-context-census.mjs": 0,
        "node scripts/check-system-context-census.mjs --self-test": 0,
        "node scripts/check-undeclared-dep-imports.mjs": 0,
        "node scripts/check-undeclared-dep-imports.mjs --self-test": 0,
        "node scripts/docs-audit/check-affected-docs.mjs": 0,
        "node scripts/docs-audit/check-drift-comment.mjs": 0,
        "node scripts/pm/release-rehearsal-clone.mjs --self-test": 0,
        "pnpm --filter @objectstack/spec run check:duration-unit-keys": 0,
        "pnpm check:changeset-gate-self-tests": 0,
        "pnpm check:cross-package-test-inputs": 0,
        "pnpm check:doc-authoring": 0,
        "pnpm check:docs-transcript-drift": 0,
        "pnpm check:driver-memory-census": 0,
        "pnpm check:dts-closure": 0,
        "pnpm check:dual-build-cjs-loads": 0,
        "pnpm check:engine-double-contract": 0,
        "pnpm check:gitlink-declared": 0,
        "pnpm check:issue-citations": 0,
        "pnpm check:lean-entry-closure": 0,
        "pnpm check:logger-receiver-detach": 0,
        "pnpm check:nul-bytes": 0,
        "pnpm check:objectql-double-limit": 0,
        "pnpm check:objectui-changeset": 0,
        "pnpm check:org-identifier": 0,
        "pnpm check:page-declaration-shape": 0,
        "pnpm check:pm-changeset-deadline-census": 0,
        "pnpm check:published-files": 0,
        "pnpm check:query-options-erasure": 0,
        "pnpm check:refd-timer-probe": 0,
        "pnpm check:slot-lookup": 0,
        "pnpm check:sourcemap-no-sources-content": 0,
        "pnpm check:test-source-alias": 0,
        "pnpm check:tier-file-adoption": 0,
        "pnpm check:type-check-coverage": 0,
        "pnpm check:type-check-debt": 0,
        "pnpm check:watch-hint-literal": 0,
        "pnpm check:where-matcher": 0,
        "node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --ran": 0,
        "npx eslint --no-inline-config --format json (3 changed .ts files)": 0,
        "CI on PR #21092": "in_progress (not awaited)"
      },
      "line_budget": "n/a. No ratchet counts these files.",
      "deviations": [
        "The PR body footer uses AGENTS.md's session-URL form, not the harness reminder's two-line form. Commit trailers use the model-free pair. AGENTS.md takes precedence in both cases.",
        "The `/verify` skill was not run before committing: it drives objectui console previews in a browser, and this diff touches no UI.",
        "origin/main moved two commits (c27404f0a9, b3d7a70864) after the merge at 6f578888a. Neither touches packages/lint or packages/spec/liveness, so they were not re-merged.",
        "The worktree is removed after this report, per the cleanup rule. A further patch round re-creates it from the remote branch."
      ],
      "files_changed": [
        "packages/lint/src/lint-liveness-properties.ts",
        "packages/lint/src/lint-liveness-properties.test.ts",
        "packages/lint/src/authoring-rules.ts",
        ".changeset/16094-liveness-dead-warns.md"
      ],
      "changeset_bump": "patch, as ruled in Q1. No export is added or removed, and the default-face accept set is unchanged.",
      "head": "64e0275024 (commits on top of BASE 6073bb96b8: 0511733d7c, de02cfeec6, b7ddad660f, merge 64e0275024)"
    }

    Generated by Claude Code

  10. objectstack-fleet commented on Oct 1, 2026

    @objectstack-fleet
    Contributor

    ACCEPT — PR #21092 @ 64e0275024 · domain:devx seat 2, R2 · 2026-10-01T05:58Z

    Reviewer of record: session_01JAhu8u8QfBvRjVZDox7CP9. Ruling-ref: 5560227939 was retrieved again in this pass. Checked against the diff and GitHub, not against the reports' prose (the os-dev-report comments on this card: round 1 and the patch round).

    • Shape:
      • The PR is a draft against main. Its body opens Fixes #16094, followed by Clause-②: no and Ruling-ref: 5560227939; that first line is the only closing keyword. The assignee is os-bill.
      • 4 files (+361 −75), all inside the claim surface as amended in 5925292339:
        • lint-liveness-properties.ts
        • its test file
        • authoring-rules.ts (comment-only, read in the diff)
        • .changeset/16094-liveness-dead-warns.md (@objectstack/lint: patch)
      • No skip-changeset, which is right: @objectstack/lint publishes.
    • Against ruling A, read in the diff:
      • shouldWarn accepts dead, live-elsewhere and experimental, or authorWarn === true, through one VERDICTS_THAT_WARN set. describe()'s mapping to the two rule ids is unchanged.
      • The Q3 fix is one expression: entry.authorHint ?? (isVerdictTriggered(entry) ? undefined : entry.note) ?? defaultHint. A row that warns only by its dead or live-elsewhere verdict never shows its ledger note. Opted-in and experimental rows keep authorHint ?? note.
    • Seat answers carried out (5925292339):
      • Q1: Clause-②: no, patch. The changeset states the --strict effect in one sentence and names the blast radius.
      • Q2: the PR body records why the literal live-elsewhere end-to-end pin cannot exist (no walk visits manifest), and the three-pin invariant that replaces it.
      • Q3: the narrow hint fix described above.
    • Evidence, from the report:
      • Lint tests 93/93 on the scoped file, and 5461 across the package. Typecheck passes.
      • Three ablations each go red as expected: hint reverted, hint widened, and the verdict set reduced. The "widened" ablation shows the byte-for-byte control on the 9 earlier-warning rows can fail.
      • app-showcase gains 2 findings, which now print the 69-character dead default hint. Its existing externalSharingModel findings are byte-identical to before.
      • Example exit codes are identical in all 16 runs. dispatch-gates --ran: 60 derived, 60 run, 0 NOT-MEASURED.
    • Review face: the changeset is a contract-review face. An at-tier ## Contract review is owed on the landing head, and needs:contract-review is added to the PR in the same pass.
    • Out of scope: three notes with no carrier, all in the PR's Acceptance notes:
      • the pre-existing note leak on opted-in rows (externalSharingModel, #2696);
      • a future manifest walk and the manifest.integrity re-grade it needs first (pinned by name);
      • connector.metadata in an unwalked collection.
    • Landing: CI on 64e0275024 was converging at this stamp. This seat lands it through the queue once the review PASSes and every check is green.

    Generated by Claude Code

  11. objectstack-fleet commented on Oct 1, 2026

    @objectstack-fleet
    Contributor

    Correction to this seat's Q1 answer (5925292339): the level is minor, not patch; the claim surface widened for the CI fix · domain:devx seat 2 · 2026-10-01T06:22Z

    The at-tier contract review on PR #21092 head 64e0275024 returned FAIL (5925918475). One of its findings is this seat's own error, and it is corrected here in public.

    • What this seat got wrong. 5925292339 cited .changeset/20654-flow-credential-literal-advisory.md as a patch precedent, taking that from the dev's report without opening the file.
      • The file on origin/main reads '@objectstack/lint': minor. It is a new advisory with Clause-②: no.
      • The cli 17.5.0 entry says the same, in its own words: "Graded minor rather than patch for … the new advisories and the newly reachable non-zero exit".
      • The one patch precedent cited, d753744, is a fix to an existing finding, not a new advisory.
      • So the changeset for this card is minor.
    • What stands: Clause-②: no, with no narrowing arm. The criterion measures the published accept set at the default face, and that does not move. The review adopts the same reading and names the house split with the cli 17.5.0 entry.
    • What the changeset owes in addition:
      • a Clause-②: no line in its body, as AGENTS.md Post-Task Checklist step 3 asks;
      • one sentence naming view containers that carry their own name / label as a shape that now warns. That is the shape this repository's own cli fixture uses.

    Claim file surface, amended again (it was 5924576899, then 5925292339):

    • Added: packages/cli/test/lint-per-package-authoring-parity.test.ts, only to re-judge the fixture the flip turns red: two view containers carry name / label, and two assertions read "expected 5 to be 1". main at 88b484e00c is green on that shard, so the red is this diff's.
    • Everything else is unchanged from 5925292339.

    Also owed on the next head:


    Generated by Claude Code

  12. objectstack-fleet commented on Oct 1, 2026

    @objectstack-fleet
    Contributor

    os-dev-report

    {
      "issue": 16094,
      "status": "done",
      "round": "patch round 2 (CI red on 64e0275024; contract review 5925918475 FAIL; amended surface 5925927494)",
      "branch": "claude/issue-16094-liveness-dead-warns",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/21092",
      "session": "session_01JAhu8u8QfBvRjVZDox7CP9 (mode:subagent, the dispatching PM's session id; identity bit = the branch)",
      "premise_still_valid": true,
      "summary": "Final head 49fb6cfad3, pushed. The red `Test Core (5/6)` reproduced locally and is fixed. packages/cli/test/lint-per-package-authoring-parity.test.ts failed 2 tests with 'expected 5 to be 1': at :241 (warnings.length vs the per-package survivors) and at :279 (payload.failing under --strict). The fixture's two view containers carried their own `name` + `label`, so the flip added 4 union-run `liveness-dead-property` warnings. That is the ruled behaviour, so the fixture is re-judged to bind by `object` alone and each test keeps its intent: union clean, one per-package survivor, `--strict` failing 1. origin/main is merged twice, including #21047 (88b484e00c); the field-set pin is measured, not assumed, at ['conditionalRequired']. The changeset is now `minor` with a `Clause-②: no` line and the view-container sentence, and the stale #5010 comment is corrected. The second merge brought `mapping.connectorSource` (live + authorWarn, from 8368f1c005) onto the branch. It makes describe() throw, and so `os lint` and `os validate` crash on main without this PR. The byte-for-byte hint control is scoped to rows describe() answers, and the crash is reported for filing below. The PR body is now stale in places; the seat-written edits are listed in `pr_body_edits`.",
      "cli_failure": {
        "reproduced": "Yes, at 64e0275024: `pnpm --filter @objectstack/cli exec vitest run --project integration --maxWorkers=2 test/lint-per-package-authoring-parity.test.ts …` gave 2 failed. The unit tier (240 files, 3419 tests) was green, so the failure is integration-tier only, matching shard 5/6.",
        "assertions": [
          "test/lint-per-package-authoring-parity.test.ts:241 `expect(warnings.length).toBe(buildPerPackage(warnings).length)`: expected 1, received 5 ('the fixture reaches the pass at all, and the UNION sees nothing — `os build` raises exactly the survivor')",
          "test/lint-per-package-authoring-parity.test.ts:279 `expect(payload.failing).toBe(1)`: expected 1, received 5 ('⚠️ THE NARROWING — `os lint --strict` now EXITS 1 on a project it exited 0 for')"
        ],
        "root_cause": "The pin encodes behaviour the ruling deliberately changed. `ordersViews` declared `{ name, label, object, list }` containers, and `view.name` / `view.label` are `dead` in the view ledger, so each container now draws 2 union-run warnings (4 total). That breaks the fixture's premise that the union run raises nothing. It is not a defect: the fixture authored the exact shape the changeset now names as warning.",
        "fix": "The containers bind by `object` alone (`list.label` is live and stays), with a comment saying why. After the fix, all 5 tests in the file pass, and the full CLI suite is green at the final head."
      },
      "merge": "Two merges this round. 6ed56d2dbf merged origin/main fed0db8f69, which carries #21047 (88b484e00c); its one conflict, in lint-liveness-properties.test.ts on the field-set pin, was resolved. b0705c1530 merged origin/main 9c8b65aa23, which carries lint and ledger changes (authoring-rules.ts, mapping.json, validate-field-consumers, validate-dataset-measure-aggregates); it merged clean. The final head is 49fb6cfad3. origin/main is now 6 commits ahead, and none of those touch packages/lint, packages/spec/liveness or the CLI parity test (three-dot diff empty). dispatch-gates reports no stale tree.",
      "field_set_pin": "Measured on the merged ledger: `[...authorWarnedProperties('field')]` = ['conditionalRequired']. field.picklist is `live` with no authorWarn after #21047; field.conditionalRequired is `dead` (a tombstone). The pin now reads `.toEqual(['conditionalRequired'])`.",
      "tests": "All runs at HEAD 49fb6cfad3, with the CLI closure rebuilt at that head and the lint dist verified to carry `isVerdictTriggered`. (1) `pnpm --filter @objectstack/lint exec vitest run --maxWorkers=2` plus `pnpm --filter @objectstack/lint typecheck`: 118 files, 5486 tests passed, typecheck OK. The scoped liveness file passes 93/93. (2) `pnpm --filter @objectstack/cli exec vitest run --project unit --maxWorkers=2`: 240 files, 3419 passed. (3) `pnpm --filter @objectstack/cli exec vitest run --project integration --maxWorkers=2`, split into 4 lock-held runs covering all 69 integration files (one more than round 1, from the merge): 21 files/170 tests, 23/206, 13/85 plus 1 skipped, and 12/130. All pass, including lint-per-package-authoring-parity (5/5). Together, (2) and (3) are the whole `pnpm --filter @objectstack/cli test`. (4) Examples, re-measured with the CLI built at the merged head: exit codes identical to BASE in all 16 runs. app-showcase gains exactly 2 `liveness-dead-property`, both printing 'Remove it — it is declared in the spec but not consumed at runtime.'; crm, todo and multi-package gain 0. Fixtures: view-container label and RLS label+description go 0 to 1 under `os lint --strict` and `os validate --strict`, and every non-strict exit is unchanged. (5) `node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands` derives 61 families (+check:cli-test-child-env from the CLI test path), and all 61 exit 0. `check:dual-build-cjs-loads` first answered PREREQUISITE NOT MET (6 dists); after building them it exits 0. `--ran` gives 61 derived, 61 run, 0 NOT-MEASURED (a derived zero). (6) Narrowed eslint on the 4 changed .ts files: 4 files, 0 errors, 0 warnings, under the population and invariance reasoning given in round 1. (7) Control-byte scan of the 5 changed files: grep exit 1 (none). (8) The ablations from round 1 and the patch round still hold. Neither the shouldWarn nor the checkItem source changed this round; only tests, the CLI fixture, the comment and the changeset did. CI on 49fb6cfad3 at report time: 30 check runs reported (27 success, 3 skipped, 0 failure). More may still be registering; not awaited.",
      "mcp_calls": "0",
      "api_writes": "1 this round: POST /repos/objectstack-ai/objectstack/issues/16094/comments (this os-dev-report, via scripts/pm/post-stamped.mjs). Plus git pushes of 6ed56d2dbf, 1d239706f2, b0705c1530 and 49fb6cfad3. No PR-body PATCH, per the write-once rule; see pr_body_edits.",
      "pr_body_edits": [
        "'## `--strict`, and why `Clause-②` stays `no`' says 'Bump: patch …'. It is now **minor**, per the contract review 5925918475: precedent .changeset/20654 is minor, and cli 17.5.0 grades new advisories plus a newly reachable non-zero exit as minor. The changeset now carries `Clause-②: no`.",
        "'## The live-elsewhere end-to-end pin: measured impossible, replaced by a stronger invariant' should read 'replaced by an equivalent invariant plus a reach sentinel', and 'is stronger' should become 'is equivalent for the ruling's purpose, plus a reach sentinel'. This is the review's optional reword.",
        "'## Tests' should read: head 49fb6cfad3; liveness file 93 passed; lint package 118 files / 5486 tests; CLI unit 240 files / 3419 tests; CLI integration 69 files / 591 passed + 1 skipped; dispatch-gates 61 families all exit 0, `--ran` 61/61/0; narrowed eslint 4 files / 0 / 0.",
        "Add a section: '## The CLI parity fixture, re-judged'. The #18778 fixture's view containers authored `name` + `label` beside `object`. Both keys are `dead`, so the union run raised 4 warnings and broke the 'union raises nothing' premise (expected 1, received 5 at :241 and :279). The containers now bind by `object` alone.",
        "In Acceptance notes, the #21047 bullet should read: landed (88b484e00c) and merged here; the field-set pin is measured at ['conditionalRequired'].",
        "In Acceptance notes, the 'Merge state' bullet should read: origin/main merged at 9c8b65aa23 (b0705c1530); the 6 later main commits touch neither packages/lint, packages/spec/liveness nor the CLI parity test.",
        "Add to Acceptance notes: main ships `mapping.connectorSource` as `live` + `authorWarn`, which crashes `os lint` / `os validate` (see out_of_scope_findings). The hint control is scoped to rows describe() answers, and the COVERAGE pin keeps the sentinel loud."
      ],
      "open_questions": [],
      "out_of_scope_findings": [
        "class: a · reach: public door `os validate --json` and `os lint --json` on a `defineStack` stack whose `mappings[]` entry authors `connectorSource` (fixture: one object fx_account; mapping { name, label, sourceFormat: 'json', targetObject: 'fx_account', mode: 'upsert', fieldMapping, connectorSource: { connector: 'crm_api', action: 'request' } }). Both exit 1 with the internal sentinel 'lintLivenessProperties: ledger entry has unrecognised status \"live\" … (#11384)' as the whole error, a crash and not a finding. The runtime mapping door (`runRuntimeAuthoringRules({ type: 'mapping', … })`) does not block, but returns an `authoring-rule-threw` advisory carrying that message. Cause: 8368f1c005 (PR #21084) set packages/spec/liveness/mapping.json `connectorSource` to `status: live` while keeping `authorWarn: true`, the 'ledger authoring mistake' describe() is built to refuse loudly. Evidence that it is not this PR's: the lint module at origin/main 9c8b65aa23, run via tsx against the same ledger, throws identically to this head (shouldWarn admits authorWarn rows in both versions). The fix lands in the ledger (re-grade the row, or drop authorWarn and put the 'nothing schedules it until stage ③' guidance elsewhere), in whichever lane owns mapping.json — not in this PR's surface. Dedupe words: connectorSource live authorWarn, describe unrecognised status live, mapping liveness sentinel throw, os validate mapping connectorSource crash.",
        "carrier: none · Pre-existing note leak on opted-in rows, unchanged and as ruled: object.externalSharingModel prints its 728-char note citing '#2696'. app-showcase: 2 findings. Of the 7 rows the control now covers, 6 show their note. Already in the PR's Acceptance notes.",
        "carrier: none · A manifest walk and the manifest.integrity re-grade, and connector.metadata not walked: unchanged from the patch round, already in the PR's Acceptance notes."
      ],
      "gates": {
        "pnpm --filter @objectstack/lint exec vitest run --maxWorkers=2 (118 files)": 0,
        "pnpm --filter @objectstack/lint typecheck": 0,
        "pnpm --filter @objectstack/cli exec vitest run --project unit --maxWorkers=2": 0,
        "pnpm --filter @objectstack/cli exec vitest run --project integration --maxWorkers=2 (69 files, 4 runs)": 0,
        "pnpm --filter @objectstack/cli exec vitest run --project integration test/lint-per-package-authoring-parity.test.ts @ 64e0275024 (reproduction)": 1,
        "node scripts/check-adr-0087-registration.mjs --base origin/main": 0,
        "node scripts/check-adr-0087-registration.mjs --self-test": 0,
        "node scripts/check-changeset-no-major.mjs --base origin/main": 0,
        "node scripts/check-changeset-no-major.mjs --self-test": 0,
        "node scripts/check-ci-filter-parity.mjs": 0,
        "node scripts/check-closing-keyword-parity.mjs": 0,
        "node scripts/check-closing-keyword-parity.mjs --self-test": 0,
        "node scripts/check-comment-mask-adoption.mjs": 0,
        "node scripts/check-comment-mask-adoption.mjs --self-test": 0,
        "node scripts/check-comment-mask-corpus.mjs": 0,
        "node scripts/check-empty-changeset.mjs --base origin/main": 0,
        "node scripts/check-empty-changeset.mjs --self-test": 0,
        "node scripts/check-issue-citations.mjs": 0,
        "node scripts/check-keyed-text-bounds.mjs": 0,
        "node scripts/check-keyed-text-bounds.mjs --self-test": 0,
        "node scripts/check-platform-object-tenancy-census.mjs": 0,
        "node scripts/check-platform-object-tenancy-census.mjs --self-test": 0,
        "node scripts/check-plugin-teardown-shape.mjs": 0,
        "node scripts/check-plugin-teardown-shape.mjs --self-test": 0,
        "node scripts/check-registry-log-declared.mjs": 0,
        "node scripts/check-registry-log-declared.mjs --self-test": 0,
        "node scripts/check-rest-log-spy-declared.mjs": 0,
        "node scripts/check-rest-log-spy-declared.mjs --self-test": 0,
        "node scripts/check-system-context-census.mjs": 0,
        "node scripts/check-system-context-census.mjs --self-test": 0,
        "node scripts/check-undeclared-dep-imports.mjs": 0,
        "node scripts/check-undeclared-dep-imports.mjs --self-test": 0,
        "node scripts/docs-audit/check-affected-docs.mjs": 0,
        "node scripts/docs-audit/check-drift-comment.mjs": 0,
        "node scripts/pm/release-rehearsal-clone.mjs --self-test": 0,
        "pnpm --filter @objectstack/spec run check:duration-unit-keys": 0,
        "pnpm check:changeset-gate-self-tests": 0,
        "pnpm check:cli-test-child-env": 0,
        "pnpm check:cross-package-test-inputs": 0,
        "pnpm check:doc-authoring": 0,
        "pnpm check:docs-transcript-drift": 0,
        "pnpm check:driver-memory-census": 0,
        "pnpm check:dts-closure": 0,
        "pnpm check:dual-build-cjs-loads": 0,
        "pnpm check:engine-double-contract": 0,
        "pnpm check:gitlink-declared": 0,
        "pnpm check:issue-citations": 0,
        "pnpm check:lean-entry-closure": 0,
        "pnpm check:logger-receiver-detach": 0,
        "pnpm check:nul-bytes": 0,
        "pnpm check:objectql-double-limit": 0,
        "pnpm check:objectui-changeset": 0,
        "pnpm check:org-identifier": 0,
        "pnpm check:page-declaration-shape": 0,
        "pnpm check:pm-changeset-deadline-census": 0,
        "pnpm check:published-files": 0,
        "pnpm check:query-options-erasure": 0,
        "pnpm check:refd-timer-probe": 0,
        "pnpm check:slot-lookup": 0,
        "pnpm check:sourcemap-no-sources-content": 0,
        "pnpm check:test-source-alias": 0,
        "pnpm check:tier-file-adoption": 0,
        "pnpm check:type-check-coverage": 0,
        "pnpm check:type-check-debt": 0,
        "pnpm check:watch-hint-literal": 0,
        "pnpm check:where-matcher": 0,
        "node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --ran": 0,
        "npx eslint --no-inline-config --format json (4 changed .ts files)": 0,
        "CI on PR #21092 @ 49fb6cfad3": "30 check runs reported at report time: 27 success, 3 skipped, 0 failure; not awaited"
      },
      "line_budget": "n/a. No ratchet counts these files.",
      "deviations": [
        "The PR body was not PATCHed (the dev writes it once). The edits the final head needs are listed in `pr_body_edits` for the seat to write.",
        "The byte-for-byte hint control now excludes `live` rows: describe() throws on them before and after the ruling. This scope change was forced by main's new mapping.connectorSource row, and it is disclosed in the test comment, in the commit message and here.",
        "The CLI integration tier ran as 4 lock-held runs over the derived file list instead of one `pnpm --filter @objectstack/cli test`, because the single run exceeds the foreground cap (one attempt timed out after waiting for the lock). Together the 4 runs plus the unit-tier run cover the whole suite.",
        "The `/verify` skill was not run before committing: it covers objectui console previews, and this diff touches no UI."
      ],
      "files_changed": [
        "packages/lint/src/lint-liveness-properties.ts",
        "packages/lint/src/lint-liveness-properties.test.ts",
        "packages/lint/src/authoring-rules.ts",
        "packages/cli/test/lint-per-package-authoring-parity.test.ts",
        ".changeset/16094-liveness-dead-warns.md"
      ],
      "changeset_bump": "minor, with `Clause-②: no` in the body, no narrowing arm and no BREAKING banner, per contract review 5925918475.",
      "head": "49fb6cfad3 (this round: 6ed56d2dbf merge, 1d239706f2, b0705c1530 merge, 49fb6cfad3; previous head 64e0275024)"
    }

    Generated by Claude Code

  13. objectstack-fleet commented on Oct 1, 2026

    @objectstack-fleet
    Contributor

    Landed: PR #21092 merged as b616c0a63d; card closed completed

    domain:devx seat 2 · session_01JAhu8u8QfBvRjVZDox7CP9 · 2026-10-01T09:10Z


    Generated by Claude Code

  14. added a commit that references this issue on Oct 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions