Skip to content

Commit cb919dc

Browse files
committed
Merge origin/main into claude/issue-20751-services-strings-stage5
Brings in stage 4 of the services lane and the plugin-security changes landed since the branch was cut. Only the prose-id ledger conflicted; it is recomputed with --census-ledger on the merged tree (shrink only: the plugin-security entries go to zero, no other entry moves). Claude-Session: https://claude.ai/code/session_01DiCSbmJrkzNhuEAier4VoJ Co-authored-by: Claude <noreply@anthropic.com>
2 parents 25723d0 + f9a8eb8 commit cb919dc

451 files changed

Lines changed: 19697 additions & 3707 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
---
2+
'@objectstack/platform-objects': patch
3+
---
4+
5+
Provenance comments in `@objectstack/platform-objects` cite the commits that decided them, not tracker numbers that no longer resolve
6+
7+
Clause-②: no
8+
9+
Docblocks and comments across the package cited issue-tracker numbers that now answer 404 on GitHub.
10+
Each now cites the commit in this repository's history that made the decision it describes, except one
11+
that cites ADR-0104's 2026-09-05 addendum, the record of that ruling. Some of these docblocks sit on
12+
exported members, so the reworded text appears in the published declaration files (`apps`, `identity`,
13+
`metadata-translations` and `system` `index.d.ts` / `index.d.mts`), and the field comments esbuild keeps
14+
appear in the JavaScript output (`index`, `apps`, `audit`, `identity` and `plugin`, `.js` / `.mjs`).
15+
16+
Comment only: no export, type, error code, status, message text or runtime behaviour changes.
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
---
2+
'@objectstack/lint': patch
3+
---
4+
5+
Flow, hook, action, approval and expression rule findings no longer cite tracker numbers; each one states the decision behind it in words
6+
7+
Clause-②: no
8+
9+
Some findings these rules show to authors through `os validate`, `os lint` and `os build`, and the startup-registry findings a plugin author reads, pointed at an issue-tracker number for the reason behind them. The number goes; where the sentence did not already say what was decided, it now does.
10+
11+
- Flow patterns: the record-change date-equality hint and the date-equality filter hint name the declarative alternative, a `schedule` flow whose start node carries a `config.timeRelative` descriptor; the unscoped `runAs` hint says `runAs` is enforced, so a run with no trigger user has its data operations refused rather than run unscoped; the unbounded bulk-write hint says `multi: true` is how a flow declares bulk intent and that the engine admits a whole-object write declared that way; the revise-target hint says the run-resume route continues a pause on a service-owned node type only through the service that owns it; the two interpolation hints say a flow node value is a string template in which only single-brace tokens resolve.
12+
- Startup-registry findings: the open-vocabulary notes say the engine judges node types only once the vocabulary is sealed at `kernel:bootstrapped`; the prescription describes the lazy cache resolution and the ADR-0104 attestation by what each does; the assertive-wording finding describes its two incidents, and how each was fixed, in words.
13+
- Expression findings: the field-level `visibleWhen` consequence names the `current_user` binding ADR-0089 D1 gives every runtime record surface; the retired `script` keys finding says spec 17 made `script` a call to a registered function and nothing else.
14+
- Trigger readiness: the array `triggerType` hint says multi-event arrays are deferred until two independent projects need a combination other than created-or-updated.
15+
- Body writes, readonly writes and approvals: the discarded `ctx.record` write says the snapshot stays read-only by design and an action writes through `ctx.api`; the `readonlyWhen` write finding says a bulk update strips the field from every matched row once any one of them is locked; the `queue` approver finding says the type was deprecated rather than built; the empty-slate hint says the admin override may act on any pending request, so that one nobody in its slate can decide never stays stuck.
16+
- The other findings drop a citation the sentence already explained.
17+
18+
Text only: no rule id, severity, condition or finding moves. A tool or test that matches the old text (for example a tracker-number suffix) needs the new spelling.
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
---
2+
'@objectstack/lint': patch
3+
---
4+
5+
Data-model, filter, predicate, search, sort, security, seed, view, widget and registry findings no longer cite tracker numbers; each one states the decision behind it in words
6+
7+
Clause-②: no
8+
9+
The remaining `@objectstack/lint` findings that `os validate`, `os lint` and `os build` show to authors, plus the `surfaceReason` texts of the exported `AUTHORING_RULES` registry and one integrity error, pointed at an issue-tracker number for the reason behind them. The number goes; where the sentence did not already say what was decided, it now does.
10+
11+
- Data model: the bare declared `unique: true` warning says that protocol 18 rejects the spelling and that stored metadata still carrying it converts to `unique: 'global'`, which builds the same physical index.
12+
- Empty filter combinators: the `$and: []`, `$or: []` and empty-node messages say every backend reduces an empty combinator to its boolean identity; the `$or: []` message says an empty disjunction never opens a read scope to the whole table.
13+
- Null guards: the fail-closed outcome says a predicate that cannot evaluate refuses the write rather than being skipped.
14+
- Visibility and metadata-form predicates: the fall-open consequence says failing open is the console's settled behaviour; the dotted right-hand-side message says the form evaluator keeps its right-hand side a literal by design and says why only in a development build.
15+
- Component props: the advisory hint says props are judged at the authoring door as a warning before they become an error.
16+
- Rule schema formats: the format hint says `rule-validator.ts` registers the default `ajv-formats` set so that a `format` is enforced on every write.
17+
- Security posture: the unset-OWD message describes the leave_request incident (an object with no `sharingModel` let an ordinary read/write grant read and edit every other user's records); the `controlled_by_parent` message says the write is refused as a metadata defect rather than a permission denial.
18+
- Seeds and views: the seed state-machine message says a seed records established facts rather than walking the lifecycle; the `views:` container message says the stack schema, the rule and the registration loop hold `views:` to one container-only contract.
19+
- React pages: the absent-`groupBy` hint states the ruling directly.
20+
- Liveness: the unrecognised-status integrity error says such a status fails loudly rather than being graded `dead`.
21+
- `AUTHORING_RULES` `surfaceReason` texts: the full-snapshot, capability-reference and sharing-rule reasons name the runtime publish gate (the Studio, REST and MCP door that runs this registry) in place of a tracker number; the advisory-volume reason says the object door opened to the gating object rules alone; the component-types reason names the crossing discipline the gating object rules went through.
22+
- The other findings (search fields, sort fields, nav servability, dashboard actions, widget bindings and the remaining predicate and combinator messages) drop a citation the sentence already explained.
23+
24+
Text only: no rule id, severity, condition, finding or registry field moves. A tool or test that matches the old text (for example a tracker-number suffix) needs the new spelling.
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
---
2+
'@objectstack/service-automation': patch
3+
'@objectstack/plugin-audit': patch
4+
---
5+
6+
Automation refusals, prescriptions, log lines and run-object field help, and the activity type help, no longer cite tracker numbers; each one states the decision behind it in words
7+
8+
Clause-②: no
9+
10+
Some strings these two packages show to flow authors, operators and administrators pointed at an issue-tracker number for the reason behind them. The number goes; where the sentence did not already say what was decided, it now does.
11+
12+
- `@objectstack/service-automation`: the refusal for a `fieldValues` write map says a runtime alias for it was rejected by design, so the node keeps one strict `fields` key; the refusal for a screen field's `visibleIf` says a predicate under any other key is never read, so the field always shows, and a `required` field meant to stay hidden then blocks the screen from ever being submitted; the undeclared-config-key refusal says the built-in node types were reconciled so that every key their executors read is declared; the unknown-function error in a flow value expression says such a name is refused rather than evaluated to null, which would write the field as undefined; the inert-connector warning says entries without a `provider` are catalog descriptors, while an entry that names a `provider` is a connector instance that provider's installed executor materializes; the `sys_automation_run` field help says the paused node's type decides who may continue a run (an approval pause only through its owning service), that rows written before run history recorded its trigger were not backfilled, and that a finished run's bounded step log keeps its per-node detail across a restart; three bridge debug lines say what each bridge provides. The bulk-intent guidance, the degraded-connector dispatch error and retry lines, the user-less `runAs` warning and refusal, the unclaimed-branch warning, the script-function and node-config refusals and the `sys_flow_dispatch` description drop their citations.
13+
- `@objectstack/plugin-audit`: the `sys_activity` `type` help, whose English text all four shipped locale bundles carry, says the vocabulary is open by decision, not a gap awaiting enforcement.
14+
15+
Text only: no status, error code, field, route or control flow moves. A client or log filter that matches the old text (for example a tracker-number suffix) needs the new spelling.
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
---
2+
'@objectstack/spec': patch
3+
'@objectstack/lint': patch
4+
---
5+
6+
`page.requires` says what the runtime now does with it: refused at save, reported at load (ADR-0080 §5).
7+
8+
Clause-②: no
9+
10+
The key's description used to say the list is "validated at save and load" while the liveness ledger recorded it as not enforced yet. Both are now true and say so. On a server that has the deployment's SDUI component manifest, saving a `kind: 'html'` page compiles its source, refuses a written `requires` that disagrees with it (`422 INVALID_METADATA`, `page-requires-disagrees-with-source`; a draft at its publish) and stores the derived list. At load, a stored page whose list names a plugin no manifest component carries is reported and still served. A server with no manifest checks neither and says so once at boot. Omit `requires`: it is derived from the source. The liveness row moves from `planned` to `live`, and the generated page reference carries the new description.
11+
12+
`validateJsxPages`' reason for staying off the runtime publish gate no longer says it parses through `typescript`/`sucrase`. It parses with the dependency-free `@objectstack/sdui-parser`, and it stays CLI-only because the save door already runs that compiler on every html page. The `ui-html-page-div-refused` upgrade-guide entry now names that save door too: on a server with a manifest, a `div` page saved from Studio or through the metadata API is refused under the same rule ids.
13+
14+
No schema accepts or refuses anything it did not before, and no runtime behaviour changes.
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
---
2+
'@objectstack/service-analytics': minor
3+
---
4+
5+
fix(service-analytics)!: both analytics strategies refuse a cube measure whose `type` names no aggregate, in the spec's words — the custom-SQL `EXPRESSION_METRIC_TYPES` partition is gone with the three types it named (#21000)
6+
7+
**BREAKING** — `@objectstack/spec` retired the cube metric types `number`, `string`
8+
and `boolean` from `AggregationMetricType` (a measure's `sql` is a column reference,
9+
so they had nothing left to compute). Every door that parses a cube refuses them;
10+
this release removes the runtime branches that still served them for a cube that
11+
reached the analytics service WITHOUT meeting that parse — one a host registers
12+
in-process from a literal, through `AnalyticsServicePlugin({ cubes })` or
13+
`AnalyticsService({ cubes })` (the registry never parses).
14+
15+
| | before | now |
16+
| --- | --- | --- |
17+
| `NativeSQLStrategy`, a measure typed `number` / `string` / `boolean` | served: the column emitted UNAGGREGATED in the statement (`amount AS "m"` beside `GROUP BY`) | refused, nothing executed |
18+
| `ObjectQLStrategy`, the same measure | refused `INVALID_FIELD` / 400 | refused, nothing executed |
19+
| either strategy, a type the spec never declared (`median`) | native: refused; ObjectQL: forwarded to `executeAggregate` as the method (the auto-bridge refused it; a host's own executor received it), and `/analytics/sql` echoed `MEDIAN(amount)` | refused, nothing executed |
20+
21+
**The one refusal** is `aggregateOfMeasure`'s, shared by both strategies and both
22+
doors (`POST /analytics/query` and `POST /analytics/sql`): it names the measure and
23+
the cube, then quotes the spec's own verdict on the type — for a retired type the
24+
retirement prescription (the six aggregates to choose from, and where a per-row or
25+
derived value goes instead), for anything else zod's message listing the six. It is
26+
a bare `Error`, the undeclared-500 tier this package assigns to a cube that never
27+
met the parse, so the HTTP answer is `500` with the message readable in the body
28+
(measured through the dispatcher's analytics route), never a caller-blaming `400`.
29+
The ObjectQL envelope for the three retired types therefore moves from
30+
`INVALID_FIELD` / 400 to that tier.
31+
32+
**The fix:** give the measure one of the six aggregate types — `count`, `sum`,
33+
`avg`, `min`, `max`, `count_distinct` — or parse the cube through `CubeSchema`
34+
before registering it, which refuses the same types with the same prescription.
35+
36+
**Removed export:** `EXPRESSION_METRIC_TYPES` from
37+
`strategies/native-sql-strategy.ts` (internal to the package; not re-exported from
38+
its entry point). **Unchanged:** every aggregate measure on both strategies, the
39+
auto-bridge's own parse of an engine method (still pinned, driven directly), and
40+
`GET /analytics/meta`, which keeps publishing each registered measure's `type` as
41+
registered.
42+
43+
Clause-②: no (narrowing)
44+
45+
<!-- adr-0087: registered cube-metric-expression-types-retired -->
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
---
2+
'@objectstack/spec': minor
3+
---
4+
5+
feat(spec)!: retire the cube metric types `number`, `string` and `boolean` — a measure's `sql` is a column reference, so the custom-SQL-expression types had nothing left to compute (#21000)
6+
7+
**BREAKING** — three members leave `AggregationMetricType`, so a cube measure's
8+
`measures.<metric>.type` no longer accepts `number`, `string` or `boolean`. ADR-0049
9+
enforce-or-remove. They declared "a custom SQL expression returning a number /
10+
string / boolean": the measure's `sql` was the whole computation. A cube member's
11+
`sql` is a column reference since `cube-member-sql-expression-retired` (#20943), so
12+
the three were left naming nothing: measured before this change, the raw-SQL
13+
analytics path returned the referenced column UNAGGREGATED (a bare column in a
14+
grouped statement — by SQL's own rules an error on PostgreSQL and an arbitrary row's
15+
value on SQLite), and the ObjectQL path refused the measure. The six aggregates — `count`, `sum`,
16+
`avg`, `min`, `max`, `count_distinct` — are unchanged and are now the whole
17+
vocabulary.
18+
19+
### FROM → TO
20+
21+
| removed | what to write instead |
22+
| --- | --- |
23+
| `measures.<metric>.type: 'number'`, `'string'` or `'boolean'` | the aggregate the measure means: `sum`, `avg`, `min` or `max` over the column; `count` (over `'*'` for a row count, or over a column for its non-null values); or `count_distinct`. |
24+
| a measure whose old expression computed a value per row | keep that value as a field of the object (a stored or formula field) and aggregate the field. |
25+
| a measure whose old expression combined measures (a ratio, a difference) | `derived: { op, of: [...] }` on an ADR-0021 dataset over the same object. |
26+
27+
**The one-line fix: give the measure an aggregate type.** There is no mechanical
28+
rewrite — the column alone does not say whether `amount` meant its sum, its average
29+
or its largest value — so `os migrate meta` lists nothing for this change.
30+
31+
Each retired member is refused at parse with a prescription naming the six
32+
aggregates, at the measure's `type`, and in `tsc` (the members are gone from the
33+
`AggregationMetricType` type). A value the enum never declared keeps zod's own
34+
message.
35+
36+
### The retirement kit
37+
38+
- **Value-level retirement.** `AggregationMetricType` is declared through
39+
`enumWithRetiredValues` (`shared/retired-key.ts`), with the prescriptions
40+
module-private. No authorable KEY and no def changed, so nothing lands in
41+
`RETIRED_KEYS_BY_MAJOR`, and the four surface ratchets (`api-surface`,
42+
`authorable-surface`, `json-schema.manifest`, `api-surface-signatures`) are
43+
byte-identical.
44+
- **No D2 conversion, by design.** A stored or built cube that still carries one of
45+
the three is REFUSED, never rewritten or dropped: the boot door
46+
(`ObjectStackDefinitionSchema`, which a built artifact is parsed through), the
47+
`analytics_cube` write door and `defineStack` refuse it with the prescription, and
48+
the rehydration seam replays no conversion over it.
49+
- **D3 entry `cube-metric-expression-types-retired`**, with its step-18 rationale
50+
fragment, carries the judgement the upgrader owes: which aggregate each measure
51+
meant.
52+
- **Liveness.** The `analytics_cube` row `measures.type` stays `live`, re-verified
53+
2026-10-02, with the narrowing recorded.
54+
- **Docs.** The `data/analytics` reference page is regenerated.
55+
- **No deprecation window**, per the project's startup-stage posture.
56+
57+
### Reach, measured
58+
59+
- This repository authors no cube measure of the three types outside tests:
60+
`examples/**`, `packages/**` (the platform objects included) and the skills and
61+
docs carry none. The showcase cube's `type: 'string'` entries are dimensions,
62+
whose `DimensionType` is a separate enum and is unchanged.
63+
- objectui at its pinned commit carries no `AggregationMetricType` mirror and no
64+
cube measure of the three types.
65+
- Out-of-repo authored cubes: NOT MEASURED.
66+
67+
Clause-②: no (narrowing)
68+
69+
<!-- adr-0087: registered cube-metric-expression-types-retired -->

0 commit comments

Comments
 (0)