Repository navigation
Commit 7e5246d
fix(spec): grade flow.description and hook.label/description live — Studio's metadata list and quick-find show them (#20541)
Part of #20299
Clause-②: no
Stage 1 of #20299. Three docs-shaped liveness rows move `dead` → `live`,
because Studio already shows them to a human: `flow.description`,
`hook.label` and `hook.description`. The other four rows of the family
need objectui code and are objectstack-ai/objectui#11027's:
`app.areas.description`, `permission.rowLevelSecurity.label` /
`.description` and the `view` container `label`. They are untouched
here, and #20299 remains open for them.
Ledger data, two README Notes cells and one gate-test fixture only. ⛔ No
schema, parse, `.describe()` or accept-set change. None of the three
rows sets `authorWarn`, so the set of lint warnings does not change. The
re-grade reverses no ADR-0033 decision: all three stay docs-shaped,
deliberately KEPT and exempt from enforce-or-remove. Each row keeps the
note it carried while `dead`, labelled as history.
## Files
- `packages/spec/liveness/flow.json`: the `description` row.
- `packages/spec/liveness/hook.json`: the `label` and `description`
rows, plus one dated sentence appended to the file `_note`. Its
2026-08-10 lookup ("both verdicts stand unchanged") is now history.
- `packages/spec/liveness/state-counts/flow.md` and
`packages/spec/liveness/state-counts/hook.md`: the two per-type count
shards, regenerated by `gen:liveness-counts`, not hand-edited. `flow`
has 35 live and 5 dead (was 34 / 6). `hook` has 21 live and 1 dead (was
19 / 3). Across all shards, the read-time sum that `check:liveness`
prints is 952 live and 136 dead (was 949 / 139). No file commits that
total.
- `packages/spec/liveness/README.md`: the `flow` and `hook` Notes cells.
Both listed these keys as dead.
- `packages/spec/scripts/liveness/check-liveness.test.ts`: the "stays
GREEN when a `dead` entry carries the SAME rotted pointer" case borrowed
`flow.description` as its sample `dead` row. It now uses the
`flow.active` tombstone, which the gate itself holds at `dead`, and it
asserts that precondition.
- `.changeset/20299-display-annotations-ledger.md`: a `patch` changeset
for `@objectstack/spec`. The ledgers ship in its `files[]` (`liveness`),
and `@objectstack/lint` reads them.
**File-surface declaration.** The claim's surface named `flow.json`,
`hook.json`, the regenerated counts and the changeset. The README cells
and the test fixture are outside it. The dispatch's pin sweep required
both to move with the flip ("grep for any test, doc or ledger note that
asserts these three rows are `dead` … and move it with the flip").
Without the fixture move, the gate test goes red: see the reverse
verification below. The README ships in the same `liveness` directory.
## The lane's call, and the ruling it applies
A Studio list column and the metadata quick-find count as consumers of a
DISPLAY-shaped key. That is the #7131 ruling's table in
`packages/spec/liveness/README.md` ("Designer previews count as
consumers"): for a display key, being shown to a human is the whole of
the claimed effect. The README's `producer` discipline binds too, so
each row names who hands the reader a record.
## Premise, reader half: measured at the `.objectui-sha` pin `dd3f7e1be`
Instrument: read-only `git -C ../objectui show SHA:PATH` and `git grep …
SHA`. Nothing in objectui was edited, checked out or stashed.
- The Studio route `metadata/:type` mounts `MetadataResourceListPage`
(`packages/app-shell/src/console/AppContent.tsx`, both the with-app and
the zero-app branch).
- `MetadataResourceListPage` renders a registered custom `ListPage` if
one exists, and otherwise `DefaultMetadataList`. `DefaultMetadataList`
takes `config.listColumns ?? defaultColumns(config.primaryKey ??
'name')`. `defaultColumns` returns the primary key, `label` and
`description`, and each cell goes through `defaultCell`.
- **No registration gives `flow` or `hook` a `ListPage` or
`listColumns`.** `git grep registerMetadataResource` over `packages` and
`apps` gives 44 hits. The non-test registrations are
`builtinComponents.tsx` (object, field, permission, view, dashboard,
page, book), `anchors.ts#registerBuiltinAnchors`,
`datasource/register.ts` and `default-schemas.ts`, which sets
`defaultSchema` / `fieldOrder` only. `flow` and `hook` register only in
`anchors.ts`, with anchors, create fields and defaults. `git grep -E
'ListPage\s*:'` hits only `datasource/register.ts`, the positive
control. The shorthand spelling `ListPage[,}]` / `listColumns[,}]` has
no registration hit.
- `MetadataQuickFind` (`QuickFind.tsx`) indexes every type's items off
the same `client.list(type)` read. It keeps `label` and `description`
and draws `label` beside the name and `description` under it. It is
mounted on `DirectoryPage` and `StudioHomePage`.
- `MetadataClient.list(type)`
(`packages/data-objectstack/src/metadata-client.ts`) is `GET
{base}/{type}`, with base `/api/v1/meta`. It accepts a top-level array
or `items`.
- **Per-string control at objectui main `5d689c3`**, counted with `git
show REF:PATH | grep -cF` at both refs. Every cited string counts the
same at the pin and at main (1/1 each): the `listColumns ??
defaultColumns` line, the `description` and `label` default-column
entries, `if (customConfig?.ListPage) {`, the `:type` route element,
QuickFind's `label: item?.label,`, `description: item?.description,` and
`{r.description}`. The `ListPage:` and `listColumns` registration counts
also match.
## Premise, producer half: measured booted, not read
Instrument: a throwaway `@objectstack/verify` test in
`packages/qa/dogfood` (deleted after the run, never committed). It
booted the real `examples/app-showcase` composition in-process with
`bootStack(showcaseStack)`, signed in as the dev admin and read the
exact doors the list page reads. It was run twice, before and after a
container restart, with the same result.
| door | status | body | rows |
|:--|:--|:--|:--|
| `GET /api/v1/meta/flow` | 200 | top-level keys `type`, `items` | 30
flows. All 30 carry their authored `label` and `description`, with
`_packageId: com.example.showcase` |
| `GET /api/v1/meta/hook` | 200 | top-level keys `type`, `items` | 4
hooks. All 4 carry `label` and `description`, with `_packageId:
com.example.showcase` |
| `GET /api/v1/meta/package` | 200 | top-level keys `type`, `items` |
`com.example.showcase` with `scope: project`, which is what
`buildPackageScopeOptions` admits, so the list page's package scope
shows those rows |
The server-side list answer is
`packages/rest/src/meta-item-read-gate.ts#createMetaListAnswer`, which
both transports serve. Its type-specific steps are for `api`, `app`,
`view`, `doc` and `object` only. For `flow` and `hook` it runs the
per-caller gate and the translation step, and neither drops `label` or
`description`. Each row's `producer` cites that answer, the route, the
list-page fall-through, the registration and the client read.
## Verification, at `cb0206219c`
All heavy runs went through `scripts/pm/os-verify-lock.sh` with
`--maxWorkers=2`. The box is shared, so wall-clock figures are
shared-box readings.
- `pnpm --filter @objectstack/spec run check:liveness`: exit 0. "✓ every
governed-type property … is classified …" and "✓
packages/spec/liveness/state-counts.md is current".
- `pnpm --filter @objectstack/spec exec vitest run --project local
--maxWorkers=2` (the whole `local` project): **573 files, 16835 passed,
1 todo**.
- `@objectstack/spec` `repo` project, narrowed. The whole `repo` project
hit its 330 s timeout on the shared box, so that run is NOT MEASURED. It
was narrowed to the 10 `repo` files that read the ledgers:
`scripts/liveness/evidence.test.ts`,
`scripts/liveness/proof-registry.test.ts` and eight `*-retirement` /
`*.pin` tests. Result: **10 files, 211 passed**. CI runs the whole
project.
- `pnpm --filter @objectstack/spec typecheck`: exit 0. It covers `tsc
--noEmit`, `check:scripts-typecheck` and `check:test-typecheck`. `tsc -p
tsconfig.scripts.json --listFiles` names
`scripts/liveness/check-liveness.test.ts`, so the edited test is
compiled.
- **Gates.** `node scripts/pm/dispatch-gates.mjs --repo
objectstack-ai/objectstack --commands` derived 68 commands at
`cb0206219c`. All 68 ran, and each exit code was captured before any
pipe. `--ran` over the exit-coded record reported: "✓ 68 derived
famil(ies) accounted for — 68 run, 0 NOT-MEASURED (a DERIVED zero …)".
- 66 exited 0.
- `check:dual-build-cjs-loads` first exited 3 (PREREQUISITE NOT MET:
eight unbuilt packages). It was re-measured after building them (all
turbo cache hits) and exited 0: "104 published require entry point(s)
across 66 package(s) load".
- `check:platform-checklist` exits 1 on a finding this diff does not
reach. See Acceptance notes.
- **Reverse verification** of the moved fixture, after the commit. The
fixture's carrier was swapped back to the flipped row through
`scripts/ablation-replace.mjs` (anchor `setEvidence(root, 'flow',
'active', ` hit ×1 → ×0, replacement ×0 → ×1, blob `9ff17ad4` →
`1091717a`). The case went **red**: `expected 1 to be +0`, and the gate
named `flow/description →
packages/plugins/driver-sql/src/sql-driver.ts`. That is the old fixture
failing because the row is now scanned as `live`. The tool restored the
file: blob after restore `9ff17ad4` equals `HEAD`, and `git diff HEAD`
is empty. The direction observed was a turn to red, as expected.
- Upstream check: `origin/main` has moved to `6427e2cf56` since the base
`fb386074f5`. None of the six paths changed upstream (`git diff
--name-only BASE origin/main -- PATHS` is empty), so the regenerated
counts need no merge.
## Acceptance notes
- **`check:platform-checklist` is red on the base and does not involve
this diff.** `docs/qa/platform-checklist/areas/identity-auth.json`
anchors `packages/plugins/plugin-auth/src/auth-plugin.ts#twoFactor`.
Since `7d63088958` (PR #20429), that symbol exists in the file only as a
member inside a `patch` object, which `symbol-anchors.mjs` does not
accept as a declaration. The files that finding names (the checklist
area, `auth-plugin.ts`, `scripts/check-platform-checklist.mjs`,
`scripts/symbol-anchors.mjs`, `scripts/checklist-select.mjs`) are
byte-identical between this branch's base `fb386074f5` and `origin/main`
`1378ec7c0c`, and none of the six paths here is among them. Carrier: the
plugin-auth / checklist owner. Not filed here.
- **Mentions left as they are, because the flip does not make them
false.** `docs/audits/2026-06-flowschema-property-liveness.md` records
the dated 2026-06 audit and is not a current-state claim.
`.claude/skills/spec-property-retirement/SKILL.md` §0 uses `hook.label`
/ `flow.description` as the example of "build the renderer, do not
retire", and that is what happened. `liveness/app.json`
(`areas.description`) and `liveness/job.json` cite "the hook.label
precedent" for "docs-shaped, kept, not warned", and that is still true.
- **Observation, objectui side (not filed).** `QuickFind.tsx`'s docblock
calls it a "Cmd+K palette", but it binds Cmd+Shift+M, to leave Cmd+K to
`CommandPalette`. The rows say "metadata quick-find" and name no key.
- Also noted: `hook` still has no registered metadata-admin preview. A
`HookPreview` is not needed for these rows, and objectui#11027 already
excludes it.
---
_Generated by [Claude
Code](https://claude.ai/code/session_014EJ1ED8X4MMrT18BhVx4tx)_
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent b057434 commit 7e5246d
7 files changed
Lines changed: 46 additions & 16 deletions
File tree
- .changeset
- packages/spec
- liveness
- state-counts
- scripts/liveness
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
908 | 908 | | |
909 | 909 | | |
910 | 910 | | |
911 | | - | |
| 911 | + | |
912 | 912 | | |
913 | | - | |
| 913 | + | |
914 | 914 | | |
915 | 915 | | |
916 | 916 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
23 | 23 | | |
24 | 24 | | |
25 | 25 | | |
26 | | - | |
27 | | - | |
28 | | - | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
29 | 32 | | |
30 | 33 | | |
31 | 34 | | |
| |||
0 commit comments