Repository navigation
docs(liveness): designer previews count as consumers — re-grade four docs-shaped rows and write the principle into the ledger methodology (#7131) - #7425
Conversation
…docs-shaped rows (#7131) The ledger recorded "no runtime consumer" for job.label, job.description, translation.label and translation.name. objectui's metadata-admin previews had been rendering all four to a human the whole time. Per the maintainer ruling of 2026-08-10, a designer preview that renders a key to a human is a runtime consumer. The four rows re-grade dead -> live with realm-marked, commit-pinned objectui evidence (@aeb8424b) and a `producer` naming the registerMetadataPreview call plus the surface that resolves it — a preview no registry hands a draft to is a read point that never runs. Nothing about enforce-or-remove moves: all four remain docs-shaped, deliberately KEPT under ADR-0033, and still not authorWarn'd. The README gains the methodology section the ruling asked for, dividing against the existing "an authoring/preview renderer is NOT a runtime consumer" section on what the property CLAIMS rather than on what the surface is. The 2026-07 sweep's ten corrections are not reopened. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016R9de1FqP7NvwKvqXi92Gh
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
📓 Docs Drift CheckThis PR changes 1 package(s): 106 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:
⛔ 7 release-owned page(s) also reference the affected code. These are read-only:
|
…nst the previews rule (objectstack-ai#7427) (objectstack-ai#7445) The 2026-08-10 maintainer ruling on objectstack-ai#7131 asked that "the ledger methodology note records the principle so the next sweep asks the question mechanically". This is that sweep, run against the README section PR objectstack-ai#7425 landed. Eighteen rows and file-level notes whose ground was "no runtime consumer" (or wording meaning it), written before 2026-08-10, were partitioned display vs behavioural. Every display hit got the README's two lookup commands run against objectui origin/main @e9ab52f9. Behavioural hits are untouched: the ruling does not reopen the 2026-07 corrections. ZERO verdicts move. That is the finding, not a shortfall — three of the four display candidates that read like twins of PR objectstack-ai#7425's re-graded rows turn out to fail on REACHABILITY or on render-vs-count: * permission.rowLevelSecurity.label/.description/.tags — PermissionPreview reads rowLevelSecurity only as an array and renders `${rls.length} RLS rules`. A count is not a render; the 2026-07-30 closure survives intact. * view.label — ViewPreview DOES read the container label (:115) and injects it as its single listView's label, but neither render path fires: the named-view tab bar returns null below two entries, and the `renderListView` label path needs a prop the object-view registration never passes. * view.name — the preview reads the `name` PROP, never the body copy, so it is not translation.name's shape after all. * validation.label/.description/.tags — ValidationPreview renders all three, but the standalone `validation` kind was retired by ADR-0088 (objectstack-ai#4509) and the surviving embedded path routes to EmbeddedItemEditor's SchemaForm, so the governed path never hands the preview a draft. Two rows already `live` gained the falsifiable pointer a `live` verdict is supposed to be: datasource.label/.description, whose prose still claimed "No runtime consumer by design" while the datasource preview had been rendering both as card title and subtitle. Three measured ABSENCES are recorded rather than skipped, per the README's "an absent preview is a finding to record": app.areas.description (AppPreview has zero `areas` reads), flow.description (FlowPreview reads no description), and hook.label/.description (no registered `hook` preview at all). datasource.json's file-level note is rewritten: its preview clause was stale on both halves independently. Its ground moved (the ruling), and its facts moved the OPPOSITE way to the card's expectation — the SideBlocks were never removed; what went away is the schema half, so three of the five renders now acknowledge keys `.strict()` rejects. Filed as objectui#4131. No changeset: verdict-neutral ledger prose plus evidence re-citation, the PR objectstack-ai#7179 shape (which shipped with skip-changeset and no changeset), not the PR objectstack-ai#7425 shape (verdict re-grades, patch changeset). Claude-Session: https://claude.ai/code/session_016R9de1FqP7NvwKvqXi92Gh Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
…fact, and eleven drifted rows are reconciled (objectstack-ai#7377) (objectstack-ai#7455) Closes objectstack-ai#7377. Two stages, in order — the reconciliation first, because doing the mechanism first would have made CI red with no path to green. STAGE 1 — per-row reconciliation. The table declares its own counting method (`check-liveness.mts --json`, fixed in objectstack-ai#4488) and says the count columns are never hand-edited. Nobody re-ran the snippet: 9 of 30 rows disagreed with the gate when objectstack-ai#7377 was filed, and PR objectstack-ai#7425 added two more. Every delta is measured and explained rather than absorbed: - field/action/hook/page/seed/webhook — the ADR-0010 protection envelope entered each schema's walked shape as the objectstack-ai#4001 campaign closed it (objectstack-ai#4514/objectstack-ai#4530/objectstack-ai#4531/objectstack-ai#4533/objectstack-ai#4974); the gate auto-classifies those keys `live`. - flow — +1 dead: errorHandling.retryDelayMs, tombstoned by objectstack-ai#4964's rename to `backoffMs`. The Note said "4 tombstone entries"; it says 5. - action — +1 live beyond the envelope: `description` (objectstack-ai#7367 / PR objectstack-ai#7430). - view — objectstack-ai#4001 batch 6e (objectstack-ai#4534) declared three container-level keys the Note never mentioned: `object` live, `name`/`label` dead. The hand-enumerated dead set was 4 against a real 6. - app — dead 9 still reconciles exactly; +1 live `_unpublished` (objectstack-ai#4829 / PR objectstack-ai#6942) and the type's first `planned`, `navigation.runAction` (objectstack-ai#4848 / PR objectstack-ai#7253). - job — zero dead under objectstack-ai#7425. First row in the table where that holds WITH the ADR-0033 exemption still in force; the Note says so. - translation — zero dead under objectstack-ai#7425. Its cell had published `dead 2` beside a sentence naming one key, and that key had already been removed in objectstack-ai#4667: the number was right and the prose false, in the same cell, on the day it was written. Recorded as measured history. The other 24 Notes are byte-identical. Five rows carried a SEVENTH cell against a six-column header, which GFM drops — that prose rendered nowhere, and the 2-column table folds it back into the Notes cell losslessly. STAGE 2 — the mechanism, on objectstack-ai#5107's precedent. The numbers move to `packages/spec/liveness/state-counts.md` (`merge=os-regen`, registered in `scripts/regen-artifacts.mjs` and `.gitattributes`); the Notes prose stays hand-written, because regenerating a Note manufactures a verdict. `gen:liveness-counts` spawns the gate rather than re-implementing its walk, and keeps objectstack-ai#7257's skeleton row. `check:liveness` gains three legs in `readme-table.mts`: artifact freshness by byte comparison, README-vs-artifact row sets in both directions, and a count column reappearing in the README — which the other two cannot see. Reverse verification (predicted, then run against a mutated `--ledger-root` copy): artifact deleted => MISSING, red; `view` live skewed 80->81 => STALE naming that line, red; a count column re-added to the `object` row => red naming the line; the `qa` row deleted => red on both the objectstack-ai#7257 heading and the new row-set heading. Verbatim copy green. Gates: liveness script tests 9 files / 186 tests; check-generated ledger tests 8/8; `check:liveness` green; `check:generated --reconcile-only` green; `git-merge-regen.mjs --self-test` green (12 paths); scripts typecheck clean; check-nul-bytes OK. Claude-Session: https://claude.ai/code/session_01PJn5BD9LY9ZCECPs25oiDb Co-authored-by: Claude <noreply@anthropic.com>
Fixes #7131
Executes the maintainer ruling of 2026-08-10 (quoted verbatim below and in the ledger README): previews count as consumers.
What was wrong
packages/spec/liveness/job.jsonandtranslation.jsonrecorded "no runtime consumer" for four docs-shaped keys. objectui's metadata-admin previews had been rendering all four to a human the whole time — measured at objectuiorigin/main@aeb8424b:job.labelJobPreview.tsx:257(d.label, falling back to the jobname):313, the preview card's titlejob.descriptionJobPreview.tsx:258(d.description):316, beneath the title when non-emptytranslation.labelTranslationPreview.tsx:67, first choice of the display chain:100, the item's titletranslation.nameTranslationPreview.tsx:67, the fallback whenlabelis unset:100, same titleIn both files
dis the metadata body of the exact type the row covers, and the previews are reachable rather than merely present:previews/index.ts:62/:50register them against thejob/translationtype names, andResourceEditPage.tsx:949resolves the registration and hands the component the draft being edited.The ruling
What changed
Four rows re-graded
dead→live, each with a realm-marked, commit-pinnedevidencestring,evidenceScope: "cross-repo",verifiedAt: "2026-08-10", and aproducernaming theregisterMetadataPreviewcall plus the surface that resolves it — a preview no registry ever hands a draft to is a read point that never runs.Each note records what the re-grade supersedes and what it does not:
job.label— the old wording ("no runtime consumer (sys_job stores name/schedule only)") was true about the scheduler and false as a whole-system claim; for a docs-shaped key the display is the runtime effect, sosys_jobwas never the surface that could falsify it. The note says explicitly thatlivehere does not mean the scheduler acquired a use for it.translation.label— the superseded wording hedged, "no runtime consumer in this repo", and that hedge was never false. What changed is that the cross-repo look was finally taken, which is exactly the blind spotevidenceScope(audit: #4667 liveness 判定的跨仓覆盖核查——两个方向各有一个实锤反例;顺带更正 homePageId 墓碑文案 #4895) exists to expose.translation.name— the re-grade supersedes one clause ("dead as a BODY key — the honest reading of a copy nobody reads"). The row's substantive door/row-column analysis is preserved verbatim and called out as the substance: thenamecolumn is still the live one on the sync path, and the body copy is still not whatauthored-translation-syncreads. The preview reads it; the sync does not.Nothing about enforce-or-remove moves. All four keys remain docs-shaped, deliberately KEPT under the ADR-0033 exemption, and still not
authorWarn'd.Methodology note — a new README section,
Designer previews count as consumers, quoting the ruling verbatim and giving the sweep a mechanical step: enumerate a type's registered preview read points before writing "no runtime consumer", and record their absence when there are none. Two commands make it a lookup rather than a search.It divides against the pre-existing
An authoring/preview renderer is NOT a runtime consumersection on what the property claims, not on what the surface is — for a display key the render is the whole of the declared effect; for a behavioural key a panel echoing the value back still proves nothing. The 2026-07 sweep's ten corrections are explicitly not reopened, and that section gains a short scope pointer so a reader landing there first is not misled. Its heading is unchanged, because several ledger notes cite it by name asREADME §preview-renderer.Verification
pnpm --filter @objectstack/spec check:liveness— green;job13 live / 2 dead → 15 live / 0 dead,translation17 live / 2 dead → 19 live / 0 dead. Repo-local evidence paths stayed at 353/353 resolved and the foreign bucket moved 131 → 135, i.e. all four new citations landed in the cross-repo bucket and none leaked into the local one.Reverse verification — dropped the
objectuirealm marker fromjob.label's evidence and re-ran the gate: exactly one MISSING, namedjob/label, with the local count rising 353 → 354 and the foreign count falling 135 → 134. Restored, green again. The realm marker is load-bearing, as expected.Also green:
packages/specliveness script tests (9 files / 166 tests),pnpm --filter @objectstack/spec typecheck, andnode scripts/check-nul-bytes.mjs.Out of scope, deliberately
The README "Current state" table's
jobandtranslationrows now carry stale counts (13/2 and 17/2) and Notes prose that enumerates the old dead sets. That table's count columns are the subject of unassigned #7377, whose stated method — "for each drifted row read the Note beside it and reconcile the prose with the new numbers" — is exactly what these two rows need. Left untouched to avoid colliding with that lane; commented on #7377 with the delta instead of filing a twin. Nothing fails:readme-table.mtsdeliberately checks the row set, never the count columns.Two ledger notes still record the superseded principle as their ground —
datasource.json's file-level_noteandpermission.json'srowLevelSecurity.labelrow. Flagged in the report on #7131 rather than fixed here; they need re-measurement under the new principle, not a text edit.Generated by Claude Code