Skip to content

Commit 8fd39d4

Browse files
committed
Merge remote-tracking branch 'origin/main' into claude/project-thread-0i0ziw
2 parents fc69ac6 + f708cf2 commit 8fd39d4

188 files changed

Lines changed: 7110 additions & 1759 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: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
---
2+
'@object-ui/i18n': patch
3+
---
4+
5+
feat(i18n): the flow designer and the rest of the Studio chrome move into the `studio` namespace
6+
7+
Clause-②: no
8+
Metadata-protocol: none
9+
10+
- **Sixth slice of the move ruled on objectstack#25922** (6431032267, Q2 = A; kept by 6431234244). Every row still left in `@object-ui/app-shell`'s private designer table, except `engine.translationPreview.*`, now lives in `@object-ui/i18n/namespaces/studio`, text unchanged: the flow designer (`engine.flowSim.*`, `engine.flowCanvas.*`, `engine.flowRuns.*` and the other `engine.flow*` families), the page-block canvas, the doc and book previews, `engine.studio.inspector.*` and the remaining Studio chrome (538 keys in `en`, 583 in `zh`). The `engine.flowNode.*` and `engine.enum.*` rows stay zh-only, as before: their English is the flow node descriptor or the enum value itself. Studio reads the same strings; a Chinese viewer sees these labels in English for the moment the `zh` catalogue takes to load, as with the earlier slices. No export changes.
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
---
2+
'@objectstack/spec': minor
3+
'@objectstack/runtime': minor
4+
'@objectstack/objectql': minor
5+
'@objectstack/cli': minor
6+
'@objectstack/lint': minor
7+
'@object-ui/i18n': patch
8+
'@object-ui/app-shell': patch
9+
---
10+
11+
feat(spec): a hook declares its refusal catalogue, and its body refuses by code, answered in the caller's language
12+
13+
Clause-②: yes (widening)
14+
Metadata-protocol: added
15+
16+
A hook can now declare `refusals`: one entry per SCREAMING_SNAKE code, each with the `message` the user reads and an optional 4xx `status` (default `400`). A sandboxed `body` then refuses by code only: it throws an error carrying a declared `code`, and the body runner answers with the entry. The message is translated through `hooks.<hook>.refusals.<CODE>.message` in the caller's locale, the code reaches the caller's error envelope as `declaredCode` (beside the status's registered `code`), and the status is the entry's. The body's own error text and `status` are not what the caller reads.
17+
18+
- **Spec.** `HookSchema.refusals` (`HookRefusalSchema`: `message`, `status?`). A new `hooks` group on the translation bundle, `hooks.<hook>.refusals.<CODE>.message`, and `hookRefusalMessageKey`. A message is plain text: a `{{ }}` hole is refused at save, so a later parameter slot stays additive. `HookContext.session.locale` declares the caller's locale.
19+
- **Undeclared codes are refused loudly.** A code the body throws literally (`{ code: 'X' }`, or `e.code = 'X'; throw e`) that the catalogue does not declare fails the save at `body.source`. Any other undeclared code, and a throw with no code, is a `500` fault at run time that names the hook and the code. A catalogue on a hook with no sandboxed `js` body (an in-process `handler`, an expression body) is refused at save, since nothing would answer it. All of this applies only to a hook that declares `refusals`; a hook without one is answered exactly as before.
20+
- **Runtime.** The body runner resolves the throw against the catalogue and picks the translation through the `i18n` service, negotiated against the locales it holds. An error a `ctx.api` call raised that the body lets leave unchanged, a crash, and the sandbox's own faults pass through untouched. ObjectQL copies the request's locale onto the hook session.
21+
- **CLI.** `os i18n extract` scaffolds one key per declared entry and `os i18n check` counts them, in a new `hook` coverage bucket.
22+
- **Lint.** A translation keyed to a hook the stack does not define, or to a code its `refusals` does not declare, is reported as `translation-target-unknown`.
23+
- **Console.** The translation preview shows a `hooks` category (en/zh labels), and the translation loader lists `hooks` among the spec groups.
24+
25+
Nothing to do for existing hooks. To use it, declare `refusals` on a sandboxed hook and throw its codes.
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
---
2+
'@object-ui/app-shell': patch
3+
'@object-ui/console': patch
4+
'@object-ui/i18n': patch
5+
---
6+
7+
fix(app-shell): the field-permission editor shows and writes exactly what the runtime enforces for a field with no explicit rule
8+
9+
Clause-②: no
10+
Metadata-protocol: none
11+
12+
- **A field row without a rule.** In the permission-set editor (Studio Access, and the permission sets under Setup), a field the set declares no field rule for is not constrained by the set: the object grant decides. Its row now shows the object row's own Read and Edit, in grey with a dashed edge, and says it follows the object. A field rule shows its own Read and Edit in the usual colour. A rule that grants Read or Edit the object row does not is ringed in amber, and its tooltip says that a holder of this set alone cannot use it.
13+
- **Every control writes only what changes on screen.** A click on a row without a rule changes only the box it lands on. The field shortcuts move each visible row to their target and leave a row that already shows it as it is: **Read-only all** (renamed from **Read all**, since it switches Edit off), **Write all**, and **Clear**, which returns the visible rows to the object grant. Adding a row condition (**When (edit)**) to a row without a rule changes no box.
14+
- **The permission-set preview** (also shown for each permission set under a position) lists every field rule the set declares, tagged hidden, read-only or editable.
15+
- **`@object-ui/i18n`:** in the `studio` namespace, `perm.field.bulk.readable` reads "Read-only all" (「全部只读」), four new keys explain the grey and amber boxes (`perm.field.inherited.legend`, `perm.field.inherited.tip`, `perm.field.capped.read` and `perm.field.capped.edit`), and `engine.permissionPreview.fls.readOnly` tags a read-only rule in the preview.
16+
17+
What to do: open the field rows of the permission sets you maintain and check them against what you intend. Coloured boxes are rules the set declares; grey boxes follow the object. **Clear** on a row returns it to the object grant.
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
---
2+
'@objectstack/plugin-auth': patch
3+
---
4+
5+
fix(plugin-auth): a session stores its client address in RFC 5952 text, and is born with `last_activity_at`
6+
7+
Clause-②: yes (widening)
8+
Metadata-protocol: none
9+
10+
Setup › Sessions printed a localhost session's IP as `0000:0000:0000:0000:0000:0000:0000:0000` and left Last Activity empty until the session's first request. Both are fixed in the data a sign-in stores; the list still prints the columns as plain text.
11+
12+
- **The address is the client's, in RFC 5952 text.** `sys_session.ip_address` is now the address the server established for the request (the client-address trust rule: forwarded headers only under `server.trustProxy`, the connection's peer otherwise), with IPv6 compressed — `::1`, `2001:db8::1` — and IPv4 unchanged. better-auth's own reader used to fill the column with its rate-limit bucket instead: every IPv6 address expanded to eight groups and masked to its /64 network, so `::1` read as `0000:…:0000` and `2001:db8::1` as `2001:0db8:0000:0000:0000:0000:0000:0000`. An IPv4-mapped address is stored as the IPv4 address it maps (`::ffff:203.0.113.7` → `203.0.113.7`), so an IPv4 client reads the same whether a dual-stack or an IPv4 listener accepted it; `last_login_ip` and the refused-sign-in audit rows, which carried the mapped text, now carry that one too. A session minted with no established address (a programmatic `auth.api.*` call) keeps better-auth's value, in the same text. An unparseable value is stored as it came.
13+
- **One text on every row of a sign-in.** The `login_failed` / `account_locked` audit rows and `sys_user.last_login_ip` carry the same text as the session row and its `login` row. `signInClientFingerprint(ctx, establishedAddress?)` takes the established address as an optional second argument.
14+
- **`last_activity_at` is written at creation**, equal to `created_at`. The activity heartbeat is unchanged; its first write now comes on the first request more than a minute after sign-in rather than on the very first request.
15+
- **Unchanged:** better-auth's per-address rate limiter still keys on its own bucket, the ADR-0069 D5 allow-list still judges the trust rule's answer as it came, and existing session rows are not rewritten.
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
---
2+
'@objectstack/metadata-protocol': patch
3+
'@objectstack/runtime': patch
4+
'@object-ui/app-shell': patch
5+
---
6+
7+
**BREAKING** fix(metadata)!: a save or a draft publish naming a package that is not installed is refused, for every metadata type; Studio shows "package not found"
8+
9+
Clause-②: yes (narrowing)
10+
Metadata-protocol: none
11+
12+
<!-- adr-0087: not-required (no-migration-prescription) What is refused is a REQUEST, never a stored shape: a `/meta` save naming a package id no installed package has, and `publish-drafts` on such an id. No metadata document changes meaning or validity, no key or value is retired, and rows already stored under such an id still read, list and discard exactly as before, so there is nothing for `objectstack migrate meta` to rewrite. The remedy is a different request (name an installed package, create one first, or name none), not a conversion between two metadata versions. The other categories are closed on facts: all three packages publish (not unpublished); no ADR-0087 id is named or touched (not registered or already-registered); no spec or Zod face moves (not type-surface-only, not runtime-interface-only); and no export is retired (not runtime-export-retired). -->
13+
14+
`PUT /api/v1/meta/:type/:name?package=ID` used to store the item bound to `ID` even when no installed package has that id, as a draft (`?mode=draft`) or live. With no manifest there was no namespace, so the object prefix rule was skipped too, and `POST /api/v1/packages/ID/publish-drafts` then put those drafts live, owned by a package that cannot be uninstalled or exported. Only `flow` refused it.
15+
16+
- **`@objectstack/metadata-protocol`**: the flow-only named-base rule is now one helper that `saveMetaItem` asks for every type, draft and active, on authoring saves. A save naming an id the package registry does not hold is refused `422 WRITABLE_PACKAGE_REQUIRED`, with the id as `packageId`, and nothing is written. The flow refusal is unchanged, byte for byte, and other types get the same sentence with their type name. Saves naming no package, the `sys_metadata` sentinel or an installed package are unchanged, and so are the two server-side rewrites of stored rows (`migrate-stored` and package duplication).
17+
- **`@objectstack/runtime`**: `POST /packages/:id/publish-drafts` on an id the registry does not hold answers `404 RESOURCE_NOT_FOUND` (the answer `GET /packages/:id` gives) before anything is published, even when drafts are stored under that id. Those drafts are not migrated or deleted. `POST /packages/:id/discard-drafts` still removes them.
18+
- **`@object-ui/app-shell`**: `/studio/ID/…` for an id the package list does not hold shows "package not found", with a way back to the Studio landing, instead of a builder whose saves the server refuses. A pillar segment Studio does not have shows a not-found state instead of the Interfaces pillar. Kernel packages the switcher hides are still opened. If the package list fails to load, the builder shows as before.
19+
20+
**What to do.** Save into a package that is installed: create it first (`POST /api/v1/packages`, or **New package** in Studio), or save without `?package=`. To clean up drafts saved under a mistyped id before this change, call `POST /api/v1/packages/ID/discard-drafts`.

