Skip to content

Commit 0e54448

Browse files
committed
Merge remote-tracking branch 'origin/main' into claude/project-thread-2l4nno
2 parents 4ba9107 + 06cccd3 commit 0e54448

47 files changed

Lines changed: 2506 additions & 137 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: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
---
2+
'create-objectstack': patch
3+
---
4+
5+
Clause-②: no
6+
Metadata-protocol: none
7+
8+
The blank template now says its `note` object is a sample: the generated README gains a "The sample `note` object" section with the steps to delete or replace it, and `src/objects/note.object.ts` opens with a comment saying the same. A developer no longer finds a `note` object in their app, over REST or in an MCP client's object list, without anything telling them it came from the scaffold.
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
---
2+
'@objectstack/service-messaging': patch
3+
'@objectstack/mcp': patch
4+
---
5+
6+
fix: the inbox's own notification list no longer shows the raw topic, and an MCP argument refusal ends at the fix
7+
8+
Clause-②: no
9+
Metadata-protocol: none
10+
11+
- **`@objectstack/service-messaging`.** The `mine` list view of `sys_inbox_message` (a person's own notifications) showed a Topic column reading the producer's machine id, such as `notify` for a flow's notify node or `collab.mention` for a mention. Nothing labels those ids, so the column is gone from that view: it now shows the title, the severity and the time. The `topic` field itself is unchanged and still searchable, so a filter or another view can use it.
12+
- **`@objectstack/mcp`.** When a tool call carries an argument the tool does not declare, the refusal names the argument and the spelling to send, and now stops there. It no longer ends with the sentence "This argument used to be dropped silently and the call still succeeded, …", which explained the history of the check to internal developers and gave the caller nothing to act on. Every tool still refuses an undeclared argument exactly as before; only the closing sentence is gone.
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
---
2+
'@objectstack/platform-objects': patch
3+
---
4+
5+
fix(platform-objects): Edit organization's Parent Organization and Sort Order help says they matter only where the deployment runs several organizations
6+
7+
Clause-②: no
8+
Metadata-protocol: none
9+
10+
On a deployment with one organization, Edit organization still shows 上级组织 / Parent Organization and 排序 / Sort Order, and nothing told the administrator they do nothing there. Their help text, in the object declaration and in the `en` / `zh-CN` packs, now ends with "Meaningful only where the deployment runs several organizations." (「仅在部署运行多个组织时有意义。」). No field, permission or behaviour changes; the fields stay visible.
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
---
2+
'@objectstack/service-messaging': patch
3+
---
4+
5+
i18n(service-messaging): the notification objects and the digest heading ship in Russian, German and Korean
6+
7+
Clause-②: no
8+
Metadata-protocol: none
9+
10+
The messaging objects and the digest heading "You have {{count}} notifications" now carry `ru`, `de` and `ko` as well, so the package ships all eleven platform languages. `t()` selects no plural form, so the Russian heading is worded as a tally (`Уведомлений: {{count}}`), correct for every count. Each new locale is registered under its platform-table tag with its source-hash companion and its messages, and the package's extract command lists the tags, so the existing `check:i18n` bundle check covers them. Second half of package 5 of #30512.
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
---
2+
'@objectstack/spec': minor
3+
---
4+
5+
feat(spec): a blueprint field carries a typed `defaultValue`, so an AI-built boolean flag defaults to false
6+
7+
Clause-②: yes (widening)
8+
Metadata-protocol: added
9+
Skills-docs: none — no file under `skills/**` restates a blueprint field key (`skills/objectstack-ai/references/_index.md` only points at `solution-blueprint.zod.ts`); the reference page `content/docs/references/ai/solution-blueprint.mdx` is regenerated from the new describe.
10+
11+
The AI solution blueprint had no slot for a field's default. A blueprint that wrote `{ type: 'boolean', defaultValue: false }` for a 是否完成 / 已付款 / `is_active` flag, as the build guidance teaches, parsed with `success: true` and lost the key, so the built field had no default and every record created without the flag read empty instead of false. A filter on false missed those records.
12+
13+
- **`defaultValue` on `BlueprintFieldSchema` and on the strict structured-output mirror's fields (`SolutionBlueprintStrictSchema.objects[].fields[]`).** Both sides declare it as one scalar union, `boolean | number | string`. Lenient: optional. Strict: required and nullable (null = no default), as with every other key in that mirror. The describe says the value becomes the built field's `defaultValue`, and states the boolean-flag rule: a yes/no flag is a `boolean` field that is never `required` and carries `defaultValue: false`.
14+
- **The value is checked against the field's `type`, on the lenient side.** The check is the field schema's own literal contract (`checkLiteralDefaultValue`, the one `FieldSchema` runs on a built field's `defaultValue`), so a default the blueprint admits is one the built field admits. These types take a default: `boolean` / `toggle` take true or false; `number` / `currency` / `percent` / `rating` / `slider` / `progress` take a number; `text` / `textarea` / `email` / `url` / `phone` / `markdown` / `html` / `richtext` / `code` / `color` take a string; `time` takes `"HH:MM"`; `select` / `radio` take the `value` of one of the field's own `options`, never a label.
15+
- **Every other type is refused by name, never stripped.** The refusal is a `custom` issue at the field's `defaultValue` that names the field and its type, says why, and lists the types that take one: a `date` / `datetime` literal is frozen at design time; a reference or file type names a record or file that does not exist yet; a `multiselect` / `checkboxes` / `tags` field stores a list; a `formula` / `summary` / `autonumber` field is computed; `password` / `secret`, `signature` / `qrcode` and the structured JSON types have no one value right for every record. A runtime token (`NOW()`, `current_user`) is refused too: a blueprint default is a literal.
16+
- The strict mirror cannot read `type` while it judges this key, so it can generate a value the lenient schema then refuses. `apply_blueprint` parses the lenient schema, and that refusal names the type at the key.
17+
18+
Additive only: every blueprint that parsed before still parses with the lenient schema. A producer of the STRICT shape must now send `defaultValue` on each field (null when it has none). The cloud build applier is what carries the value into the built field; it moves on its own pin.
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
---
2+
'@objectstack/spec': patch
3+
---
4+
5+
fix(spec): the liveness ledger's `Seed.locale` row names the six seed-load request builders and where each one's locale comes from, so it no longer says the marketplace install passes none
6+
7+
Clause-②: no
8+
Metadata-protocol: none
9+
10+
- `liveness/seed.json`'s `locale` row said three publish/install-time paths pass no locale, the marketplace install among them. The install paths now pick seed datasets by locale: the marketplace install, the sample-data reseed, the restart heal and the per-organization replay each resolve one (the organization's Localization setting, then the user's language, then the app's `i18n.defaultLocale`). The boot seed and the dev hot-reload seeder still use the app's `i18n.defaultLocale`. Package apply and draft publish still pass none.
11+
- The earlier ruling that publish and install are locale-neutral is kept as recorded, followed by a dated line saying its bound now covers package apply and draft publish only. The note also says why the loader's no-locale warning stays.
12+
- Nothing to do on upgrade: only the shipped `liveness/seed.json` note text and its `verifiedAt` date change. No key, schema shape or export changes.
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
---
2+
'@objectstack/core': patch
3+
---
4+
5+
fix(core): a banned user's assigned-work count and hand-over now include the platform's injected `owner_id`
6+
7+
Clause-②: no
8+
Metadata-protocol: none
9+
10+
The field picker behind the assigned-work count and the bulk hand-over skipped every `system: true` user reference. The `owner_id` the platform injects on every business object is `system: true` (it is a platform column) but not `readonly` (ownership is transferable), so it was skipped: on any object whose author had not declared `owner_id` themselves, the records a banned user owned were counted as zero and never handed over. The picker now leaves out `readonly` user references only, so `created_by` / `updated_by` stay out and the injected `owner_id` is counted and handed over. Platform tables (`sys_*`), `managedBy` objects and injected columns with no storage stay excluded as before.

‎.github/workflows/ci.yml‎

Lines changed: 45 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -114,26 +114,43 @@ concurrency:
114114
cancel-in-progress: true
115115

116116
# ── THE TWO SPEEDS (#22998) ──────────────────────────────────────────────────
117-
# A `pull_request` or `merge_group` run carries the FAST SET: `Build Core`,
118-
# `Test Core` (affected-only) and `Build Docs`. `main`'s 4-hourly `schedule` run,
119-
# its `push` runs and an on-demand `workflow_dispatch` carry the FULL BATTERY:
120-
# the fast set plus the four jobs below that spell
117+
# A `pull_request` run carries the FAST SET: `Build Core`, `Test Core`
118+
# (affected-only) and `Build Docs`. A `merge_group` run carries the fast set
119+
# plus the three `dogfood` shards (#30461, ruling record 6663883327, letter B),
120+
# whose job alone spells
121+
#
122+
# github.event_name != 'pull_request'
123+
#
124+
# in its `if:`, so a dogfood red shows on the queue build of the entry that
125+
# carries it rather than first on `main`'s push run after it. It is advisory
126+
# there: the context is outside the required set (below), so the queue may
127+
# merge the entry before or despite the shards.
128+
# `main`'s 4-hourly `schedule` run, its `push` runs and an on-demand
129+
# `workflow_dispatch` carry the FULL BATTERY: all of that plus the three jobs
130+
# below that spell
121131
#
122132
# github.event_name != 'pull_request' && github.event_name != 'merge_group'
123133
#
124-
# in their `if:` — `temporal-conformance`, `dogfood` (three shards),
125-
# `dogfood-verify` and `ui-published-artifacts`. Nothing is deleted: the jobs
126-
# moved off the per-PR path, the last one never had one (#26717). A red in one of them is a main-red for the owning lane, and the
127-
# publish lane reads the full battery before its approval (release.yml's
128-
# `full-battery` job, `scripts/ci/scheduled-full-run.mjs --release-gate`).
134+
# in their `if:` — `temporal-conformance`, `dogfood-verify` and
135+
# `ui-published-artifacts`. Nothing is deleted: the jobs moved off the per-PR
136+
# path, the last one never had one (#26717). A red in one of them is a main-red
137+
# for the owning lane, and the publish lane reads the full battery before its
138+
# approval (release.yml's `full-battery` job,
139+
# `scripts/ci/scheduled-full-run.mjs --release-gate`). ⛔ No affected-only
140+
# dogfood filter on the queue: the shards run on every entry, through the
141+
# `core` output, which is 'true' on `merge_group` (letter C was not ruled).
142+
# The queue enforces only the ruleset's required set, and `Dogfood Regression
143+
# Gate` is outside it since #22998 (check:required-contexts' tombstone); this
144+
# file does not decide whether it rejoins.
129145
#
130-
# Spelled as a DENY-list of the two per-PR events on purpose — the filter
146+
# Spelled as a DENY-list of the per-PR events on purpose — the filter
131147
# contract's half 1 below, applied to events: a trigger added to `on:` later
132148
# RUNS these jobs, and the failure direction of a wrong guess is a slower PR,
133149
# never a full battery that silently stopped running. `dogfood-gate` keeps
134-
# `if: always()` (check:shard-attestation pins it): with both legs skipped it
135-
# counts zero expected attestations and concludes success, exactly as on a
136-
# filter-skipped diff.
150+
# `if: always()` (check:shard-attestation pins it): with both legs skipped (a
151+
# pull request) it counts zero expected attestations and concludes success,
152+
# exactly as on a filter-skipped diff; on a queue build it counts the three
153+
# shards' credentials and `dogfood-verify`'s skip as zero expected.
137154

138155
jobs:
139156
filter:
@@ -158,8 +175,9 @@ jobs:
158175
# 'true'` did.
159176
docs: ${{ github.event_name != 'merge_group' && steps.changes.outputs.docs || 'true' }}
160177
# Read by `test-plan`, `test` (ORed with `crosspkg`) and `build-core`,
161-
# and on push / schedule by `dogfood`, `dogfood-verify` and
162-
# `temporal-conformance` — see the `core` filter below and each job's `if:`.
178+
# on merge_group / push / schedule by `dogfood`, and on push / schedule
179+
# by `dogfood-verify` and `temporal-conformance` — see the `core` filter
180+
# below and each job's `if:`.
163181
core: ${{ github.event_name != 'merge_group' && steps.changes.outputs.core || 'true' }}
164182
# Gates the `test` job ONLY, ORed with `core` — see the `crosspkg`
165183
# filter below and the `test` job's `if:` (#9829, #10015).
@@ -2390,9 +2408,11 @@ jobs:
23902408
# below — the shard count can change without touching it.
23912409
name: Dogfood Regression Gate (${{ matrix.shard }}/3)
23922410
needs: filter
2393-
# See THE FILTER CONTRACT on the filter job's outputs (#4928). Full battery
2394-
# only: see THE TWO SPEEDS above `jobs:` (#22998).
2395-
if: ${{ !cancelled() && needs.filter.outputs.core != 'false' && github.event_name != 'pull_request' && github.event_name != 'merge_group' }}
2411+
# See THE FILTER CONTRACT on the filter job's outputs (#4928). The queue
2412+
# build and the full battery, never a pull request: see THE TWO SPEEDS
2413+
# above `jobs:` (#22998; `merge_group` left this deny-list by #30461,
2414+
# ruling record 6663883327). `dogfood-verify` below keeps both events.
2415+
if: ${{ !cancelled() && needs.filter.outputs.core != 'false' && github.event_name != 'pull_request' }}
23962416
runs-on: ubuntu-latest
23972417
# Backstop only — the stall guard on the test step is the primary detector
23982418
# for a #4250-style hang (see Test Core). 30 min is ~6× a shard (~5 min;
@@ -2704,11 +2724,13 @@ jobs:
27042724
overwrite: true
27052725

27062726
dogfood-gate:
2707-
# Stable check name for a SHARDED job (#3622 follow-up). Since #22998 its
2708-
# legs run in the full battery only (THE TWO SPEEDS above `jobs:`) and the
2709-
# context leaves the required set; on a PR or queue build both legs skip
2710-
# and this gate counts zero expected attestations. The history below is
2711-
# why the name and the `if: always()` stay.
2727+
# Stable check name for a SHARDED job (#3622 follow-up). Since #22998 the
2728+
# context is outside the required set (THE TWO SPEEDS above `jobs:`). On a
2729+
# PR both legs skip and this gate counts zero expected attestations; on a
2730+
# queue build (since #30461) the three dogfood shards run and are counted,
2731+
# while `dogfood-verify` skips and is judged with zero expected; the full
2732+
# battery runs and counts both. The history below is why the name and the
2733+
# `if: always()` stay.
27122734
#
27132735
# Branch protection required the context "Dogfood Regression Gate". Once
27142736
# the job became a 2-way matrix its checks publish as "Dogfood Regression

‎.github/workflows/release.yml‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1496,8 +1496,9 @@ jobs:
14961496
# ══════════════════════════════════════════════════════════════════════════
14971497
full-battery:
14981498
name: Full battery green at the version commit
1499-
# A PR or queue build carries ci.yml's FAST SET since #22998; the dogfood
1500-
# and temporal legs run only on main's push, 4-hourly schedule and
1499+
# A PR or queue build carries ci.yml's FAST SET since #22998 (a queue
1500+
# build adds the three dogfood shards since #30461); `dogfood-verify` and
1501+
# the temporal leg run only on main's push, 4-hourly schedule and
15011502
# dispatch runs. So "tested before a release" is read HERE, as a gate: the
15021503
# newest completed `schedule` or `push` runs of `CI` and `Lint & Type
15031504
# Check` whose head is the version commit or a first-parent ancestor of it

‎.github/workflows/upgrade-harness-optin.yml‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@
88
# its data" — a check each of the line's PRs passes BEFORE it merges. The
99
# harness that answers R2 is a dogfood test,
1010
# `packages/qa/dogfood/test/upgrade-18x-harness.dogfood.test.ts`, and the
11-
# dogfood jobs in `ci.yml` run only on `main`'s full battery (THE TWO SPEEDS,
12-
# #22998): they skip `pull_request` and `merge_group`. So without this workflow
13-
# the line's PRs get their first R2 answer after the merge.
11+
# dogfood shards in `ci.yml` skip `pull_request` (THE TWO SPEEDS, #22998; they
12+
# run on `merge_group` since #30461). So without this workflow the line's PRs
13+
# get their first R2 answer in the merge queue, after the PR is enqueued.
1414
#
1515
# This workflow adds the missing TRIGGER for that ONE file, and nothing else.
1616
# The test, its fixture and its assertions stay where they are, and the full

0 commit comments

Comments
 (0)