Repository navigation
Commit e901c27
fix(spec): the protocol 16 → 17 conversion summaries and three descriptions state each decision in words instead of a tracker number (stage 4) (#21555)
Part of #20749
Clause-②: no
Stage 4 of the `domain:spec` lane's share of the runtime-string
burn-down (ruling `5902360492`, form D): class (b) of the stage-3
census, the text `packages/spec/src` shows authors and administrators.
Every rewritten string now states in words what the cited decision was,
or drops a citation its sentence already explained. Text only.
## What changed
- **56 ADR-0087 conversion summaries** in
`packages/spec/src/conversions/registry.ts` (68 tracker ids): every
protocol 16 → 17 summary that carried an id. A summary is the "Change"
column of `docs/protocol-upgrade-guide.md`, the `to` text of
`spec-changes.json`'s `converted[]` records and what `os migrate meta
--json` reports under `specChanges`, so it is read by an author
upgrading metadata.
- **Three descriptions** (3 ids): `FieldSchema.autonumberFormat`'s
`.meta()` description (`data/field.zod.ts`), and the route descriptions
of `GET /:type/:name/layers` and `POST /:type/:name/publish`
(`api/plugin-rest-api.zod.ts`).
- **Generated, by `check:generated --fix`** (exactly the three artifacts
it proved stale): `docs/protocol-upgrade-guide.md` (56 rows),
`packages/spec/spec-changes.json` (56 summaries, twice each: the
per-major record and the aggregate), and the `autonumberFormat` rows of
`content/docs/references/data/field.mdx`, `data/object.mdx` and
`system/migration.mdx`.
- One `@objectstack/spec` **patch** changeset, `Clause-②: no`.
## Size: the split (A2)
At this base the class (b) population is unchanged from the stage-3
census: 91 conversion summaries with 108 ids (81 distinct cards) plus 3
descriptions with 3 ids. That is over the ~60-card bar, so this PR
delivers up to a protocol-step boundary, lowest step first:
| protocol step | summaries with ids | ids | distinct cards |
|---|--:|--:|--:|
| toMajor 11 / 13 / 14 / 15 | 0 | 0 | 0 |
| **toMajor 17 (delivered)** | **56** | **68** | **48** |
| toMajor 18 (next stage) | 35 | 40 | 34 (33 not cited in 17) |
The three descriptions ride this part (3 more cards, no overlap): 59
messages, 71 ids, 51 distinct cards delivered. The next stage's exact
list is the 35 toMajor-18 summaries at the end of this body.
## Delivered: each site, the decision read, the new words
Every cited card was read through REST with all comments; the record
column names the comment (or commit) the decision was read from. Where a
summary already said why, the citation is dropped and the sentence kept.
Placeholders `OBJECT_NAME` / `RULE_NAME` below stand for the
angle-bracket spelling in the source.
| conversion (head line) | cited | decision as read (record) | summary
now reads |
|---|---|---|---|
| `action-execute-to-target` (:727) | #3713 | `execute` is the
deprecated alias of `target`; spec and objectui resolved the pair in
opposite directions; align on the spec rule and drop the alias so the
divergence is unrepresentable (body (closed completed, no comments)) |
action key 'execute' → 'target' (the deprecated handler alias; the spec
and the renderer had resolved the pair in opposite directions, so one
key now names the handler) |
| `field-conditionalRequired-to-requiredWhen` (:767) | #3754 | same
fold-and-drop as #3713: `requiredWhen` canonical, alias folded and
dropped from parsed output (body (closed completed, no comments)) |
field key 'conditionalRequired' → 'requiredWhen' (the deprecated
predicate alias, folded into the canonical key so no reader picks its
own precedence) |
| `agent-tools-to-skills` (:822) | #3894 | ADR-0109 accepted;
`agent.tools[]` removed because it resolved names against the full
registry with no surface check, breaking ADR-0064 (tool set = union of
skills' tools) (PR body (merged)) | agent key 'tools' removed — declare
capability in a skill (ADR-0064: an agent's tools are exactly its
skills' tools, and this inline slot resolved names against the whole
registry with no surface check) |
| `sharing-rule-access-level-full-to-edit` (:881) | #3865 | route B is
the end state: sharing grants read/edit only; delete, transfer and
re-share come from object permissions, ownership and admin scope; `full`
→ `edit` is lossless (5105498900) | sharing-rule accessLevel 'full' →
'edit' (`full` never granted more than `edit`; a sharing rule grants
read or edit, while delete and transfer come from object permissions and
ownership) |
| `flow-node-crud-object-alias` (:954) | #3796 | the seven aliases (six
open-coded `??` + the shim's last `object`) graduate straight into the
D2 layer; the `readAliasedConfig` shim is deleted (5125152179) | CRUD
flow-node config key 'object' → 'objectName' (the last alias in the
executors' `readAliasedConfig` shim graduates into this layer, and the
shim is deleted) |
| `flow-node-notify-config-aliases` (:1077) | #3796, #4045 | #3796: `??`
fallbacks graduate, `actionUrl` canonical (downstream chain uses it).
#4045: `notify.source` was a read-but-undeclared shape → conversion
layer, not configSchema (5125152179; 5127636357, 5138487536) | notify
flow-node config keys 'to' → 'recipients', 'subject' → 'title', 'body' →
'message', 'url' → 'actionUrl' (executor `??` fallbacks graduated into
this layer; `actionUrl` is canonical because the notification chain
downstream already uses it), and nested 'source: {object, id}' →
'sourceObject' / 'sourceId' (a shape the executor read that no config
schema declared) |
| `flow-node-wait-event-config-lift` (:1288) | #4045 |
`node.config.eventType` etc. were an undeclared second contract beside
the declared `waitEventConfig`; graduate them (#4161) (5130277099,
5138487536) | wait flow-node loose config keys → the declared
`waitEventConfig` block: 'eventType', 'timerDuration'/'duration' →
'timerDuration', 'signalName'/'signal' → 'signalName', 'timeoutMs' (the
executor also read these keys from the loose config, a second contract
beside the declared block) |
| `flow-node-map-flow-alias` (:1364) | #4045 | reconciliation found the
`map.flow` alias (undeclared executor fallback); graduated (#4228)
(5138487536) | map flow-node config key 'flow' → 'flowName' (an
undeclared spelling the executor accepted through a bare fallback; it
graduates into this layer) |
| `flow-node-subflow-flow-alias` (:1421) | #4278 | reconcile the
schemaless nodes' forms with their executors and check `subflow`; its
bare `flowName ?? flow` fallback graduates (body (closed completed, no
comments) + conversion docblock) | subflow flow-node config key 'flow' →
'flowName' (an undeclared spelling the executor accepted through a bare
fallback, found when the schemaless nodes were reconciled with their
executors; it graduates into this layer) |
| `flow-node-connector-config-lift` (:1529) | #4045 | executor reads
only `connectorConfig`; the descriptor schema rooted the triple at
`config`, so the Studio form wrote unread keys; descriptor stops
publishing, stored loose keys lift (5132926517, 5138487536) |
connector_action flow-node loose config keys 'connectorId' / 'actionId'
/ 'input' → the declared `connectorConfig` block (the executor reads
only that block; the published designer form had been writing these keys
where nothing read them) |
| `flow-node-script-config-aliases` (:1641) | #3796 | as above:
open-coded `??` fallbacks graduate into the D2 layer (5125152179) |
script flow-node config keys 'functionName' → 'function', 'input' →
'inputs' (executor `??` fallbacks, graduated into this layer) |
| `app-dead-authoring-keys-removed` (:1742) | #4001, #4509, #4667, #4709
| liveness audits: keys unread or wrongly encoded are removed; #4709:
the "no shell read homePageId" premise was false, ruling B keeps the
retirement (an ID cross-reference that dangles is the wrong encoding)
(5158421901 (#4509), 5159774792 (#4667), 5164227920 (#4709 ruling B)) |
app keys
'version'/'aria'/'objects'/'apis'/'sharing'/'embed'/'mobileNavigation'/'homePageId'
plus contextSelectors 'includeAll'/'placement' and areas 'order' removed
(liveness audits found each one unread or wrongly encoded; sharing/embed
declared a public surface no route enforced, mobileNavigation was fully
unimplemented, includeAll was deliberately disobeyed because an 'All'
row would clear a mandatory scope, homePageId WAS read by objectui's
console before v17 but encoded the landing page as an ID cross-reference
that silently fell back when it dangled — the landing page is the first
nav item (the first retirement record said nothing read it, a premise
since corrected; the retirement stands), and no renderer ever sorted
areas) |
| `app-area-fail-open-gates-removed` (:1853) | #4651 | ruling B: remove
both keys; removing a fail-open gate is strictly safer than keeping it;
prescription names the two enforced layers (5160072589) |
navigation-area keys 'visible'/'requiredPermissions' removed (ADR-0049 —
FAIL-OPEN access gates: no layer ever read them, so a 'hidden' or
permission-gated area was served and rendered to every user, while the
identically named keys on a navigation ITEM and on the APP are enforced;
gate the items inside the area, or gate the app) |
| `permission-rls-priority-removed` (:1955) | #3896 | the #3896
security-audit line: rls.priority promised conflict resolution that
cannot exist (policies OR-combine) and had no reader; removed (card body
+ commits d6bfb3d (#3990), eb95d97 (#3998)) | RLS-policy key
'priority' removed (a security audit found no reader: policies
OR-combine, so the promised conflict-resolution semantics cannot exist;
dropping it changes no outcome) |
| `tool-inert-authoring-keys-removed` (:2024) | #3896 | the audit
close-out removes the four inert tool keys (permissions gated nothing,
active:false withdrew nothing) (commit eb95d97 (#3998)) | tool keys
'category'/'permissions'/'active'/'builtIn' removed (authorable and
inert, so removed under ADR-0049 enforce-or-remove; permissions gated
nothing, active:false withdrew nothing) |
| `action-inert-keys-removed` (:2137) | #3896 | close-out sweep:
enforce-or-remove worklist, fourteen inert authoring keys leave the
surface (commit 12a19a8 (#4054)) | action keys
'shortcut'/'bulkEnabled' removed (inert, removed under ADR-0049
enforce-or-remove: no keydown path dispatches shortcuts; the
multi-select toolbar reads the view's bulkActions) |
| `flow-inert-keys-removed` (:2162) | #3896 | as above (commit
12a19a8 (#4054)) | flow keys 'active'/'template', node 'outputSchema'
and errorHandling 'fallbackNodeId' removed (inert, removed under
ADR-0049 enforce-or-remove: active:false never stopped a flow; status is
the enforced lifecycle) |
| `view-inert-keys-removed` (:2221) | #3896 | as above (commit
12a19a8 (#4054)) | view keys removed as inert (ADR-0049
enforce-or-remove): list 'responsive'/'performance', form
'defaultSort'/'aria' — no renderer read them (list aria/data and form
data stay live) |
| `view-list-passthrough-keys-removed` (:2267) | #7176 | maintainer
ruling: retire under ADR-0049; pass-through-only reads are dead in
effect (5236139723) | view list keys removed:
'striped'/'bordered'/'virtualScroll' — every measured reader copied the
key forward and none applied it (a key that is only passed through is
dead in effect; ADR-0049 enforce-or-remove) |
| `view-export-options-pdf-removed` (:2335) | #8010, #1301 | option A;
`pdf` leaves the enum (honest narrowing, not a runtime console.warn);
PDF export declined (5270998514; #1301 is not planned) | list-view
export format 'pdf' removed (PDF export was declined as not planned, and
ObjectGrid dropped the declared format from the menu with only a runtime
console.warn; an honest enum replaces that warning) |
| `dashboard-inert-keys-removed` (:2404) | #3896 | close-out sweep (as
above) (commit 12a19a8 (#4054)) | dashboard keys 'aria'/'performance'
and widget 'performance' removed (inert, removed under ADR-0049
enforce-or-remove: no renderer applied any of them) |
| `dashboard-widget-responsive-removed` (:2474) | #4876, #11027 | #4876
ruling A: retire widget `responsive` (no reader); #11027 ruling B:
retire `page.components[].responsive`, equally unread (5169512655;
5380752244) | dashboard widget key 'responsive' removed (no renderer
ever applied per-widget breakpoint overrides; the
page.components[].responsive key this entry once deferred to was
measured equally unread and retired at protocol 18) |
| `dashboard-widget-action-aria-removed` (:2555) | #5010 | retire the
four dead widget keys; colorVariant kept (body ruling + 5179556002) |
dashboard widget keys 'actionUrl'/'actionType'/'actionIcon' and 'aria'
removed (no renderer ever drew a per-widget action button, and widget
ARIA attributes never reached the DOM; use header.actions[] and the
widget title/description) |
| `dashboard-widget-compareto-converged` (:2652) | #5011 | converge
`compareTo` on the implemented executor contract `{ kind, dimension? }`;
`1y` rewrites, other offsets delegated (5173559485) | dashboard widget
'compareTo' converged on the executor's { kind, dimension? } contract
(the shape the dataset executor implements; the bare strings and {
offset: '1y' } rewrite mechanically; other { offset } durations have no
faithful target and are reported, not guessed) |
| `agent-knowledge-removed` (:2726) | #3896 | close-out sweep (as above)
(commit 12a19a8 (#4054)) | agent key 'knowledge' removed (inert,
removed under ADR-0049 enforce-or-remove: declaring sources/indexes
never scoped retrieval; restrict at the knowledge-service level) |
| `skill-trigger-phrases-removed` (:2744) | #3896 | close-out sweep (as
above) (commit 12a19a8 (#4054)) | skill key 'triggerPhrases' removed
(inert, removed under ADR-0049 enforce-or-remove: activation is
triggerConditions + the agent's skills[] allowlist; phrases were a
dead-end projection) |
| `stack-api-require-auth-removed` (:2782) | #3963 | delete the
`api.requireAuth` opt-out; anonymous always denied; public surfaces
derive authorization from a declaration (form, share link, book
audience) (body (decision recorded in body)) | stack key
'api.requireAuth' removed — anonymous access is always denied; publish
public surfaces by declaration (a public form, a share link or
`book.audience: 'public'`), which replaced the deployment-wide opt-out |
| `flow-node-wait-timeout-keys-removed` (:2864) | #4158 | wait never had
a timeout: withdraw the contract (route B), `timeoutMs` moves to
`timerDuration` (body (closed completed) + conversion docblock) |
waitEventConfig keys 'timeoutMs' (→ 'timerDuration', stringified — its
only reader used it as the duration) and 'onTimeout' (removed — zero
readers, so no timeout ever fired): wait never had a timeout, so its
timeout contract is withdrawn rather than built |
| `datasource-inert-blocks-removed` (:2944) | #4583 | all 20 dead
datasource keys removed; each job already has a different live mechanism
(5157934690) | datasource keys 'retryPolicy'/'healthCheck' and external
'label'/'requirePermission' removed (nothing retried, nothing probed on
a schedule, and the federation label/permission were read by nobody;
each of those jobs already has a live mechanism) |
| `mapping-inert-keys-removed` (:3032) | #4509 | the three mapping keys
retire (schema defaults made authorWarn impossible; removal is the only
signal) (5158421901, 5158744185) | mapping keys
'extractQuery'/'errorPolicy'/'batchSize' removed (no exporter reads a
mapping, error handling belongs to the import request, and the write
path sizes its own batches) |
| `book-translations-removed` (:3087) | #4667 | six dead authorWarn keys
retired (5159774792) | book keys 'translations' (book-level and
group-level) removed (no resolver read them; the tree endpoint and
portal render labels verbatim, so a localized book served its authoring
locale to everyone). Localize the docs instead: `doc.translations` is
live |
| `job-id-removed` (:3149) | #4667 | as above (5159774792) | job key
'id' removed (nothing read it; `name` is the job's identity everywhere,
so two jobs differing only in `id` were the same job, and the key's own
description advertised an override that did not exist) |
| `translation-validation-messages-removed` (:3206) | #4667, #3778,
#14381 | #4667 retire; #3778 legacy-key table had pointed `errors` at
it; #14381 an object-scoped key ships with its reader (ADR-0049
enforced) (5159774792; #3778 body; 5503980929) | translation key
'validationMessages' removed (no resolver read it, so a translated rule
message was stored and never shown; the legacy-key table of the
translation-bundle migration had been steering retired `errors:` authors
into it). Author the message on the rule itself
(`object.validations[].message`), and translate it under the
object-scoped group
`objects.OBJECT_NAME._validations.RULE_NAME.message`, which the write
path resolves (17.3.0, a translation key shipped together with its
reader) |
| `datasource-capabilities-removed` (:3264) | #4583 | as above
(5157934690) | datasource key 'capabilities' removed (eleven flags no
code read; pushdown comes from the driver's own supports.*, and
`readOnly` never made anything read-only) |
| `datasource-read-replicas-removed` (:3319) | #4468 | remove:
read-replica routing is an unbuilt feature (5150771330) | datasource key
'readReplicas' removed (no driver opened a replica connection and no
query path splits reads from writes; front replicas behind one endpoint
and point `config` at it) |
| `datasource-config-driver-key-aliases` (:3419) | #4456 | the factory's
undeclared `??` fallbacks graduate to a D2 entry and are deleted from
the reader (5157922838) | datasource config keys → canonical per driver:
sqlite 'file'/'database' → 'filename', postgres/mysql 'connectionString'
→ 'url' and 'user' → 'username', mongo 'uri' → 'url' and 'user' →
'username' (undeclared driver-factory `??` fallbacks, graduated into
this layer and deleted from the reader) |
| `flow-node-script-branch-keys-removed` (:3665) | #4343 | operator
ruling: `script` converges to a pure function-call node; the five branch
keys retire (5151704360) | script flow-node config keys 'actionType' (→
'function' when it was shorthand for one; otherwise removed —
'email'/'slack' were logger-backed stubs that delivered nothing), plus
'template' / 'recipients' / 'variables' (fed those stubs) and 'script'
(inline JS the runtime never executed); script is now a pure
function-call node, the only path that ran real logic |
| `object-managed-by-system-to-system-data` (:3762) | #3355 | retire
`system`, new value `system-data` (not `platform-data`) (5157022965) |
object managedBy 'system' → 'system-data' (ADR-0103's residual bucket
named the engine-owned half v16 had already moved out to `engine-owned`;
the rename leaves the name describing what the bucket actually holds:
admin/user-writable platform data) |
| `object-enable-trash-mru-removed` (:3829) | #3207, #2377 | remove
`enable.trash` / `enable.mru`; soft delete stays parked; last slice of
the dead-property removals (5156966571, 5161298967; 5051634768) | object
capability flags 'enable.trash'/'enable.mru' removed (the last slice of
the dead author-facing property removals: no recycle bin and no MRU
tracking ever ran; both default-true flags gated nothing) |
| `object-index-type-partial-removed` (:3912) | #5248, #4943 | remove
both index keys; no DDL consumer; return enforce-first on real demand
(5199336983; 5194762679 (duplicate)) | object index keys
'indexes[].type'/'indexes[].partial' removed (no driver ever read
either: the index method is the dialect's choice and a partial index is
built by a database-layer migration, not declared) |
| `retry-policy-converged` (:4070) | #4661, #4964 | one RetryPolicy
declaration, `backoffMs`, merged default 0 / 1 with pre-17 job defaults
written out; flow.errorHandling joins, default 0 (silent retry can
double-write) (5158710540 (analysis); 5173148383) | retry policy unified
across job.retryPolicy, try_catch retry and flow.errorHandling: base
delay 'retryDelayMs' → 'backoffMs', and the pre-17 job defaults
(maxRetries 3, backoffMultiplier 2) written out explicitly now that the
merged default is 0 / 1: two declarations that differed only by accident
became one, and retry is opt-in because a retry replays whatever the
attempt already did |
| `hook-body-crypto-hash-removed` (:4249) | #4391 | remove the
capability token and its build-time inference (5156969500) | script-body
capability token 'crypto.hash' removed (the sandbox never installed
ctx.crypto.hash, so the token granted a call that always threw; the CLI
inferred it too) |
| `dataset-measure-array-string-agg-removed` (:4419) | #6188 | retire
`array_agg` / `string_agg`; keep and enforce `count_distinct`
(5219849918) | dataset measure aggregates 'array_agg' / 'string_agg'
removed (no SQL backend compiled them and the v1 dataset runtime refused
them by name, so a measure declaring one never produced a value; the
measure is dropped, and with it any derived measure left referencing it)
|
| `connector-rate-limit-config-removed` (:4544) | #4911 | outbound
rate-limit vocabulary removed: no engine exists (implementation-first)
(body ruling + 5169405647) | connector key 'rateLimitConfig' removed (no
outbound rate-limiting engine exists; the runtime's only token bucket
limits INBOUND requests, so every knob here was inert while reading like
a configured cap. The whole ConnectorRateLimitConfig shape went with it)
|
| `field-mapping-transform-removed` (:4669) | #5552, #3278 |
enforce-or-remove: all five members dead, the union retires; `js`
dialect was retired as redundant with the L2 script body (5199338349;
#3278 body) | field-mapping key 'transform' removed (the whole
five-member FieldMappingTransform union went with it: no runtime ever
executed constant/cast/lookup/javascript/map, and the javascript member
advertised dialect="js", a dialect already retired because JavaScript
belongs in a script body. The enforced transform pipeline is the import
mapping's string-enum `mapping.fieldMapping[].transform`, which is
unaffected) |
| `theme-inert-token-scales-removed` (:4786) | #5021 | retire all nine
token groups; re-declare under `customVars` (5175091297) | theme keys
'typography.fontSize'/'fontWeight'/'lineHeight'/'letterSpacing',
'typography.fontFamily.heading'/'mono', 'animation' and 'zIndex' removed
(ADR-0049 — the engine emitted --font-size-*, --font-weight-*,
--line-height-*, --letter-spacing-*, --duration-*, --timing-*, --z-*,
--font-heading and --font-mono faithfully, and no first-party component
or stylesheet has ever read one. Re-declare any variable you actually
consume under customVars, which emits it verbatim) |
| `page-header-subtitle-alias` (:4940) | #3226 | route B: a D2
conversion rewrites `description` → `subtitle`; the consumer's bare `??`
retires (5160118898, 5194297145) | page-header component prop
'description' → 'subtitle' (the off-spec spelling a renderer tolerated
through a bare `subtitle ?? description` fallback; `subtitle` is the
declared key, and the fallback retires) |
| `record-picker-display-field-to-label-field` (:5165) | #5775 |
direction A: `labelField` (the delivered spelling) becomes canonical;
`displayField` retires via conversion (5202137085) | record-picker
component prop 'displayField' → 'labelField' (the required key no
renderer read; `labelField ?? 'name'` is what renders the row, so the
delivered spelling became the declared one) |
| `record-picker-inert-keys-removed` (:5287) | #5775 | `searchFields` /
`multiple` retire (zero readers) (5202137085) | record-picker component
props 'searchFields'/'multiple' removed (the control is a plain
single-select with no search box; neither key had a reader) |
| `page-card-body-to-children` (:5414) | #5775 | `children` is the one
composition key (5202137085) | page:card component prop 'body' →
'children' (one composition key across every container; the card
renderer already reads both) |
| `inline-action-api-params-to-body-extra` (:5582) | #5777 | direction
A: the static payload gets its own key (`bodyExtra`); `params` keeps one
meaning (5202138112, 5228796853) | inline type:'api' action prop
'params' (object form) → 'bodyExtra' (a static payload and a parameter
definition are two things, so the payload gets its own key; `params`
stays the ActionParam[] definition array) |
| `page-tabs-type-to-tab-style` (:5847) | #6776 | Route A: rename to the
spelling the renderer reads (5229120747, 5229693342) | page:tabs
component prop 'type' → 'tabStyle' (a props key named `type` collides
with the node's dispatch key and is unauthorable in flat/JSX carriers;
`tabStyle` is the spelling the renderer reads in all of them) |
| `page-structure-inert-keys-removed` (:6035) | #6946 | retire three
zero-reader UI keys (body (maintainer ruling) + 5232767409) |
page:header prop 'icon' and page:card prop 'actions' removed (neither
has a renderer read point in objectui; the header resolves icons per
action and the card renders title/children/footer only) |
| `record-details-layout-removed` (:6201) | #6946 | as above (as above)
| record:details component prop 'layout' removed (the declared
auto\|custom modes were never implemented; the renderer branches only on
inline\|compact, values the schema never permitted, so both legal values
selected nothing) |
| `app-hidden-to-unpublished` (:6343) | #4829 | A1: a machine-managed
key carries the publish gate; `hidden` back to navigation presentation
only; ADR-0045 amended (5173161521) | stored app publish gate 'hidden' →
'_unpublished' (ADR-0045 amended — `hidden` carried BOTH the publish
gate and 'keep out of the App Switcher', so the built-in Account app was
withheld from every non-builder; the gate is now the machine-managed
`_unpublished`, and `hidden` is navigation presentation only, never an
access gate. Stored rows only — an authored `hidden: true` is left
untouched) |
| `action-global-nav-location-removed` (:6456) | #6888 | direction 2:
retire `global_nav` (no demand; the designer previewed a surface the
product lacks) (5229990375) | action location 'global_nav' removed (no
running-app surface rendered it; the ⌘K palette reads no action
metadata, while the Studio designer previewed a command-palette frame
for it. The value is stripped and the key kept, so an action left with
no location becomes the documented headless shape `locations: []`) |
Descriptions:
| site | cited | decision as read (record) | change |
|---|---|---|---|
| `data/field.zod.ts` `autonumberFormat` | #6555 | route 3: `{0000}` is
a declared contract default, and both hand-written fallbacks read it
(5225535766, family done 5240072006) | "⇒ the contract default `{0000}`,
which every driver and the engine fallback read, so one field numbers
alike on every backend." |
| `api/plugin-rest-api.zod.ts` `GET /:type/:name/layers` | #5882 |
ruling B: its own `/layers` path and schema, one route one shape
(recorded 5216370790) | "…hence its own path and its own response
schema, since one route answers one shape." |
| `api/plugin-rest-api.zod.ts` `POST /:type/:name/publish` | #7294 |
declare the served publish route's response, the save door's discipline
(5237410722) | "The route was served for a long time with no declaration
behind it — this entry is what makes its response contract nameable, the
same declared-equals-returned rule the save door follows." |
## Text-only proof (A4)
Stage 3's AST-skeleton + string-text tool (`skeleton.cjs`, one line
changed: the TypeScript 6.0.3 load path), BASE `1ac7308d7a` against this
branch: **3 of 3 SAME** on both legs, exit 0 each — `registry.ts` 62346
tokens, 4920 string groups, 56 changed; `field.zod.ts` 8682 / 581 / 1;
`plugin-rest-api.zod.ts` 4912 / 475 / 2 (one literal re-split into three
`+` pieces, which the skeleton reads as one string); parse diagnostics 0
/ 0. Every changed group carried an id before and carries none after;
every other string is byte-identical. Controls on scratch copies of the
head `registry.ts`, each mutation counted on disk first: an identifier
rename → DIFF exit 1; `!==` flipped to `===` → DIFF exit 1; one summary
re-split into two `+` operands → SAME exit 0; a `surface` string (never
carried an id) changed → text leg VIOLATION exit 1. No repo file was
mutated for the controls.
Census after the edit (stage 3's instrument, unchanged): non-test 219 →
160 messages, 429 → 358 ids; class (b) conversion summaries 91 / 108 →
35 / 40 (all toMajor 18); descriptions 3 / 3 → 0.
## Pins and quotes (A6)
- **Tests:** no test asserts a changed summary or description phrase.
The id-bearing fragments and the distinctive phrases of all 59 messages
were searched across every `*.test.*` / `*.spec.*`; the hits are other
files' own prose with their own citations (test titles and comments such
as `(#3896 close-out)` in `view.test.ts`), not quotes of a summary.
- **`content/docs/**`:** the only quotes are the three generated
`references/**` pages, regenerated.
`content/docs/releases/v17/17-0.mdx:1052` repeats one action-key phrase
with its own `(#3896 close-out)`; it is release-owned and untouched.
- **`skills/**`:** no quote of any changed text.
## Gates
`node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack
--commands` at `db81973cf6` (9 paths, 493 changed lines): 105 commands,
each run from the worktree with its exit code written before any pipe;
`--ran` → "✓ dispatch-gates --ran: 105 derived famil(ies) accounted for
— 105 run, 0 NOT-MEASURED". Three first exited 3 (prerequisite: unbuilt
lint / client packages) and were re-run green after the full package
build (71 tasks). Plus `@objectstack/spec` build, `check:generated`
("All 15 generated artifacts are up to date"), the package `test`
project (603 files, 17845 passed), 48 of the 51 `test:repo` files, and
`typecheck`. Details are in the report on the card.
## Acceptance notes
- **The `#3896` citations.** Eight summaries cited `#3896` as an "audit"
or a "close-out". The card's own body is the sharing-rule REST finding
that opened that security-audit line; the decisions the summaries cited
(remove `rls.priority`, the four inert tool keys, then the fourteen-key
enforce-or-remove sweep) are recorded in the landed commits
`d6bfb3d0ab`, `eb95d97c02` and `12a19a88a5`. Each summary already said
why its key went, so the new words name the rule (ADR-0049
enforce-or-remove) rather than the card.
- Comments in `conversions/registry.ts` still carry tracker ids; they
belong to #20234's comment stages and are untouched. So is
`migrations/registry.ts`.
- `docs/protocol-upgrade-guide.md` is not a governed surface
(`check-governed-merges`' register).
- **Hot file:** open PR #21547 (#21459) also edits
`conversions/registry.ts`: it adds one toMajor-18 entry and its ordering
row, in a region this PR does not touch. A local `git merge-tree` of the
two heads is clean (the file is hand-written, so no merge driver is
involved). Neither PR's spec-changes / upgrade-guide output includes the
other's entries, so whichever lands second merges `main` and regenerates
them.
## Next stage: the 35 toMajor-18 summaries (file:line at this head · ids
· conversion)
- `registry.ts:6550` #8321 `field-malformed-scale-precision-removed`
- `registry.ts:6658` #8762 `record-chatter-position-vocabulary`
- `registry.ts:6777` #9198 `element-input-target-variable-removed`
- `registry.ts:7024` #9220 `element-filter-removed`
- `registry.ts:7177` #9249 `element-form-removed`
- `registry.ts:7355` #15178,#19620
`translation-per-app-settings-removed`
- `registry.ts:7609` #9249 `translation-component-submit-label-removed`
- `registry.ts:7782` #3951,#9227 `field-column-lists-canonicalized`
- `registry.ts:7909` #10414 `metric-filters-removed`
- `registry.ts:8104` #17296 `cube-sub-day-granularities-removed`
- `registry.ts:8237` #18612 `cube-join-sql-and-relationship-removed`
- `registry.ts:8502` #10054 `record-highlights-field-icon-removed`
- `registry.ts:8759` #11027 `page-component-responsive-removed`
- `registry.ts:8860` #11805 `object-grid-default-sort-removed`
- `registry.ts:9047` #21445 `object-grid-resizable-columns-removed`
- `registry.ts:9250` #17260 `object-kanban-quick-add-removed`
- `registry.ts:9563` #12497,#1883
`permission-allow-restore-purge-removed`
- `registry.ts:9881` #6837 `field-reference-to-alias`
- `registry.ts:10301` #14478 `hook-timeout-to-timeout-ms`
- `registry.ts:10342` #14478 `job-timeout-to-timeout-ms`
- `registry.ts:10946` #14478
`api-endpoint-cache-ttl-to-cache-ttl-seconds`
- `registry.ts:11015` #14478
`dashboard-refresh-interval-to-refresh-interval-seconds`
- `registry.ts:11376` #14478
`memory-persistence-auto-save-interval-to-ms`
- `registry.ts:11600` #14478 `turso-config-timeout-to-timeout-ms`
- `registry.ts:11690` #17063 `view-page-mount-removed`
- `registry.ts:11795` #17053,#8221
`list-view-sort-string-clause-to-array`
- `registry.ts:12295` #19054 `object-tenancy-organization-field-removed`
- `registry.ts:12405` #20085 `view-item-owner-hidden-removed`
- `registry.ts:12549` #20230 `view-overlay-owner-hidden-removed`
- `registry.ts:13137` #6206,#17321
`page-component-filter-record-to-rule-array`
- `registry.ts:13392` #20161 `report-joined-chart-removed`
- `registry.ts:13657` #20221 `form-layout-inline-grid-to-vertical`
- `registry.ts:13813` #19992 `currency-config-precision-removed`
- `registry.ts:13917` #20321 `permission-rls-tags-removed`
- `registry.ts:14056` #15429 `flow-decision-mode-inclusive-explicit`
---
_Generated by [Claude
Code](https://claude.ai/code/session_01YDt3PzwfrkuFzUBF89WPmM)_
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent 81e69ca commit e901c27
9 files changed
Lines changed: 256 additions & 237 deletions
File tree
- .changeset
- content/docs/references
- data
- system
- docs
- packages/spec
- src
- api
- conversions
- data
Lines changed: 18 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
118 | 118 | | |
119 | 119 | | |
120 | 120 | | |
121 | | - | |
| 121 | + | |
122 | 122 | | |
123 | 123 | | |
124 | 124 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
282 | 282 | | |
283 | 283 | | |
284 | 284 | | |
285 | | - | |
| 285 | + | |
286 | 286 | | |
287 | 287 | | |
288 | 288 | | |
| |||
616 | 616 | | |
617 | 617 | | |
618 | 618 | | |
619 | | - | |
| 619 | + | |
620 | 620 | | |
621 | 621 | | |
622 | 622 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
119 | 119 | | |
120 | 120 | | |
121 | 121 | | |
122 | | - | |
| 122 | + | |
123 | 123 | | |
124 | 124 | | |
125 | 125 | | |
| |||
541 | 541 | | |
542 | 542 | | |
543 | 543 | | |
544 | | - | |
| 544 | + | |
545 | 545 | | |
546 | 546 | | |
547 | 547 | | |
| |||
0 commit comments