Skip to content

Commit f79b9d5

Browse files
refactor(spec-liveness): the renderer's ledger evidence is in-repo under ui/; objectui becomes a retired realm that fails (#26150)
<!-- claude-projects-attribution: {"github_login":"os-tesla"} --> _Requested by **Tesla** · [project thread](https://claude.ai/code/project/chan_01AXsQkLFKRdQCqQ3RTr6Jao?thread=cmsg_01AXsQkLFKRdQCqQ3RTr6JaoAqVfFyhsxKuFcEP9YTBj25)_ Part of #25712 (4b PR 2e: the liveness ledger, card item 1; Tier S). Part of objectstack#25677. Clause-②: no Metadata-protocol: none ## 维护者速读(草稿) - **改了什么:** - liveness 账本里写成「objectui @某提交: 路径」的 374 个证据指针(164 行)改为仓内路径 `ui/<路径>`,去掉 objectui 标记和钉的提交。`verifiedAt` 一律不动。 - 107 行的 `evidenceScope` 由 `cross-repo` 改为 `in-repo`。同时引用 cloud 的那 1 行保持 `cross-repo`,原本没声明 scope 的 56 行仍不声明。 - 读取器把 `ui` 当作本仓根目录,`ui/` 路径和其他路径一样必须存在、行号在文件内、锚点符号存在、文件里出现该键名。 - `objectui` 改为「已退役的 realm」:仍标成 objectui 的路径单独列出并判红,同时打印对应的 `ui/` 写法。 - **为什么改:** objectui 已并入本仓(#25677),这些证据不再是跨仓的。 - **行号怎么处理(PM 已裁定):** objectui 的 133 个 `路径:行号` 是在旧提交上读的。对照今天的 `ui/`:6 个行号未变,66 个代码挪了位置,58 个无法核对,3 个所在文件已删除。规则如下: - 只有当整段引文在旧提交和今天的文件里都恰好出现一次、逐字节相同时,才改到新行号。 - 其余去掉指针上的行号,保留路径和锚点,旧读数留作文字 `(:N at objectui @sha)`。 - 同一句里的行号要么一起改,要么一起保留。 - **读者已消失的 5 行:** 删去失效的那一句,加一条带日期的 note,不猜新路径。其中 `field.externalId` 唯一的读者已被删除,记为后续 F23。 - **新增 13 条 key-mention 豁免:** 这些 `ui/` 文件读的是改名后的键(如 `inlineEdit` → `editable`)或委托给已锚定的兄弟文件,每条都写明文件实际用的名字。 - **风险与代价(含回滚):** 只改账本、读取器、测试和 README,不影响运行时。回滚 = revert 本 PR。 - **你要做的:** 无(Tier S)。 ## 席位核对 (left for the dispatch seat) ## Why objectui merged into this repo as `ui/` (#25677). The liveness ledger still cited the renderer as another repository: `objectui @<sha>: packages/…` pointers, counted foreign, never resolved. Card #25712 item 1 asks for: - the pointers rewritten in-repo (`ui/<path>#<symbol>`); - the scope set to `in-repo`; - the reader to resolve them exist-or-fail. PM ruling A (#25712 comment 6415397254) sets done-when to 0 objectui-attributed pointers, enforced by the reader. Note history stays. Unresolved rows go to the worklist, and no path is guessed. ## What changes ### Reader: `scripts/liveness/evidence.mts`, `check-liveness.mts` - `ui` joins `REPO_ROOTS`. A `ui/…` pointer is local, so it gets the existence, line-bound, symbol-anchor and key-mention checks. - `objectui` leaves `FOREIGN_REALMS` (now `cloud`, `ee`) and becomes the one entry of `RETIRED_REALMS` (`objectui → ui`). `scanEvidence` returns a third bucket, `retired`. - The gate fails on a path in it, at any status and in `evidence` or `producer`. - The message prints the `ui/` spelling: `query/limit → objectui: packages/core/src/index.ts (now ui/packages/core/src/index.ts)`. - Neither existing bucket would do. Counted foreign, the path never fails. Read as local, `objectui: packages/core/…` could pass against this repo's own `packages/core/`, a file that never was the reader. - A path already spelled `ui/…` is local whatever prose precedes it, so a dated reading can still name the old repo. - `empty-state.mts` and the `key-mention.mts` remedy text say `cloud:` where they said `objectui:`. ### Ledger: 24 files | | Count | |---|---| | Rows with an objectui-attributed pointer in `evidence` or `producer` | 164 | | Pointers re-rooted under `ui/` | 374 | | `objectui [@sha]:` markers in front of a pointer, dropped | 347 | | `objectui` words left as prose (no path directly after) | 50 | | `evidenceScope` `cross-repo` → `in-repo` | 107 | | Stays `cross-repo` (also cites `cloud`) | 1 | | Scope undeclared, left undeclared | 56 | | `verifiedAt` changed | 0 | Untouched: - `note` history, including the "read at objectui @sha" readings and `_note` fields; - 13 `evidence` strings that name objectui in prose with no repo-rooted path (`objectui action renderers + runtime dispatcher`); - every `cloud` row. ### Line citations: the 133-citation breakdown Each objectui `path:N` was read at a pinned objectui commit. A clone of objectui (unshallowed) was compared with today's `ui/` at this PR's base: | Measured per line | Count | |---|---| | Same lines today | 6 | | Moved: the cited block is byte-identical at exactly one other offset | 66 | | Unverifiable: the pin is not in objectui main's history (e.g. `940ba24`, 33 lines), or the cited lines changed | 58 | | In a deleted file | 3 | Rule applied (PM-accepted): - **Exact relocation only.** A block must occur exactly once in the file at the cited commit and exactly once in today's file. - **One clause, one reading.** A pointer moves together with its bare `:N` follow-ons (`…ListView.tsx:763 seeds … and :1491-1494 filters …`), or none of them moves. | Pointer lines, as applied | Count | |---|---| | Kept (same) | 7 | | Relocated | 58 | | Line dropped from the pointer; reading kept as prose `ui/p (:N at objectui @sha)` | 65 | | In a deleted file: clause dropped or turned to prose, see below | 3 | - **Follow-ons.** There are 50 follow-on `:N` refs: 18 relocated with their clause, and 32 stay at the dated reading. - **Prose line numbers.** Numbers written as prose inside a relocated clause were relocated by the same rule, in 8 rows: - sibling `action-icon.tsx:N` refs in `action.bodyShape` and `bodyExtra`; - `:N/:N` pairs in `action.method` and `view.form.subforms`; - "line N" in `email_template.label`/`description` and `webhook.label`/`description`. - **Rebases.** Each time main moved, the relocation was regenerated from main's ledger by the same script, not hand-edited: - #25987 moved `ListView.tsx` by 9 lines; `view.list.hiddenFields` now cites `:2020`, `:3812-3815` and `:3830`; - #26103 moved `NavigationRenderer.tsx` by 28 lines; `app.navigation.runAction`'s producer now cites `:850`, `:870-875`, `:963` and `:968`. - **Exact matches only.** Nothing was relocated by similarity. ### Rows whose objectui reader is gone Each row below gets no repoint and a dated note line saying what left and when: | Row | Dropped clause | Why | |---|---|---| | `app.navigation.id` | `UnifiedSidebar.tsx#useNavOrder` | objectui#12059 made the sidebar menu non-reorderable; the other three readers stay | | `view.list.hiddenFields` | spec-bridge `list-view.ts:123` | objectui#6632 retired the spec-bridge; the ListView reader stays | | `view.form.groups` | spec-bridge `form-view.ts:169` | objectui#6632; the producer fold in `view.zod.ts` and the ObjectForm reading stay | | `field.widget` | `plugin-detail/src/index.tsx:563` (`permission-facet-link` registration) | objectui#12998 removed the widget; found by the key-mention check once the path was in-repo | | `field.externalId` | its only pointer, console `metadataConverters.ts:125` | deleted in objectui dc14bcb ("zero importers"); the pointer is now prose, **F23** | ### Key mention: 13 exemptions Once in-repo, these citations met the key-mention check for the first time. Each cited file reads a renamed key, or names the parent key and delegates to the entry's other, anchored citation. Each row in `key-mention.baseline.json` names the spelling the file actually uses: | Entry | File | Name used | |---|---|---| | `page/print.{paperSize, orientation, margins.right, margins.bottom, pageNumbers, repeatTableHeaders, avoidBreakInside}` (7) | `layout/page.tsx` | `print` (`isPagePrintDeclaration(schema.print)`) | | `dashboard/widgets.chartConfig.{showDataLabels, annotations, interaction}` (3) | `plugin-dashboard/src/DatasetWidget.tsx` | `chartConfigPresentation` | | `view/list.inlineEdit` | `plugin-grid/src/ObjectGrid.tsx` | `editable` | | `view/form.subforms` | `plugin-form/src/MasterDetailForm.tsx` | `details` | | `translation/flows.screens` | `app-shell/src/views/ScreenView.tsx` | `label` / `placeholder` / `inlineHelpText` | The baseline stays shrink-only. A row fails once its pair anchors. ### Tests and README - **`evidence.test.ts`:** the realm cases move from `objectui` to `cloud`/`ee`, and a new block pins the retired realm. Its `ui/` paths are synthetic (`ui/packages/layout/src/x.tsx`): the scanner never opens them, and a real path there would be an undeclared cross-package input to `check:cross-package-test-inputs`. - **`check-liveness.test.ts`:** - the realm cases move from `objectui` to `cloud`/`ee`; - two subprocess cases fail on a retired-realm pointer in `evidence`, in `producer`, and on a `dead` row; - the summary-line pin reads `(cloud / ee — not resolvable here)`. - **Restored.** The `path:NNN` contract test and the line-citation floor come back. #13043 retired both when that population reached zero, with the instruction to restore them when a citation was written again. - **`README.md`** updates the realm-marker section, the commit-pin advice, `evidenceScope`, the preview lookup (`git grep -- ui/` instead of `git -C ../objectui`) and "confirmed absence". The first commit is the governed-queue-guard comment fix held from #26058, accepted by the PM to ride here. ## Follow-ups (sub-issue 4) - **F23 (new).** Find the current reader of `field.externalId`, or re-classify it. Its only cited reader was a dead module. - **F24 (new, optional).** The 65 relocated `ui/` line citations are now in-repo `path:N` pointers, and they rot in range the way #12516 describes: the two base moves above shifted seven of them in two hours. Converting them to `#symbol` anchors is the durable form. - **F25 (new, noted).** `docs/qa/platform-checklist/areas/*.json` still write `objectui: packages/…` refs. That surface is not the ledger and is outside this card item. - **F26 (new, reported to the dispatch seat).** After this PR, the gate and `evidence.test.ts` read 113 `ui/` files through the ledger. A `ui/`-only PR schedules neither: - Spec Liveness Check's PR `paths:` do not list `ui/**`, and it is not a required context; - Test Core does not start, and `@objectstack/spec#test:repo` does not hash `ui/**`, so in the merge queue it is a cache hit. So a `ui/` PR that renames a cited file or symbol lands green, and the next spec PR goes red (#7802's shape). `packages/**` citations do not have this gap, because spec declares those roots. Closing it means declaring the `ui/` sources in `scripts/cross-package-test-inputs.mjs` and `turbo.json`, and covering them in root `ci.yml` `crosspkg`, which `check:ci-filter-parity` requires. That is Tier H, and every `ui/` PR would then start Test Core for spec. Left to the dispatch seat; not widened here. ## Verification Head `23cb18d2b9`, on `main` `1bbb7bf490`. 33 files, +703 / −385. Tier S: no governed path, under 3000 lines. - **`pnpm --filter @objectstack/spec check:liveness`**, exit 0: - `evidence paths`: 1036 repo-local paths declared, 1036 resolved; 41 attributed to another repo (cloud / ee). - `line citations`: 65 written `path:NNN`, 65 inside the cited file. - `symbol anchors`: 1220 written `path#symbol`, 1220 named. - `key-mention anchoring`: 1036 pairs, 1022 anchored, 14 exempt. - Retired-realm pointers: 0. - **`vitest run scripts/liveness/`:** 12 files, 358 tests passed. - **Gate sweep:** `node scripts/pm/dispatch-gates.mjs --commands` derives 68 gates for this diff. All 68 ran on `23cb18d2b9` and exited 0. `--ran` reconciles 68 derived, 68 run, 0 NOT-MEASURED, 0 UNRUN. - The first pass found `check:cross-package-test-inputs` red on four real `ui/` paths in `evidence.test.ts`. Those unit cases now use synthetic paths (see Tests). - **Push battery** (`GITHUB_ACTIONS=true GITHUB_EVENT_NAME=push node scripts/pm/check-dispatch-gates.mjs`) on `23cb18d2b9`: 2182 cases pass, 0 fail (fast 1917, slow 265; 1800 s). 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01A6SEitNngAMugGbuJHWsDS --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 83a26c3 commit f79b9d5

33 files changed

Lines changed: 703 additions & 385 deletions

‎packages/spec/liveness/README.md‎

Lines changed: 41 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ registry to fold it back onto — the override *is* its governance.
7272

7373
| Status | Meaning |
7474
|---|---|
75-
| `live` | Has a runtime consumer. Cite it in `evidence`, preferably anchored to the consuming symbol — `file#symbol` — with `file:line` as an optional convenience; for another repo's path, prefix the realm — `objectui: packages/app-shell/…` (see below). |
75+
| `live` | Has a runtime consumer. Cite it in `evidence`, preferably anchored to the consuming symbol — `file#symbol` — with `file:line` as an optional convenience; the renderer is in this repo, cited `ui/packages/app-shell/…#Symbol`; for another repo's path, prefix the realm — `cloud: packages/…` (see below). |
7676
| `experimental` / `planned` | Declared, intentionally not enforced yet. Also read from a spec `.describe()` marker like `[EXPERIMENTAL — not enforced]`. |
7777
| `dead` | Parsed, no consumer **anywhere the census looked**. Tracked for **enforce-or-remove** (ADR-0049). A key that is dead here but enforced in a sibling repo is NOT `dead` — it is `live-elsewhere`, below. |
7878
| `live-elsewhere` | Dead here by measurement, **genuinely enforced in a sibling repo** (#13483). Not deletable. The template is `manifest.runtime`, "ENFORCED AT THE CLOUD PUBLISH GATE, NOT ENFORCED AT LOAD" — its load side belongs to this repo's loader. A key whose consumer is the closed cloud runtime is `live` with a `cloud`-attributed pointer instead (`agent.json`'s `_note`). Carries its own gate-executable criteria — see the section below. Ledger-entry-only (no `.describe()` marker). |
@@ -116,22 +116,38 @@ enforce-or-remove instead of repointing at a plausible survivor.
116116

117117
**A path in another repo must say so**, or the gate will report it as rot —
118118
attributed paths are counted and never resolved, so they cannot fail this check
119-
(that boundary is deliberate: ~101 entries cite the renderer repo or the closed
120-
cloud runtime, whose files are legitimately absent here):
119+
(that boundary is deliberate: the closed cloud runtime's files are legitimately
120+
absent here):
121121

122122
```jsonc
123-
"evidence": "objectui: packages/app-shell/src/views/RecordDetailView.tsx:573"
124-
"evidence": "registered into ActionEngine (objectui packages/core/…/ActionEngine.ts:150) but no caller"
123+
"evidence": "cloud @cb8ee7ff: packages/service-ai/src/tools/action-tools.ts#buildParametersSchema"
124+
"evidence": "carried onto the tool definition (cloud packages/service-ai/…/action-tools.ts) but no runtime here"
125125
```
126126

127-
A realm marker (`objectui`, `cloud`, `ee`) attributes the paths that follow it,
128-
up to the next clause boundary (`;` or `)`), so a string can cite both repos —
127+
A realm marker (`cloud`, `ee`) attributes the paths that follow it, up to the
128+
next clause boundary (`;` or `)`), so a string can cite both repos —
129129
`framework` switches back explicitly. `packages/services/service-ai/…` is always
130130
treated as foreign: that is the closed cloud runtime, and `packages/services/`
131131
here ships every sibling service except it. A path that is not repo-rooted
132132
(`app-shell/MetadataProvider.tsx`, `action-button/-group`) reads as prose and is
133133
neither resolved nor reported.
134134

135+
**The renderer is not another repo.** objectui merged into this one as `ui/`
136+
(#25677), so its files are cited like any other path — `ui/packages/app-shell/src/views/RecordDetailView.tsx#RecordDetailView`
137+
— and held to the same standard: the file exists, a cited line is inside it, an
138+
anchored symbol is in it, and the file names the key. `objectui` is a **retired**
139+
realm marker (`RETIRED_REALMS` in `scripts/liveness/evidence.mts`): a path still
140+
attributed to it FAILS, with its `ui/` spelling printed as the remedy. It cannot
141+
stay foreign (counted, never resolved, it would never fail) and it cannot be read
142+
as local either: both trees have a `packages/core/`, so `objectui: packages/core/…`
143+
could pass on a file that never was the reader. A path already spelled `ui/…` is
144+
local whatever prose precedes it, so a dated reading may keep naming the old repo —
145+
`ui/packages/layout/src/NavigationRenderer.tsx (:961 at objectui @940ba24)` — and
146+
that is exactly how #25712 re-rooted the 164 renderer rows: a `path:NNN` whose cited
147+
block is byte-identical at one offset of today's file was relocated there; any other
148+
line left the pointer and stayed behind as that dated prose, because a line read at
149+
an old objectui commit is not a line of this checkout.
150+
135151
> This replaces the old advice to write objectui paths "as prose to avoid false
136152
> stale-flags." That was a workaround for a parser bug — the check took
137153
> `evidence.split(':')[0]` as the filename, so any prose made it fail. It flagged
@@ -171,9 +187,11 @@ Two rules, and the asymmetry between them is the point:
171187
loudly instead.
172188

173189
Most entries predate the field and are simply undated; date them as you
174-
re-verify rather than back-filling guesses. **For objectui-side evidence, pin
175-
the commit** (`objectui @732b1bf`) — `action.undoable`'s reader line numbers had
190+
re-verify rather than back-filling guesses. **For cloud-side evidence, pin the
191+
commit** (`cloud @15f55df`) — `action.undoable`'s objectui reader line numbers had
176192
already drifted by 28 lines one day after the issue citing them was filed.
193+
Renderer evidence needs no pin since the merge (#25712): `ui/` is this checkout,
194+
and the gate bounds its lines and anchors on every run.
177195

178196
### `producer` — a consumer is only HALF the call graph (#4837)
179197

@@ -259,7 +277,11 @@ barrel package adds a blind spot, and the renderer repo is all barrels.
259277
| Value | Means |
260278
|---|---|
261279
| `in-repo` | the call graph was closed inside this repo only |
262-
| `cross-repo` | a named foreign realm was walked too — say **which**, in the evidence, and **pin the commit** (`objectui @c2fd1223`): `action.undoable`'s reader line numbers drifted 28 lines in one day (#3714) |
280+
| `cross-repo` | a named foreign realm was walked too — say **which**, in the evidence, and **pin the commit** (`cloud @15f55df`): `action.undoable`'s reader line numbers drifted 28 lines in one day (#3714) |
281+
282+
`in-repo` covers `ui/` — the renderer is this repo since #25712, which moved the
283+
scope of every re-rooted renderer row from `cross-repo` to `in-repo` (a row that
284+
also cites `cloud` kept `cross-repo`; an undeclared scope stayed undeclared).
263285

264286
Absent = scope undeclared, a worklist row rather than a failure; the field is
265287
younger than nearly every entry. A value outside the vocabulary FAILS, the same
@@ -397,16 +419,15 @@ the maintainer settled it directly:
397419
398420
**The mechanical rule, for the next sweep.** Before writing `"no runtime
399421
consumer"` — or any wording that means it — **enumerate the metadata-admin preview
400-
read points** for the type, in objectui, and say what you found. The population is
422+
read points** for the type, under `ui/`, and say what you found. The population is
401423
small, enumerable, and registered by type name, so this is a lookup and not a
402424
search:
403425

404426
```bash
405427
# 1. does this type have a preview at all?
406-
git -C ../objectui grep -n "registerMetadataPreview('<type>'" origin/main
428+
git grep -n "registerMetadataPreview('<type>'" -- ui/
407429
# 2. what does that preview read off the draft?
408-
git -C ../objectui show origin/main:packages/app-shell/src/views/metadata-admin/previews/<X>Preview.tsx \
409-
| grep -n 'd\.<key>'
430+
grep -n 'd\.<key>' ui/packages/app-shell/src/views/metadata-admin/previews/<X>Preview.tsx
410431
```
411432

412433
An absent preview is a finding to record, not a step to skip — "the type has no
@@ -433,10 +454,10 @@ person.
433454
because a human sees it in the designer; the scheduler still stores name/schedule
434455
only, and the row says so. A re-grade is **not** an ADR-0033 change: these four
435456
rows are docs-shaped, deliberately KEPT, and still not `authorWarn`'d — nothing
436-
about enforce-or-remove moves. Cite the preview the way any cross-repo evidence is
437-
cited: realm marker, pinned objectui commit, and a `producer` naming the
438-
`registerMetadataPreview` call plus the surface that resolves it — a preview no
439-
registry hands a draft to is a read point that never runs.
457+
about enforce-or-remove moves. Cite the preview the way any in-repo evidence is
458+
cited — `ui/…#Symbol` — with a `producer` naming the `registerMetadataPreview`
459+
call plus the surface that resolves it: a preview no registry hands a draft to is
460+
a read point that never runs.
440461

441462
### How to verify a claim without fooling yourself
442463

@@ -858,8 +879,8 @@ The governed set is `GOVERNED` at the top of `check-liveness.mts`. To add a type
858879
2. Seed `<type>.json` from that type's liveness audit (file:line evidence) + targeted
859880
greps. **Classify only with evidence** — `live` needs a cited consumer; `dead` needs a
860881
confirmed absence.
861-
3. **Confirmed absence means BOTH repos.** The renderer layer is a legitimate consumer
862-
(`live` with objectui evidence as prose), so grep `../objectui` before writing `dead`.
882+
3. **Confirmed absence includes the renderer.** The renderer layer is a legitimate
883+
consumer (`live` with `ui/…` evidence), so grep `ui/` before writing `dead`.
863884
Precedent: `enable.trackHistory` was misclassified dead for a month while
864885
RecordDetailView had been gating the History tab on it the whole time (#2707).
865886
4. Add the type to `GOVERNED`; confirm the gate is green.

0 commit comments

Comments
 (0)