‎.github/workflows/ci.yml‎

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -913,16 +913,29 @@ jobs:
913913
# hash in one namespace (turbo.json `globalEnv`).
914914
#
915915
# ⛔ EXCEPT a shard carrying a package in DTS_READERS, whose tests compile
916-
# against BUILT declarations and go red without them (TS7016). Measured
917-
# on 830a63a, the whole Test Core list under `OS_SKIP_DTS=1`: red only in
916+
# against BUILT declarations and go red without them (TS7016, or ENOENT
917+
# on a `dist/*.d.ts`). Measured on 830a63a, the whole Test Core list under
918+
# `OS_SKIP_DTS=1`, re-measured for the CLI on 43731031 (#25870): red only in
918919
# @objectstack/cli published-subpath-console/-hook-body.pin (its
919920
# own dist/*.d.ts), scaffold-emission-typechecks,
920-
# scaffold-test-script-passes (spec's .d.ts)
921+
# scaffold-test-script-passes,
922+
# create-in-repo-scaffold-typechecks (spec's .d.ts)
921923
# @objectstack/metadata typescript-serializer-annotation (spec's .d.ts)
922-
# A new test that reads a built `.d.ts` reds the same loud way on a
923-
# skipping shard: add its package here. Every other package's counts
924-
# matched the DTS-on run but one: spec's root-entry-type-nameability
925-
# pin skips (by its own design) and runs in lint.yml's built-dist step.
924+
# Every other package's counts matched the DTS-on run but one: spec's
925+
# root-entry-type-nameability pin skips (by its own design) and runs in
926+
# lint.yml's built-dist step.
927+
#
928+
# ⛔ A SLICED reader keeps declarations on ONE slice, DTS_READER_SLICE
929+
# (#25870). The CLI's vitest config pins its reader files into slice 1/n
930+
# (DECLARATION_READER_FILES, packages/cli/vitest.config.ts), so its other
931+
# slices read no `.d.ts` and skip them like any other shard; the
932+
# partitioner charges slice 1/n the closure and puts the whole readers
933+
# here on its shard (DECLARATION_READER_SLICES / DECLARATION_READER_PACKAGES,
934+
# scripts/partition-test-shards.mjs, whose self-test reads this step back).
935+
# A whole reader, or the CLI unsliced, keeps them as before. A new test
936+
# that reads a built `.d.ts` reds the same loud way on a skipping shard:
937+
# add its package here, or, for the CLI, its file to
938+
# DECLARATION_READER_FILES.
926939
#
927940
# Local cold closure, 65 packages, 4 cores, `--force`: 5m59s with
928941
# declarations, 1m24s without. The cost is replay: a skipping shard
@@ -931,10 +944,14 @@ jobs:
931944
- name: Choose this shard's declaration build
932945
run: |
933946
DTS_READERS='@objectstack/cli @objectstack/metadata'
947+
DTS_READER_SLICE='1'
934948
KEEP=""
935-
while read -r PKG _; do
949+
while read -r PKG SLICE; do
936950
for R in $DTS_READERS; do
937-
[ "$PKG" = "$R" ] && KEEP="$KEEP $PKG"
951+
[ "$PKG" = "$R" ] || continue
952+
case "$SLICE" in
953+
''|"$DTS_READER_SLICE"/*) KEEP="$KEEP $PKG${SLICE:+ $SLICE}" ;;
954+
esac
938955
done
939956
done < "$RUNNER_TEMP/shard-packages.txt"
940957
if [ -n "$KEEP" ]; then

‎AGENTS.md‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ Primary AI instruction file for this repo — and the human contributors' source
44
Read natively by Claude Code, GitHub Copilot (coding agent + CLI), and other agents — no
55
separate `.github/copilot-instructions.md` mirror needed. When any other instruction file
66
in this repo (including `.claude/skills/**`) conflicts with this one, **AGENTS.md wins**.
7-
`docs/NORTH-STAR.md` (先做什么: goals, priority) outranks it; `content/docs/concepts/north-star.mdx` = architecture.
7+
`docs/NORTH-STAR.md`, the North Star, outranks it; `content/docs/concepts/architecture-overview.mdx` = architecture.
88

99
> **v5.0 breaking rename: `project` → `environment`** everywhere (CLI `-e`, `/api/v1/environments/:id`, header `X-Environment-Id`, `OS_ENVIRONMENT_ID`, DB column `environment_id` [control-plane tables; on the metadata tables since deprecated in favour of `organization_id`, ADR-0006 v4]). No aliases. See ADR-0006. "Project" now only means the npm/monorepo sense.
1010
@@ -194,8 +194,8 @@ localStorage / auth gotchas.
194194
7. **One Zod source per metadata type.** Each type (`view`, `flow`, `agent`, …) has exactly one schema in
195195
`packages/spec/src/{domain}/`. Environment-overlay opt-in lives only in `allowEnvironmentOverlay` on
196196
`DEFAULT_METADATA_TYPE_REGISTRY` — no parallel whitelists. See ADR-0005, amended by ADR-0131 C5.
197-
8. **North Star alignment.** Read `content/docs/concepts/north-star.mdx` before structural changes. If a change doesn't
198-
advance §7 Built, shrink Drift, or unlock Missing — it probably shouldn't ship.
197+
8. **North Star alignment.** Read `docs/NORTH-STAR.md` before structural changes. If a change doesn't advance a row of
198+
its 路上的功能点 ladder, graded by its 优先级 — it probably shouldn't ship.
199199
9. **`OS_` env-var prefix + structure.** All ObjectStack-owned env vars MUST start with `OS_`, then follow
200200
**`OS_{DOMAIN}_{FEATURE}[_QUALIFIER]`** where `DOMAIN` is the subsystem (`AUTH`, `SEARCH`, `CORS`, `CLOUD`,
201201
`DATABASE`, `CLUSTER`, `MCP`, `SSO`, …) so related vars group together (cf. `OS_AUTH_*`, `OS_CORS_*`). Pick the

‎CONTRIBUTING.md‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,8 @@ Other examples: `pnpm dev:crm`, `pnpm dev:todo`. Docs site: `pnpm docs:dev`.
112112
1. **Start from an issue.** Pick one from the
113113
[issue tracker](https://github.com/objectstack-ai/objectstack/issues), or open one
114114
that describes the bug or the proposal before you write code.
115-
[docs/NORTH-STAR.md](./docs/NORTH-STAR.md) shows where the project is going.
115+
[docs/NORTH-STAR.md](./docs/NORTH-STAR.md), the project's one North Star, shows where the project is going
116+
and what comes first; an English summary closes it.
116117
2. **Branch off an up-to-date `main`, one branch and one pull request per issue**,
117118
with the issue number in the branch name:
118119

‎README.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -292,7 +292,7 @@ Every package in `packages/`, grouped by layer, plus the example apps, is in
292292
- ⭐ **Star this repo** if ObjectStack is useful — it helps others find it.
293293
- 🐛 Questions, bugs, or feature requests → [open an issue](https://github.com/objectstack-ai/objectstack/issues).
294294
- 💬 Ideas and open-ended questions → [GitHub Discussions](https://github.com/objectstack-ai/objectstack/discussions).
295-
- 🤝 Want to contribute? Start with [CONTRIBUTING.md](./CONTRIBUTING.md); [North Star](https://objectstack.ai/docs/concepts/north-star) says where the project is headed.
295+
- 🤝 Want to contribute? Start with [CONTRIBUTING.md](./CONTRIBUTING.md); [North Star](./docs/NORTH-STAR.md) says where the project is headed.
296296
- 📖 Full documentation at [objectstack.ai/docs](https://objectstack.ai/docs); upgrading between majors is covered in the [upgrade guide](https://objectstack.ai/docs/upgrading).
297297
- ☁️ Want it hosted, with Build & Ask AI built in? [ObjectOS](https://www.objectos.ai) Cloud runs these definitions for you — [objectstack-ai/objectos](https://github.com/objectstack-ai/objectos) is its public home.
298298

‎ROADMAP.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# ObjectStack Roadmap
22

33
> **Last updated:** 2026-06-06
4-
> **Source of truth:** [content/docs/concepts/north-star.mdx](content/docs/concepts/north-star.mdx) for product direction, ADRs for architectural decisions, and code for shipped behavior.
4+
> **Source of truth:** [docs/NORTH-STAR.md](docs/NORTH-STAR.md), the one North Star, for product direction and priority, ADRs for architectural decisions, and code for shipped behavior.
55
66
This roadmap tracks the current framework repository. Historical Cloud/ObjectOS
77
milestone plans that referred to `apps/cloud`, `apps/objectos`,

0 commit comments

Comments
 (0)