You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Maintainer ruling 2026-09-02 (option A, ADR-0049 enforce-or-remove),
12
+
re-affirmed 2026-09-03 as A1 with the carriers a published narrowing owes
13
+
once the type was measured as public API: the member was written by the two
14
+
posture-conditional API-key refusals (`organization_required` at admission,
15
+
`organization_membership_ended` after grants) since #8287 and read by nothing
16
+
— zero runtime readers across every transport and consumer in the repo for
17
+
its whole life; only test assertions ever looked at it.
18
+
19
+
What changes:
20
+
21
+
-`ResolvedAuthzContext` no longer declares `authRefusal`. Code that reads
22
+
`ctx.authRefusal` stops compiling (`TS2339`); at runtime the property was
23
+
already absent from every resolved context except the two refused ones.
24
+
- The two refusals themselves are UNCHANGED: they still fire, still fail
25
+
closed (no `userId`, empty grants), and every transport still answers the
26
+
generic anonymous `401 UNAUTHENTICATED`. No status code, body or header
27
+
moves — a holder of someone else's key learns nothing, exactly as before.
28
+
- The refusal REASON is observable on exactly one surface, and it is not the
29
+
envelope: the server-side `[security] API key refused (reason) ...``warn`
30
+
line at the decision point (#15256 / 2A), which names the key row id,
31
+
principal and organization for the operator. The pins that kept the two
32
+
reasons distinguishable through the field now read that line.
33
+
-`ApiKeyRefusalReason` and `ApiKeyAdmission` are unchanged — the reason
34
+
vocabulary still exists; it just no longer has a copy on the resolved
35
+
context.
36
+
37
+
**Migration.** A consumer that read `ctx.authRefusal` deletes the read; there
38
+
is no replacement on the envelope, by design — disclosing the reason to a
39
+
caller (option B) was ruled out as a security-boundary question, and the
40
+
recorded fallback if a reader ever appears is an audit-side outlet (option C),
41
+
never the wire. Fail-closed handling keys on the absent `userId`, as every
42
+
in-repo transport already did. An operator who needs the reason reads the
43
+
server log line.
44
+
45
+
<!-- adr-0087: not-required (runtime-interface-only packages/core/src/security/resolve-authz-context.ts#ResolvedAuthzContext) A published runtime TypeScript interface lost an optional member. No Zod schema, no `packages/spec` declaration, no object definition and no stored representation is touched — `ResolvedAuthzContext` is a plain interface in `packages/core`, projected from no schema and referenced by no metadata surface — so `objectstack migrate meta` has nothing to rewrite and there is no tombstone to mint. The channel that reaches an affected consumer is the compiler at the read site (`TS2339`), which is more precise than a ledger line. The in-repo census (zero runtime readers; the only readers were test assertions, relocated onto the `warnApiKeyRefusal` line) and the workspace typecheck are recorded on the PR. -->
`DatasetMeasureSchema.format` now documents what a DATE-valued measure can and cannot say, and the numeral-pattern examples no longer stand as the whole story.
6
+
7
+
The field was silent about date measures while advertising `e.g. "$0,0.00", "0.0%"` — the pattern grammar a date measure is precisely unable to read. An author with a `min` / `max` over a date field read that line, wrote `format: 'YYYY-MM-DD'`, parsed clean, and got the locale default.
8
+
9
+
The statement is carried by a `.describe()` where there was none, so it reaches the published surfaces an author actually reads: the generated JSON Schema (`json-schema/ui/Dataset.json`, `DatasetMeasure.json`) and the reference table in `content/docs/references/ui/dataset.mdx`, whose Description cell for `format` had been rendering the silence as a blank. The docblock above it carries the longer measured record.
10
+
11
+
What it now says, measured rather than assumed against the objectui pin this repo builds against: a numeral pattern applies to a numeric measure; a date-valued measure never reads a date PATTERN — a date-only value reads `format` as a display STYLE (`short`, `relative`), and a datetime value ignores `format` altogether.
12
+
13
+
Nothing accepts or rejects differently: `format` remains `z.string().optional()` and no measure is refused. Documentation over a published schema (objectui#7178 ruled A).
`GetMetaItemsRequestSchema.organizationId` no longer documents itself as always consulted.
6
+
7
+
The published `describe()` opened with "Selects the org partition in the ADR-0005 overlay read order" and closed with "Absent = environment-wide read: only env-level overlays apply and no org partition is consulted." Stating only the absent case invites the converse, and an integrator reading it completes it as *present ⇒ consulted* — so a caller who supplies an organization believes it has scoped a read that can in fact be environment-wide. A supplied organization is not consulted on every `getMetaItems` read.
8
+
9
+
The corrected text qualifies the promise instead of implying its converse: the parameter selects the org partition **when an org partition applies**, and supplying a value "does not by itself guarantee an org partition is consulted; where none applies, and whenever it is absent, the read is environment-wide and only env-level overlays apply."
10
+
11
+
Prose only. No key is added, removed or renamed, no export moves, no accept set changes and no runtime behaviour changes — the schema's shape and validation are byte-for-byte what they were. What ships is the JSON-Schema `description` for the existing `organizationId` key and the matching row in the generated API reference, which is why this is user-visible enough to owe an entry and narrow enough to be a patch.
12
+
13
+
The three sibling `organizationId` describes on `GetMetaItemRequestSchema`, `GetMetaItemLayeredRequestSchema` and `GetMetaItemCachedRequestSchema` are deliberately left alone.
`KnowledgeRefreshPolicy.cron` no longer tells authors that the `cron` dialect engine judges their syntax "when the expression is evaluated". Both halves of that sentence were false: nothing evaluates `refresh.cron` — `service-knowledge` reads `refresh.onRecordChange` and never `refresh.cron` — and `@objectstack/formula`'s registered `cron` engine has no caller outside that package, so it was never going to issue that verdict either. The claim shipped to authors through the generated reference page (`content/docs/references/ai/knowledge-source.mdx`), naming both an engine that never sees the value and an event that never happens.
6
+
7
+
The docblock, the `.describe()` and the slot's two pin-test comments now say what is true today, matching the wording of the already-corrected Expression Protocol dialect table: cron syntax is not checked at parse time and no engine evaluates this slot — `croner` judges a cron pattern only where a schedule is wired (`CronSchedule.expression`, a different slot) — so the verdict belongs to whatever external scheduler the author hands the value to. Documentation only: no exported symbol, no authorable key and no accept-set movement; the parse behaviour is byte-for-byte unchanged, and the pin that proves `'not a cron'` still normalizes is untouched.
`navigationContributions[].group` now documents the mis-aimed case, not only the omitted one (#14925)
6
+
7
+
The `describe()` on that key said what happens when `group` is **omitted** and nothing about what happens when it is **present and names no group the target app declares** — which is the case that actually bites. A contributing package cannot see the target app's group ids at authoring time (the target app belongs to another package), so a wrong id is undetectable by reading the contributor's own source; and the platform **relocates** the items to the app's top level rather than refusing them, so the menu renders, a smoke test passes, and the information architecture has silently changed.
8
+
9
+
The description now names that third case: it is **not refused**, the items are appended at the app top level anyway, and a `nav_contribution_group_missing` diagnostic is emitted — by the runtime at `warn`, and by **both**`os build` and `os validate` at compile time, in each command's `--json` payload under the existing `warnings` key.
10
+
11
+
Prose only. `group` remains `SnakeCaseIdentifierSchema.optional()`, the accept set is unchanged and nothing is refused that was not refused before; the recorded authorable key surface (`authorable-surface.json`) and the schema manifest (`json-schema.manifest.json`) are byte-identical. What moves is the string an author reads: the generated reference rows in `content/docs/references/ui/app.mdx` and `content/docs/references/kernel/manifest.mdx`, and the `description` on the published JSON Schemas that embed `NavigationContribution` (its own schema, the bundled `objectstack.json`, and 22 `json-schema/api/*` and `json-schema/kernel/*` package envelopes).
0 commit comments