Skip to content

Commit 7c98551

Browse files
objectstack-agentclaude
andcommitted
fix(spec): the liveness governance denominator is the authorable set, and every run prints it
Claude-Session: https://claude.ai/code/session_01JbZnqu8bt6YqfJsr9vaFb3 Co-authored-by: Claude <noreply@anthropic.com>
1 parent 220b424 commit 7c98551

2 files changed

Lines changed: 71 additions & 11 deletions

File tree

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
---
2+
'@objectstack/spec': patch
3+
---
4+
5+
The liveness ledger's published README no longer claims the metadata-type registry is "exactly the set of authorable metadata types" — the governance denominator is now that set, and every run prints it
6+
7+
`check-liveness.mts` built its coverage denominator from
8+
`listMetadataTypeSchemaTypes()` under a comment stating that function returns
9+
"exactly the set of *authorable* metadata types", and the ledger README carried
10+
the same sentence. It is false in a specific, load-bearing way: that function
11+
deliberately does not enumerate `UNREGISTERED_KIND_SCHEMAS` — enrolling those
12+
entries there "would claim a status this change is careful not to grant" — while
13+
the kinds bound in that map are authored on every boot through their stack
14+
collections (`connectors:`, `sharingRules:`, `analyticsCubes:`, `webhooks:`) and
15+
on every write through `PUT /api/v1/meta/:type/:name`, whose `resolveOverlaySchema`
16+
resolves them through `getMetadataTypeSchema()`.
17+
18+
So `connector`, `sharing_rule` and `analytics_cube` sat in **neither** `GOVERNED`
19+
**nor** `PENDING_GOVERNANCE`, and a type in no bucket produces no row in any of
20+
this gate's lists. The blindness was therefore invisible in the gate's own
21+
output: `ungoverned: []` read exactly the same whether the gate had looked and
22+
found nothing or had never looked at all.
23+
24+
The denominator is now `authorableTypes()` — the registered kinds UNION
25+
`listUnregisteredKindSchemaTypes()`, the enumeration helper that exists so a check
26+
can read that map and which grants nothing by listing a name. The registry itself
27+
is untouched: no kind is registered, no enum grows, no create seed is demanded and
28+
no accept set moves, and the same split already landed one gate over as
29+
`reachabilityRootTypes()` in `build-schemas.ts`. The three newly visible types are
30+
recorded as declared debts with a reason and an issue number apiece, which is what
31+
the ratchet asks for and what the README now says; the direction of travel is out
32+
of that map and into `GOVERNED`.
33+
34+
Every run also prints the denominator and its composition unconditionally. That
35+
line used to appear only when `PENDING_GOVERNANCE` was non-empty, so the one state
36+
worth reporting — "N authorable types looked at, none unaccounted for" — rendered
37+
as nothing at all, which is the same silence an unseen type produces.

‎packages/spec/liveness/README.md‎

Lines changed: 34 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,26 @@ route decides the disposition — see `scripts/liveness/orphans.mts` and
2828
The gate reads `BUILTIN_METADATA_TYPE_SCHEMAS` (`packages/spec/src/kernel/metadata-type-schemas.ts`)
2929
via `listMetadataTypeSchemaTypes()` / `getMetadataTypeSchema()` — **the same registry the
3030
runtime `/api/v1/meta/types/:type` endpoint and the Studio metadata-admin forms use**,
31-
i.e. exactly the set of *authorable* metadata types. It walks each type's Zod schema
31+
i.e. the set of *registered* metadata KINDS. It walks each type's Zod schema
3232
directly (not `z.toJSONSchema`, which throws on `object`/`action`).
3333

3434
This matters: the older gate read the generated `json-schema/` directory, which omits
3535
most top-level authorable types (object/field/flow/action/...) — so it was blind to the
36-
core surface. The registry is complete.
36+
core surface. The registry is complete *as a registry*.
37+
38+
⛔ **Registered is not the same set as authorable, and this page used to say it was** —
39+
"i.e. exactly the set of *authorable* metadata types" was the sentence #17356 measured
40+
false for the reachability gate and #18133 for this one. `listMetadataTypeSchemaTypes()`
41+
deliberately does not enumerate `UNREGISTERED_KIND_SCHEMAS` (#6245: enrolling those
42+
entries there "would claim a status this change is careful not to grant"), yet the kinds
43+
bound in that map are authored on every boot through their stack collections
44+
(`connectors:` / `sharingRules:` / `analyticsCubes:` / `webhooks:`) and on every write
45+
through `PUT /api/v1/meta/:type/:name`. So the WALK is registry-rooted, as above, while
46+
the **governance denominator** — whom a ledger must exist for — is the registered kinds
47+
UNION `listUnregisteredKindSchemaTypes()` (#6931), computed by `authorableTypes()` in
48+
`check-liveness.mts`. Every run prints that denominator and how it is composed, because
49+
a type in no bucket produces no row anywhere: without the printed count, "nothing
50+
ungoverned here" and "never looked" are the same output.
3751

3852
**Spec-only exception (`SPEC_ONLY_SCHEMAS`).** A type can be authorable yet deliberately
3953
*not* registered — `webhook` is the case: its schema is authored on a Stack/connector but
@@ -929,12 +943,21 @@ misleading entry carries `authorWarn` so authors hear about it at compile time
929943
(governed types with warn entries must also be registered in the CLI lint's
930944
`TYPE_COLLECTIONS` — see lint-liveness-properties.ts).
931945

932-
**Coverage is complete as of #4488**: every type in the metadata-type registry
933-
is governed, and `PENDING_GOVERNANCE` in `check-liveness.mts` is empty. The map
934-
itself stays, because the ratchet is the point — registering a new type without
935-
a ledger fails CI with instructions to govern it or record the debt (reason +
936-
issue number). The paragraph that used to sit here, listing nine ungoverned
937-
types as prose, is precisely how the gap survived for a year: prose cannot fail
938-
a build. Now the gate compares `GOVERNED` against the registry in both
939-
directions (an ungoverned registered type fails; so does a stale pending row
940-
whose debt is already paid).
946+
**Every registered type has been governed since #4488**, which emptied
947+
`PENDING_GOVERNANCE` of all nine debts the map opened with. The map itself stays,
948+
because the ratchet is the point — registering a new type without a ledger fails
949+
CI with instructions to govern it or record the debt (reason + issue number). The
950+
paragraph that used to sit here, listing nine ungoverned types as prose, is
951+
precisely how the gap survived for a year: prose cannot fail a build. Now the gate
952+
compares `GOVERNED` against the denominator in both directions (an ungoverned
953+
authorable type fails; so does a stale pending row whose debt is already paid).
954+
955+
⚠️ **The map is no longer empty, and that is #18133's finding rather than a
956+
regression.** Widening the denominator from the registered kinds to the authorable
957+
set (see the ⛔ note under *Source of truth* above) made three types visible that
958+
had been in **neither** `GOVERNED` **nor** `PENDING_GOVERNANCE` — `connector`,
959+
`sharing_rule` and `analytics_cube` — and therefore produced no row in any of the
960+
gate's lists while the report read complete. They are now declared debts with a
961+
reason and an issue number apiece, which is the state this ratchet exists to
962+
produce; the direction of travel is out of that map and into `GOVERNED`, exactly
963+
as it was for the nine. ⛔ Their presence is not a licence to leave them there.

0 commit comments

Comments
 (0)