From ea7bf28f115649ebb9448850fe3d4c96d7f7bdc7 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 7 Oct 2026 03:06:48 +0000 Subject: [PATCH 1/6] =?UTF-8?q?content(blog):=20executable=20ontology=20vs?= =?UTF-8?q?=20knowledge=20ontology=20=E2=80=94=20post=202=20of=20the=20pos?= =?UTF-8?q?itioning=20wave=20(slot=202026-10-13)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit New post content/blog/executable-ontology-vs-knowledge-ontology/ in en and zh-Hans (zh-Hant generated by gen-zh-hant), date 2026-10-13, no updated. It argues the second negation of the pillar post in full: a knowledge ontology is built to be reasoned over, an executable business ontology to be validated and run, and the README's "no inheritance, no axioms, no reasoner" is what makes a definition checkable and a diff reviewable. The proof artifact is the ADR-0049 retirement of the object `abstract` key, measured on the published @objectstack/spec 15.1.1, 16.0.0 and 17.7.0: parsed and kept, then refused by create() while parse() still stripped, then refused on both paths with the prescription "Remove the key." Six SVGs: cover-en/cover, two-pipelines(-en), abstract-before-after(-en); all pass svg-text-fit, text fills chosen for >= 4.5:1 on their fills. Also in this PR: enterprise-ontology-race-open-vs-closed, every locale — the title, description and TL;DR no longer name the contested layer "the business semantic layer"; they name the business ontology (the README's term) and say which of the five entrants shipped an ontology by name and which a semantic layer; the race-section line and the zh closing line match. updated set to 2026-10-07T10:00:00+08:00; date and slug unchanged. Co-Authored-By: Claude Claude-Session: https://claude.ai/code/session_01FeA1nwBz1ohH65dvffUGKr --- .../index.de.mdx | 10 +- .../index.es.mdx | 10 +- .../index.fr.mdx | 10 +- .../index.ja.mdx | 10 +- .../index.ko.mdx | 10 +- .../index.mdx | 10 +- .../index.zh-Hans.mdx | 12 +- .../index.zh-Hant.mdx | 12 +- .../abstract-before-after-en.svg | 114 ++++++++++ .../abstract-before-after.svg | 113 ++++++++++ .../cover-en.svg | 70 ++++++ .../cover.svg | 70 ++++++ .../index.mdx | 196 +++++++++++++++++ .../index.zh-Hans.mdx | 200 +++++++++++++++++ .../index.zh-Hant.mdx | 201 ++++++++++++++++++ .../two-pipelines-en.svg | 106 +++++++++ .../two-pipelines.svg | 106 +++++++++ 17 files changed, 1218 insertions(+), 42 deletions(-) create mode 100644 content/blog/executable-ontology-vs-knowledge-ontology/abstract-before-after-en.svg create mode 100644 content/blog/executable-ontology-vs-knowledge-ontology/abstract-before-after.svg create mode 100644 content/blog/executable-ontology-vs-knowledge-ontology/cover-en.svg create mode 100644 content/blog/executable-ontology-vs-knowledge-ontology/cover.svg create mode 100644 content/blog/executable-ontology-vs-knowledge-ontology/index.mdx create mode 100644 content/blog/executable-ontology-vs-knowledge-ontology/index.zh-Hans.mdx create mode 100644 content/blog/executable-ontology-vs-knowledge-ontology/index.zh-Hant.mdx create mode 100644 content/blog/executable-ontology-vs-knowledge-ontology/two-pipelines-en.svg create mode 100644 content/blog/executable-ontology-vs-knowledge-ontology/two-pipelines.svg diff --git a/content/blog/enterprise-ontology-race-open-vs-closed/index.de.mdx b/content/blog/enterprise-ontology-race-open-vs-closed/index.de.mdx index 29d51ad..d07dc1d 100644 --- a/content/blog/enterprise-ontology-race-open-vs-closed/index.de.mdx +++ b/content/blog/enterprise-ontology-race-open-vs-closed/index.de.mdx @@ -1,9 +1,9 @@ --- -title: "Offene Unternehmens-Ontologie: Wem sollte die semantische Geschäftsschicht gehören?" -description: "Zwischen Nov. 2025 und Aug. 2026 haben fünf Plattformen eine semantische Geschäftsschicht ausgeliefert und meist einen MCP-Lesepfad geöffnet — die Definition blieb drinnen. Protokoll offen, Definition geschlossen." +title: "Offene Unternehmens-Ontologie: Wem sollte die Geschäftsontologie gehören?" +description: "Nov. 2025 bis Aug. 2026: Fünf Plattformen haben eine Geschäftsdefinitionsschicht ausgeliefert — zwei als Ontologie, drei als semantische Schicht — und meist einen MCP-Lesepfad geöffnet. Protokoll offen, Definition geschlossen." author: ObjectStack Team date: 2026-06-16T17:00:00+08:00 -updated: 2026-10-06T10:00:00+08:00 +updated: 2026-10-07T10:00:00+08:00 status: published topic: governance audience: business @@ -21,7 +21,7 @@ tags: - Looker --- -**Kurz gesagt:** Als dieser Beitrag im Juni 2026 erschien, fragte er, wem die semantische Geschäftsschicht gehören wird. Das Rennen ist inzwischen gelaufen: **Fünf Plattformen haben in neun Monaten eine ausgeliefert.** Und sie taten etwas, das niemand vorhergesagt hatte — sie öffneten den *Zugangspfad* zu ihrer Ontologie über MCP und behielten die *Definition* im Inneren. Nennen wir es **Protokoll offen, Definition geschlossen**. Ein Agent kann heute fünf Ontologien lesen, die er nicht mitnehmen kann. Das macht die Eigentumsfrage schärfer, nicht schwächer — und die Antwort bleibt: Die Definitionsschicht, auf die Ihre Anwendungen, Agents, Prüfer und Anbieter gemeinsam angewiesen sind, sollte eine neutrale Schicht in Ihrem eigenen Repository sein. +**Kurz gesagt:** Als dieser Beitrag im Juni 2026 erschien, fragte er, wem die Geschäftsontologie gehören wird — die gesteuerte Schicht, die das Geschäft definiert. Das Rennen ist inzwischen gelaufen: **Fünf Plattformen haben in neun Monaten eine ausgeliefert**, zwei unter dem Namen Ontologie, drei als semantische Schicht. Und sie taten etwas, das niemand vorhergesagt hatte — sie öffneten den *Zugangspfad* zu ihrer Ontologie über MCP und behielten die *Definition* im Inneren. Nennen wir es **Protokoll offen, Definition geschlossen**. Ein Agent kann heute fünf Ontologien lesen, die er nicht mitnehmen kann. Das macht die Eigentumsfrage schärfer, nicht schwächer — und die Antwort bleibt: Die Definitionsschicht, auf die Ihre Anwendungen, Agents, Prüfer und Anbieter gemeinsam angewiesen sind, sollte eine neutrale Schicht in Ihrem eigenen Repository sein. Beginnen wir damit, wie ein Unternehmen genau daran einen Kunden verlor. Die Details sind anonymisiert, aber jeden Schritt haben Sie vermutlich schon gesehen. @@ -45,7 +45,7 @@ Das war nicht bloß ein Fehler von Yuanfeng. Es ist das vorhersehbare Ergebnis d ## Das Rennen, das dieser Beitrag benannte, ist gelaufen -Als dieser Text zuerst erschien, waren die Teilnehmer noch eine Prognose. Inzwischen sind sie Aktenlage. Zwischen November 2025 und August 2026 haben fünf Plattformen eine semantische Geschäftsschicht ausgeliefert: +Als dieser Text zuerst erschien, waren die Teilnehmer noch eine Prognose. Inzwischen sind sie Aktenlage. Zwischen November 2025 und August 2026 haben fünf Plattformen eine gesteuerte Schicht der Geschäftsdefinition ausgeliefert — zwei davon unter dem Namen Ontologie, drei als semantische Schicht: | Plattform | Was ausgeliefert wurde | Wann | | --- | --- | --- | diff --git a/content/blog/enterprise-ontology-race-open-vs-closed/index.es.mdx b/content/blog/enterprise-ontology-race-open-vs-closed/index.es.mdx index f80fc63..2b85d80 100644 --- a/content/blog/enterprise-ontology-race-open-vs-closed/index.es.mdx +++ b/content/blog/enterprise-ontology-race-open-vs-closed/index.es.mdx @@ -1,9 +1,9 @@ --- -title: "Ontología empresarial abierta: quién debería poseer la capa semántica del negocio" -description: "Entre nov. 2025 y ago. 2026 cinco plataformas lanzaron una capa semántica de negocio y la mayoría abrió una vía de lectura por MCP, dejando dentro la definición. Protocolo abierto, definición cerrada." +title: "Ontología empresarial abierta: quién debería poseer la ontología del negocio" +description: "Entre nov. 2025 y ago. 2026 cinco plataformas lanzaron una capa de definición del negocio — dos como ontología, tres como capa semántica — y la mayoría abrió una vía de lectura por MCP. Protocolo abierto, definición cerrada." author: ObjectStack Team date: 2026-06-16T17:00:00+08:00 -updated: 2026-10-06T10:00:00+08:00 +updated: 2026-10-07T10:00:00+08:00 status: published topic: governance audience: business @@ -21,7 +21,7 @@ tags: - Looker --- -**En corto:** cuando este artículo se publicó en junio de 2026 preguntaba quién sería dueño de la capa semántica del negocio. La carrera ya se corrió: **cinco plataformas lanzaron una en nueve meses.** Y además hicieron algo que nadie predijo: abrieron la *vía de acceso* a su ontología mediante MCP y se quedaron dentro con la *definición*. Llamémoslo **protocolo abierto, definición cerrada**. Hoy un agent puede leer cinco ontologías que no puede llevarse. Eso vuelve la pregunta de propiedad más afilada, no más débil, y la respuesta no cambia: la capa de definición de la que dependen a la vez tus aplicaciones, tus agents, tus auditores y tus proveedores debería ser una capa neutral que guardes en tu propio repositorio. +**En corto:** cuando este artículo se publicó en junio de 2026 preguntaba quién sería dueño de la ontología del negocio — la capa gobernada que define el negocio. La carrera ya se corrió: **cinco plataformas lanzaron una en nueve meses**, dos con el nombre de ontología y tres como capa semántica. Y además hicieron algo que nadie predijo: abrieron la *vía de acceso* a su ontología mediante MCP y se quedaron dentro con la *definición*. Llamémoslo **protocolo abierto, definición cerrada**. Hoy un agent puede leer cinco ontologías que no puede llevarse. Eso vuelve la pregunta de propiedad más afilada, no más débil, y la respuesta no cambia: la capa de definición de la que dependen a la vez tus aplicaciones, tus agents, tus auditores y tus proveedores debería ser una capa neutral que guardes en tu propio repositorio. Empecemos por cómo una empresa perdió un cliente exactamente por esto. Los detalles están anonimizados, pero probablemente hayas visto cada paso. @@ -45,7 +45,7 @@ Esto no fue simplemente un error de Yuanfeng. Es el resultado previsible de entr ## La carrera que este artículo nombró ya se corrió -Cuando este texto se publicó por primera vez, los participantes eran todavía un pronóstico. Ahora son un hecho registrado. Entre noviembre de 2025 y agosto de 2026, cinco plataformas lanzaron una capa semántica de negocio: +Cuando este texto se publicó por primera vez, los participantes eran todavía un pronóstico. Ahora son un hecho registrado. Entre noviembre de 2025 y agosto de 2026, cinco plataformas lanzaron una capa gobernada de definición del negocio — dos de ellas con el nombre de ontología, tres como capa semántica: | Plataforma | Qué lanzó | Cuándo | | --- | --- | --- | diff --git a/content/blog/enterprise-ontology-race-open-vs-closed/index.fr.mdx b/content/blog/enterprise-ontology-race-open-vs-closed/index.fr.mdx index 4d1e3ed..87e2c32 100644 --- a/content/blog/enterprise-ontology-race-open-vs-closed/index.fr.mdx +++ b/content/blog/enterprise-ontology-race-open-vs-closed/index.fr.mdx @@ -1,9 +1,9 @@ --- -title: "Ontologie d'entreprise ouverte : qui doit posséder la couche sémantique du métier ?" -description: "Entre nov. 2025 et août 2026, cinq plateformes ont livré une couche sémantique métier et la plupart ont ouvert une voie de lecture MCP, en gardant la définition à l'intérieur. Protocole ouvert, définition fermée." +title: "Ontologie d'entreprise ouverte : qui doit posséder l'ontologie métier ?" +description: "Nov. 2025 – août 2026 : cinq plateformes ont livré une couche de définition du métier — deux comme ontologie, trois comme couche sémantique — et la plupart ont ouvert une voie de lecture MCP. Protocole ouvert, définition fermée." author: ObjectStack Team date: 2026-06-16T17:00:00+08:00 -updated: 2026-10-06T10:00:00+08:00 +updated: 2026-10-07T10:00:00+08:00 status: published topic: governance audience: business @@ -21,7 +21,7 @@ tags: - Looker --- -**En bref :** quand cet article a paru en juin 2026, il demandait qui posséderait la couche sémantique du métier. La course a depuis été courue : **cinq plateformes en ont livré une en neuf mois.** Et elles ont fait quelque chose que personne n'avait prévu : elles ont ouvert la *voie d'accès* à leur ontologie via MCP tout en gardant la *définition* à l'intérieur. Appelons cela **protocole ouvert, définition fermée**. Un agent peut désormais lire cinq ontologies qu'il ne peut pas emporter. Cela rend la question de la propriété plus aiguë, pas plus faible, et la réponse ne change pas : la couche de définition dont dépendent à la fois vos applications, vos agents, vos auditeurs et vos fournisseurs devrait être une couche neutre que vous détenez dans votre propre dépôt. +**En bref :** quand cet article a paru en juin 2026, il demandait qui posséderait l'ontologie métier — la couche gouvernée qui définit le métier. La course a depuis été courue : **cinq plateformes en ont livré une en neuf mois**, deux sous le nom d'ontologie et trois comme couche sémantique. Et elles ont fait quelque chose que personne n'avait prévu : elles ont ouvert la *voie d'accès* à leur ontologie via MCP tout en gardant la *définition* à l'intérieur. Appelons cela **protocole ouvert, définition fermée**. Un agent peut désormais lire cinq ontologies qu'il ne peut pas emporter. Cela rend la question de la propriété plus aiguë, pas plus faible, et la réponse ne change pas : la couche de définition dont dépendent à la fois vos applications, vos agents, vos auditeurs et vos fournisseurs devrait être une couche neutre que vous détenez dans votre propre dépôt. Commençons par la façon dont une entreprise a perdu un client exactement là-dessus. Les détails sont anonymisés, mais vous avez probablement déjà vu chaque étape. @@ -45,7 +45,7 @@ Ce n'était pas simplement une erreur de Yuanfeng. C'est le résultat prévisibl ## La course que cet article nommait a maintenant été courue -Quand ce texte a paru pour la première fois, les concurrents étaient encore une prévision. Ils sont désormais un fait établi. Entre novembre 2025 et août 2026, cinq plateformes ont livré une couche sémantique métier : +Quand ce texte a paru pour la première fois, les concurrents étaient encore une prévision. Ils sont désormais un fait établi. Entre novembre 2025 et août 2026, cinq plateformes ont livré une couche gouvernée de définition du métier — deux d'entre elles sous le nom d'ontologie, trois comme couche sémantique : | Plateforme | Ce qui a été livré | Quand | | --- | --- | --- | diff --git a/content/blog/enterprise-ontology-race-open-vs-closed/index.ja.mdx b/content/blog/enterprise-ontology-race-open-vs-closed/index.ja.mdx index 8f0dd67..dfa8344 100644 --- a/content/blog/enterprise-ontology-race-open-vs-closed/index.ja.mdx +++ b/content/blog/enterprise-ontology-race-open-vs-closed/index.ja.mdx @@ -1,9 +1,9 @@ --- -title: "オープンな企業オントロジー:業務セマンティックレイヤーは誰が所有すべきか" -description: "2025年11月から2026年8月にかけて5つのプラットフォームが業務セマンティックレイヤーを投入し、その多くが MCP の読み取り経路を開放した。だが定義そのものは内部に残る。プロトコルは開放、定義は封鎖——所有権の問いはより鋭くなった。" +title: "オープンな企業オントロジー:業務オントロジーは誰が所有すべきか" +description: "2025年11月から2026年8月にかけて5つのプラットフォームが業務定義層を投入し——2社はオントロジー、3社はセマンティックレイヤーとして——その多くが MCP の読み取り経路を開放した。だが定義そのものは内部に残る。プロトコルは開放、定義は封鎖——所有権の問いはより鋭くなった。" author: ObjectStack Team date: 2026-06-16T17:00:00+08:00 -updated: 2026-10-06T10:00:00+08:00 +updated: 2026-10-07T10:00:00+08:00 status: published topic: governance audience: business @@ -21,7 +21,7 @@ tags: - Looker --- -**結論から**:この記事が 2026 年 6 月に公開されたとき、問いは「業務セマンティックレイヤーを誰が所有するのか」だった。そのレースはもう走り終えた。**9 か月で 5 つのプラットフォームがセマンティックレイヤーを投入した。** そして誰も予想しなかったことが起きた——各社はオントロジーへの**アクセス経路**を MCP で開放し、**定義そのもの**は内部に留めたのだ。これを**プロトコルは開放、定義は封鎖**と呼ぼう。agent はいま、持ち出せない 5 つのオントロジーを読める。これは所有権の問いを弱めるのではなく、鋭くする。そして答えは変わらない。アプリ、agent、監査、そして複数ベンダーのツールが揃って依存する定義層は、自社リポジトリで保持する中立な層であるべきだ。 +**結論から**:この記事が 2026 年 6 月に公開されたとき、問いは「業務オントロジー——業務を定義する、統制された層——を誰が所有するのか」だった。そのレースはもう走り終えた。**9 か月で 5 つのプラットフォームがそれを投入した**——2 社はオントロジーの名で、3 社はセマンティックレイヤーの名で。そして誰も予想しなかったことが起きた——各社はオントロジーへの**アクセス経路**を MCP で開放し、**定義そのもの**は内部に留めたのだ。これを**プロトコルは開放、定義は封鎖**と呼ぼう。agent はいま、持ち出せない 5 つのオントロジーを読める。これは所有権の問いを弱めるのではなく、鋭くする。そして答えは変わらない。アプリ、agent、監査、そして複数ベンダーのツールが揃って依存する定義層は、自社リポジトリで保持する中立な層であるべきだ。 まず、ある会社がまさにこの問題で顧客を失った話から始めよう。詳細は匿名化しているが、どのステップも見覚えがあるはずだ。 @@ -45,7 +45,7 @@ tags: ## この記事が名指ししたレースは、もう走り終えた -この記事が最初に公開された時点では、参加者はまだ予測だった。いまやそれは記録である。2025 年 11 月から 2026 年 8 月にかけて、5 つのプラットフォームが業務セマンティックレイヤーを投入した。 +この記事が最初に公開された時点では、参加者はまだ予測だった。いまやそれは記録である。2025 年 11 月から 2026 年 8 月にかけて、5 つのプラットフォームが統制された業務定義層を投入した——2 社はオントロジーの名で、3 社はセマンティックレイヤーの名で。 | プラットフォーム | 何を投入したか | 時期 | | --- | --- | --- | diff --git a/content/blog/enterprise-ontology-race-open-vs-closed/index.ko.mdx b/content/blog/enterprise-ontology-race-open-vs-closed/index.ko.mdx index 3cc2a50..955e1fa 100644 --- a/content/blog/enterprise-ontology-race-open-vs-closed/index.ko.mdx +++ b/content/blog/enterprise-ontology-race-open-vs-closed/index.ko.mdx @@ -1,9 +1,9 @@ --- -title: "열린 기업 온톨로지: 비즈니스 의미 계층은 누가 소유해야 하는가" -description: "2025년 11월부터 2026년 8월까지 다섯 플랫폼이 비즈니스 의미 계층을 출시했고, 대부분 MCP 읽기 경로를 열었다. 그러나 정의 자체는 안에 남는다. 프로토콜은 열리고 정의는 닫혔다 — 소유권 질문은 더 날카로워졌다." +title: "열린 기업 온톨로지: 비즈니스 온톨로지는 누가 소유해야 하는가" +description: "2025년 11월부터 2026년 8월까지 다섯 플랫폼이 비즈니스 정의 계층을 출시했고 — 둘은 온톨로지로, 셋은 의미 계층으로 — 대부분 MCP 읽기 경로를 열었다. 그러나 정의 자체는 안에 남는다. 프로토콜은 열리고 정의는 닫혔다 — 소유권 질문은 더 날카로워졌다." author: ObjectStack Team date: 2026-06-16T17:00:00+08:00 -updated: 2026-10-06T10:00:00+08:00 +updated: 2026-10-07T10:00:00+08:00 status: published topic: governance audience: business @@ -21,7 +21,7 @@ tags: - Looker --- -**요약부터**: 이 글이 2026년 6월에 처음 나왔을 때 던진 질문은 "비즈니스 의미 계층을 누가 소유할 것인가"였다. 그 경주는 이미 끝났다. **아홉 달 동안 다섯 플랫폼이 각자 하나씩 출시했다.** 그리고 아무도 예상하지 못한 일이 벌어졌다. 이들은 온톨로지에 이르는 **접근 경로**를 MCP로 열면서, **정의 자체**는 안에 남겨두었다. 이를 **프로토콜은 열리고 정의는 닫혔다**고 부르자. 이제 agent는 가져갈 수 없는 다섯 개의 온톨로지를 읽을 수 있다. 이것은 소유권 질문을 무디게 하는 것이 아니라 날카롭게 만든다. 그리고 답은 그대로다. 애플리케이션, agent, 감사, 여러 벤더의 도구가 함께 의존하는 정의 계층은 당신의 저장소에 두는 중립 계층이어야 한다. +**요약부터**: 이 글이 2026년 6월에 처음 나왔을 때 던진 질문은 "비즈니스 온톨로지 — 비즈니스를 정의하는, 거버넌스가 적용된 계층 — 를 누가 소유할 것인가"였다. 그 경주는 이미 끝났다. **아홉 달 동안 다섯 플랫폼이 각자 하나씩 출시했다** — 둘은 온톨로지라는 이름으로, 셋은 의미 계층으로. 그리고 아무도 예상하지 못한 일이 벌어졌다. 이들은 온톨로지에 이르는 **접근 경로**를 MCP로 열면서, **정의 자체**는 안에 남겨두었다. 이를 **프로토콜은 열리고 정의는 닫혔다**고 부르자. 이제 agent는 가져갈 수 없는 다섯 개의 온톨로지를 읽을 수 있다. 이것은 소유권 질문을 무디게 하는 것이 아니라 날카롭게 만든다. 그리고 답은 그대로다. 애플리케이션, agent, 감사, 여러 벤더의 도구가 함께 의존하는 정의 계층은 당신의 저장소에 두는 중립 계층이어야 한다. 먼저 한 회사가 바로 이 문제로 고객을 잃은 이야기부터 시작하자. 세부는 익명화했지만, 각 단계는 아마 익숙할 것이다. @@ -45,7 +45,7 @@ tags: ## 이 글이 지목한 경주는 이미 끝났다 -이 글이 처음 나왔을 때 참가자는 아직 예측이었다. 이제는 기록이다. 2025년 11월부터 2026년 8월까지 다섯 플랫폼이 비즈니스 의미 계층을 출시했다. +이 글이 처음 나왔을 때 참가자는 아직 예측이었다. 이제는 기록이다. 2025년 11월부터 2026년 8월까지 다섯 플랫폼이 거버넌스가 적용된 비즈니스 정의 계층을 출시했다 — 둘은 온톨로지라는 이름으로, 셋은 의미 계층으로. | 플랫폼 | 무엇을 출시했나 | 시점 | | --- | --- | --- | diff --git a/content/blog/enterprise-ontology-race-open-vs-closed/index.mdx b/content/blog/enterprise-ontology-race-open-vs-closed/index.mdx index 76f3809..6e80a66 100644 --- a/content/blog/enterprise-ontology-race-open-vs-closed/index.mdx +++ b/content/blog/enterprise-ontology-race-open-vs-closed/index.mdx @@ -1,9 +1,9 @@ --- -title: "Open vs. Closed Enterprise Ontologies: Who Owns the Business Semantic Layer?" -description: "Five platforms shipped a business semantic layer between Nov 2025 and Aug 2026, and most opened an MCP read path while keeping the definition inside. Protocol-open, definition-closed — and the ownership question got sharper." +title: "Open vs. Closed Enterprise Ontologies: Who Owns the Business Ontology?" +description: "Five platforms shipped a business definition layer between Nov 2025 and Aug 2026 — two as an ontology, three as a semantic layer — and most opened an MCP read path, not the definition. Protocol-open, definition-closed." author: ObjectStack Team date: 2026-06-16T17:00:00+08:00 -updated: 2026-10-06T10:00:00+08:00 +updated: 2026-10-07T10:00:00+08:00 status: published topic: governance audience: business @@ -21,7 +21,7 @@ tags: - Looker --- -**The short version:** when this post first ran in June 2026 it asked who would own the business semantic layer. The race has since been run: **five platforms shipped one in nine months.** They also did something nobody predicted — they opened the *access path* to their ontology over MCP while keeping the *definition* inside. Call it **protocol-open, definition-closed**. An agent can now read five ontologies it cannot move. That makes the ownership question sharper, not weaker, and the answer is unchanged: the definition layer your apps, agents, auditors, and vendors all depend on should be a neutral layer you hold in your own repository. +**The short version:** when this post first ran in June 2026 it asked who would own the business ontology — the governed layer that defines your business. The race has since been run: **five platforms shipped one in nine months**, two as an ontology by name and three as a semantic layer. They also did something nobody predicted — they opened the *access path* to their ontology over MCP while keeping the *definition* inside. Call it **protocol-open, definition-closed**. An agent can now read five ontologies it cannot move. That makes the ownership question sharper, not weaker, and the answer is unchanged: the definition layer your apps, agents, auditors, and vendors all depend on should be a neutral layer you hold in your own repository. Start with how one company lost a customer over exactly this. The details are anonymized, but you have probably seen every step before. @@ -45,7 +45,7 @@ This was not simply a Yuanfeng mistake. It is the predictable result of handing ## The Race Named in This Post Has Now Been Run -When this piece first published, the entrants were still a forecast. They are now a matter of record. Between November 2025 and August 2026, five platforms shipped a business semantic layer: +When this piece first published, the entrants were still a forecast. They are now a matter of record. Between November 2025 and August 2026, five platforms shipped a governed layer of business definition — two of them an ontology by name, three a semantic layer: | Platform | What it shipped | When | | --- | --- | --- | diff --git a/content/blog/enterprise-ontology-race-open-vs-closed/index.zh-Hans.mdx b/content/blog/enterprise-ontology-race-open-vs-closed/index.zh-Hans.mdx index 9ec2f4c..9f6ab9b 100644 --- a/content/blog/enterprise-ontology-race-open-vs-closed/index.zh-Hans.mdx +++ b/content/blog/enterprise-ontology-race-open-vs-closed/index.zh-Hans.mdx @@ -1,9 +1,9 @@ --- -title: 开放与封闭企业本体:谁拥有业务语义层 -description: 2025 年 11 月到 2026 年 8 月,五家平台各自交付了业务语义层,而且大多把 MCP 读取通道开放了出来,定义本身却仍留在平台里。协议开放,定义封闭——归属之争因此更尖锐了。 +title: 开放与封闭企业本体:谁拥有业务本体 +description: 2025 年 11 月到 2026 年 8 月,五家平台各自交付了业务定义层——两家以本体为名,三家以语义层为名——而且大多把 MCP 读取通道开放了出来,定义本身却仍留在平台里。协议开放,定义封闭——归属之争因此更尖锐了。 author: ObjectStack Team date: 2026-06-16T17:00:00+08:00 -updated: 2026-10-06T10:00:00+08:00 +updated: 2026-10-07T10:00:00+08:00 status: published topic: governance audience: business @@ -21,7 +21,7 @@ tags: - Looker --- -**先给结论**:这篇文章 2026 年 6 月首发时问的是"谁会拥有业务语义层"。这场竞赛现在已经跑完了:**九个月里,五家平台各自交付了一层。** 它们还做了一件没人预料到的事——把访问本体的**通道**用 MCP 开放了出来,而**定义**本身仍留在平台内部。可以叫它**协议开放,定义封闭**。agent 现在能读到五份它搬不走的本体。这让归属问题变得更尖锐,而不是更无关;答案也没有变:被你的应用、agent、审计、甚至多家厂商共同依赖的业务定义层,应该是一份你自己持有的中立定义。 +**先给结论**:这篇文章 2026 年 6 月首发时问的是"谁会拥有业务本体"——那一层受治理的、定义你业务的东西。这场竞赛现在已经跑完了:**九个月里,五家平台各自交付了一层**,两家以本体为名,三家以语义层为名。它们还做了一件没人预料到的事——把访问本体的**通道**用 MCP 开放了出来,而**定义**本身仍留在平台内部。可以叫它**协议开放,定义封闭**。agent 现在能读到五份它搬不走的本体。这让归属问题变得更尖锐,而不是更无关;答案也没有变:被你的应用、agent、审计、甚至多家厂商共同依赖的业务定义层,应该是一份你自己持有的中立定义。 先讲一家公司怎么因为这件事丢了一个客户。细节做了脱敏,但每一步你大概都见过。 @@ -45,7 +45,7 @@ tags: ## 这篇文章点名的那场竞赛,已经跑完了 -这篇文章首发时,参赛者还是一份预测。现在它们是一份记录。2025 年 11 月到 2026 年 8 月,五家平台各自交付了业务语义层: +这篇文章首发时,参赛者还是一份预测。现在它们是一份记录。2025 年 11 月到 2026 年 8 月,五家平台各自交付了受治理的业务定义层——其中两家以本体为名,三家以语义层为名: | 平台 | 交付了什么 | 时间 | | --- | --- | --- | @@ -188,7 +188,7 @@ MCP 那一点在这里同样成立,而且方向恰好是对的:同一份定 九个月前这个问题还是假设,因为这个领域还没交付。现在交付了。五家平台证明了这一层值得建,然后它们中的大多数,在一栋不属于你的房子上开了一扇前门。别人本体上的一个 MCP 端点确实有用——它只是和"拥有这份定义"不是同一件事,而这两者之间的那道缝,正是远峰的 H 集团掉下去的地方。 -被多方共同依赖的层,往往更适合成为中立资产。这一次,业务语义层也该按同样的标准来评估。 +被多方共同依赖的层,往往更适合成为中立资产。这一次,业务本体也该按同样的标准来评估。 ```bash npm create objectstack@latest my-app && cd my-app diff --git a/content/blog/enterprise-ontology-race-open-vs-closed/index.zh-Hant.mdx b/content/blog/enterprise-ontology-race-open-vs-closed/index.zh-Hant.mdx index c38fb3d..d9402b8 100644 --- a/content/blog/enterprise-ontology-race-open-vs-closed/index.zh-Hant.mdx +++ b/content/blog/enterprise-ontology-race-open-vs-closed/index.zh-Hant.mdx @@ -1,10 +1,10 @@ --- # @generated zh-Hant from zh-Hans (s2twp) — edit the zh-Hans file; delete this line to hand-maintain. -title: 開放與封閉企業本體:誰擁有業務語義層 -description: 2025 年 11 月到 2026 年 8 月,五家平臺各自交付了業務語義層,而且大多把 MCP 讀取通道開放了出來,定義本身卻仍留在平臺裡。協議開放,定義封閉——歸屬之爭因此更尖銳了。 +title: 開放與封閉企業本體:誰擁有業務本體 +description: 2025 年 11 月到 2026 年 8 月,五家平臺各自交付了業務定義層——兩家以本體為名,三家以語義層為名——而且大多把 MCP 讀取通道開放了出來,定義本身卻仍留在平臺裡。協議開放,定義封閉——歸屬之爭因此更尖銳了。 author: ObjectStack Team date: 2026-06-16T17:00:00+08:00 -updated: 2026-10-06T10:00:00+08:00 +updated: 2026-10-07T10:00:00+08:00 status: published topic: governance audience: business @@ -22,7 +22,7 @@ tags: - Looker --- -**先給結論**:這篇文章 2026 年 6 月首發時問的是"誰會擁有業務語義層"。這場競賽現在已經跑完了:**九個月裡,五家平臺各自交付了一層。** 它們還做了一件沒人預料到的事——把訪問本體的**通道**用 MCP 開放了出來,而**定義**本身仍留在平臺內部。可以叫它**協議開放,定義封閉**。agent 現在能讀到五份它搬不走的本體。這讓歸屬問題變得更尖銳,而不是更無關;答案也沒有變:被你的應用、agent、審計、甚至多家廠商共同依賴的業務定義層,應該是一份你自己持有的中立定義。 +**先給結論**:這篇文章 2026 年 6 月首發時問的是"誰會擁有業務本體"——那一層受治理的、定義你業務的東西。這場競賽現在已經跑完了:**九個月裡,五家平臺各自交付了一層**,兩家以本體為名,三家以語義層為名。它們還做了一件沒人預料到的事——把訪問本體的**通道**用 MCP 開放了出來,而**定義**本身仍留在平臺內部。可以叫它**協議開放,定義封閉**。agent 現在能讀到五份它搬不走的本體。這讓歸屬問題變得更尖銳,而不是更無關;答案也沒有變:被你的應用、agent、審計、甚至多家廠商共同依賴的業務定義層,應該是一份你自己持有的中立定義。 先講一家公司怎麼因為這件事丟了一個客戶。細節做了脫敏,但每一步你大概都見過。 @@ -46,7 +46,7 @@ tags: ## 這篇文章點名的那場競賽,已經跑完了 -這篇文章首發時,參賽者還是一份預測。現在它們是一份記錄。2025 年 11 月到 2026 年 8 月,五家平臺各自交付了業務語義層: +這篇文章首發時,參賽者還是一份預測。現在它們是一份記錄。2025 年 11 月到 2026 年 8 月,五家平臺各自交付了受治理的業務定義層——其中兩家以本體為名,三家以語義層為名: | 平臺 | 交付了什麼 | 時間 | | --- | --- | --- | @@ -189,7 +189,7 @@ MCP 那一點在這裡同樣成立,而且方向恰好是對的:同一份定 九個月前這個問題還是假設,因為這個領域還沒交付。現在交付了。五家平臺證明了這一層值得建,然後它們中的大多數,在一棟不屬於你的房子上開了一扇前門。別人本體上的一個 MCP 端點確實有用——它只是和"擁有這份定義"不是同一件事,而這兩者之間的那道縫,正是遠峰的 H 集團掉下去的地方。 -被多方共同依賴的層,往往更適合成為中立資產。這一次,業務語義層也該按同樣的標準來評估。 +被多方共同依賴的層,往往更適合成為中立資產。這一次,業務本體也該按同樣的標準來評估。 ```bash npm create objectstack@latest my-app && cd my-app diff --git a/content/blog/executable-ontology-vs-knowledge-ontology/abstract-before-after-en.svg b/content/blog/executable-ontology-vs-knowledge-ontology/abstract-before-after-en.svg new file mode 100644 index 0000000..c6c073e --- /dev/null +++ b/content/blog/executable-ontology-vs-knowledge-ontology/abstract-before-after-en.svg @@ -0,0 +1,114 @@ + + + + + + + One key, three releases: abstract on the object schema + The same object, parsed through each published @objectstack/spec. Dates are npm publish dates. + + + + + + + + + + + 15.1.1 · 2026-07-17 · BEFORE + + + abstract: z.boolean() +   .optional().default(false) +   .describe('Is abstract base +   object (cannot be instantiated)') + + Ledger: dead · no runtime reader + An abstract object still gets a table + and is instantiable. + + + Parsed, unread. + create() accepts · parse() accepts + + + + + + + 16.0.0 · 2026-07-21 + + + ADR-0049: enforce it, or remove it + No committed inheritance feature, + so the key is removed. + + create(): throws, naming the removal + parse(): still strips the key silently — + the authoring path closed, the parse path not. + + + Half closed. + one entry point of two + + + + + + + 17.7.0 · 2026-10-06 · AFTER + + + Unrecognized key(s) on this + object: `abstract`. +   • abstract was removed from the +   spec (ADR-0049) … Remove the key. + + Tombstone, with the prescription + Object inheritance/abstraction is not + implemented. Remove the key. + + + A reader for every key. + create() throws · parse() rejects + + + + + ADR-0049 — A KEY IS IN EXACTLY ONE OF THREE STATES + + + Enforced + a runtime reader changes a decision + + + + Experimental + marked and documented as not enforced + + + + Absent + removed from the spec + + The fourth state — parsed, unmarked, unenforced — is prohibited. abstract had no reader and no committed feature, so it is absent. + Sources: @objectstack/spec 15.1.1, 16.0.0 and 17.7.0 on npm; packages/spec/src/data/object.zod.ts; docs/adr/0049; packages/spec/liveness/README.md. + diff --git a/content/blog/executable-ontology-vs-knowledge-ontology/abstract-before-after.svg b/content/blog/executable-ontology-vs-knowledge-ontology/abstract-before-after.svg new file mode 100644 index 0000000..0c7712a --- /dev/null +++ b/content/blog/executable-ontology-vs-knowledge-ontology/abstract-before-after.svg @@ -0,0 +1,113 @@ + + + + + + + 一个键,三个版本:对象 schema 上的 abstract + 同一个对象,依次用每个已发布的 @objectstack/spec 解析;日期为 npm 发布日期。 + + + + + + + + + + + 15.1.1 · 2026-07-17 · 之前 + + + abstract: z.boolean() +   .optional().default(false) +   .describe('Is abstract base +   object (cannot be instantiated)') + + 台账:dead · 没有运行时读者 + 抽象对象照样建表, + 照样可以实例化。 + + + 被解析,无人读取。 + create() 接受 · parse() 接受 + + + + + + + 16.0.0 · 2026-07-21 + + + ADR-0049:要么强制执行,要么移除 + 没有已承诺的继承功能, + 于是这个键被移除。 + + create():抛错,点名这次移除 + parse():仍然静默丢弃这个键—— + 编写路径关上了,解析路径没有。 + + + 只关了一半。 + 两个入口关了一个 + + + + + + + 17.7.0 · 2026-10-06 · 之后 + + + Unrecognized key(s) on this + object: `abstract`. +   • abstract was removed from the +   spec (ADR-0049) … Remove the key. + + 墓碑,附带处方 + 对象继承/抽象并未实现。 + Remove the key. + + + 每个键都有读者。 + create() 抛错 · parse() 拒绝 + + + + + ADR-0049——一个键恰好处于三种状态之一 + + + 已强制执行 + 有运行时读者,它改变某个决定 + + + + 实验性 + 显式标注,并记录为尚未强制执行 + + + + 不存在 + 从规范中移除 + + 第四种状态——被解析、未标注、未强制执行——是被禁止的。abstract 没有读者、也没有已承诺的功能,所以它不存在。 + 来源:npm 上的 @objectstack/spec 15.1.1、16.0.0 与 17.7.0;packages/spec/src/data/object.zod.ts;docs/adr/0049;packages/spec/liveness/README.md。 + diff --git a/content/blog/executable-ontology-vs-knowledge-ontology/cover-en.svg b/content/blog/executable-ontology-vs-knowledge-ontology/cover-en.svg new file mode 100644 index 0000000..3a20a31 --- /dev/null +++ b/content/blog/executable-ontology-vs-knowledge-ontology/cover-en.svg @@ -0,0 +1,70 @@ + + + + + + + + + + + + + + + EXECUTABLE VS KNOWLEDGE ONTOLOGY + No inheritance, + no axioms, + no reasoner. + Why the three refusals are the point: + a key you can write has a reader + in the runtime, or it is removed. + + Executable + validated, not reasoned over + + + + ONE KEY, TWO RELEASES + + + abstract: true + + + + + @objectstack/spec 15.1.1 + Parses. Key kept. No runtime reader: + an abstract object still gets a table. + PARSED, UNREAD + + + ADR-0049: enforce it, or remove it + + + + @objectstack/spec 17.7.0 + Refused: Unrecognized key(s) on this object. + The message ends: "Remove the key." + A READER FOR EVERY KEY + + + No inheritance was built in between. The promise was removed instead. + + + Measured on the published packages; every quoted line is in the ObjectStack repository. + diff --git a/content/blog/executable-ontology-vs-knowledge-ontology/cover.svg b/content/blog/executable-ontology-vs-knowledge-ontology/cover.svg new file mode 100644 index 0000000..325aad1 --- /dev/null +++ b/content/blog/executable-ontology-vs-knowledge-ontology/cover.svg @@ -0,0 +1,70 @@ + + + + + + + + + + + + + + + 可执行本体 VS 知识本体 + 没有继承, + 没有公理, + 没有推理器。 + 为什么这三个「不」正是重点: + 你能写下的每一个键,运行时都有读者, + 否则它就被移除。 + + 可执行 + 先校验,不推理 + + + + 一个键,两个版本 + + + abstract: true + + + + + @objectstack/spec 15.1.1 + 能解析,键被保留,运行时没有读者: + 抽象对象照样建表。 + 被解析,无人读取 + + + ADR-0049:要么强制执行,要么移除 + + + + @objectstack/spec 17.7.0 + 被拒绝:Unrecognized key(s) on this object. + 消息以「Remove the key.」结尾 + 每个键都有读者 + + + 中间没有建过继承,被移除的是那个承诺。 + + + 在已发布的包上实测;每一句引文都在 ObjectStack 仓库里。 + diff --git a/content/blog/executable-ontology-vs-knowledge-ontology/index.mdx b/content/blog/executable-ontology-vs-knowledge-ontology/index.mdx new file mode 100644 index 0000000..f023a81 --- /dev/null +++ b/content/blog/executable-ontology-vs-knowledge-ontology/index.mdx @@ -0,0 +1,196 @@ +--- +title: "Executable Ontology vs Knowledge Ontology: Why \"No Inheritance, No Axioms, No Reasoner\" Is the Point" +description: "A knowledge ontology is built to be reasoned over; an executable business ontology is built to be validated and run. The proof is one key, abstract, that promised inheritance, did nothing, and was removed rather than kept." +author: ObjectStack Team +date: 2026-10-13 +status: published +topic: app-building +audience: developer +solutions: [] +industries: [] +cover: ./cover-en.svg +tags: + - Ontology + - Executable business ontology + - Knowledge graph +--- + +A knowledge ontology is built to be reasoned over. An executable business ontology is built to be validated, reviewed and run. The ObjectStack README states the difference as three refusals — "no inheritance, no axioms, no reasoner" — and the refusals are not a gap in a roadmap. They are what makes every definition checkable and every diff reviewable: a key you can write either has a reader in the runtime, or it is removed. This post argues that in full, and the proof is one key. + +Here is the key. An object marked abstract, the way a class in a knowledge ontology can be: + +```ts +export const Party = ObjectSchema.create({ + name: 'crm_party', + label: 'Party', + abstract: true, + sharingModel: 'private', + fields: { name: Field.text({ label: 'Name', required: true }) }, +}); +``` + +On `@objectstack/spec@15.1.1`, published 2026-07-17, that definition parses, and `abstract` comes back `true`. On `@objectstack/spec@17.7.0`, the current release, it is refused: + +```text +Unrecognized key(s) on this object: `abstract`. + • `abstract` was removed from the spec (ADR-0049) — object inheritance/abstraction is not implemented, so an abstract object still got a table and was instantiable. Remove the key. +``` + +Nothing was built in between. No inheritance arrived. What changed is that a key the runtime never read was taken out of the spec, with a message that says why. Everything below is the argument for why that is the right move for one kind of ontology and would be the wrong move for the other, drawn from the README, the [Business Ontology](https://github.com/objectstack-ai/objectstack/blob/8caa131e52717ef35fe71742d758f10b8f2b77ee/content/docs/concepts/ontology.mdx) concepts page, the decision record that governs the removal, and the published packages, measured rather than described. + +## Two ontologies, two jobs + +The word *ontology* names two different objects in the same conversations, and [the pillar post](/en/blog/what-does-the-ontology-is-the-software-mean/) listed this as the first of three things an executable business ontology is not. The README's paragraph on the subject is short enough to quote whole: + +> Your app's definition — objects and fields, relations, actions, permissions, flows, and agent and tool definitions — is a **business ontology**: open, versioned, and yours, not code scattered across a framework. It is executable, not a knowledge-representation ontology: no inheritance, no axioms, no reasoner — validated rather than reasoned over — and it is not a semantic layer over your existing systems (federating an external datasource is read-only by default and early). +> +> — objectstack `README.md`, ["What we mean by ontology"](https://github.com/objectstack-ai/objectstack/blob/8caa131e52717ef35fe71742d758f10b8f2b77ee/README.md#L79-L89) + +Take the two kinds in turn, and take the first one seriously, because it is the older and the more rigorous of the two. + +**A knowledge ontology is built for inference.** In the W3C's [OWL 2](https://www.w3.org/TR/owl2-primer/) and the RDF stack under it, you write classes, properties and *axioms*: this class is a subclass of that one; these two classes are disjoint; this property has at most one value; this property chain implies that other property. Then a *reasoner* reads the whole model and derives what follows. The class hierarchy is computed, not just declared. An individual is classified by what is known about it, not by the class someone typed. Contradictions between axioms are found. And the whole thing runs under the open-world assumption: a fact that is absent is unknown, not false. That is exactly the right design when the data comes from many sources, the vocabulary has to be shared across institutions, and the question is "what follows from everything we know?" The [comparison with semantic layers and knowledge graphs](/en/blog/ontology-vs-semantic-layer-vs-knowledge-graph/) gives that tradition its due; this post only needs its shape. + +**An executable business ontology is built so software can be generated, validated, reviewed and run.** The concepts page gives the pipeline in one line: + +```text +objectstack.config.ts -> objectstack.json -> ObjectStack runtime -> database · REST / realtime API · UI · MCP server +``` + +and the rule that governs it in another: "Nothing is reasoned over at runtime: what runs is what was validated." The definition is one business's application, authored as typed metadata, checked by Zod schemas and `os validate`, and then executed. There is no second artifact of derived facts. The world is closed by construction: what the definition says is what the system does, and what it does not say, the system does not do. + +| | Knowledge ontology | Executable business ontology | +| :--- | :--- | :--- | +| **Built for** | Inference over a described domain | Generating, validating, reviewing and running one application | +| **Unit of definition** | Classes, properties, individuals, axioms | Objects and fields, relations, actions, permissions, flows, agent and tool definitions | +| **What reads a definition** | A reasoner, which derives entailments from the whole model | A named runtime reader per key — the database driver, the API, the permission evaluator, the Console, the MCP server | +| **What exists at runtime that nobody wrote** | Inferred facts: the computed hierarchy, classifications, consequences | Nothing. "What runs is what was validated." | +| **How a wrong definition fails** | An inconsistency the reasoner reports, or an entailment nobody expected | A located error at authoring time, before anything runs | +| **Where to look to know what the system does** | The model *and* what it entails | The diff | + +The two columns are not a ranking. They are two answers to two questions, and the second question — can a reviewer tell what this change does, and can a runtime refuse a definition that would not work — is the one an application has to answer. + +![Two pipelines side by side. A knowledge ontology runs classes, properties and axioms through a reasoner and produces entailments derived at runtime, which are not in the diff. An executable business ontology runs typed metadata through validation (strict TypeScript, Zod, os validate) and then executes it into the database, the API, the UI and the MCP server; nothing is reasoned over at runtime. Beneath them, the three refusals: no inheritance, no axioms, no reasoner.](./two-pipelines-en.svg) + +## The three refusals, one at a time + +Each refusal removes something a knowledge ontology is right to have. Each one is there because of what it would cost a reviewer and a gate. + +### No inheritance + +In a class hierarchy, a child inherits the restrictions of its parent, and a reasoner is what tells you the full set a given class ends up with. That is a feature for a vocabulary: declare the general case once and let the engine propagate it. For an application definition it is a cost paid by the reader. A one-line change to a parent changes the behaviour of every child, none of which appears in the diff, and the question "what does this object actually enforce?" cannot be answered from the object's own file. + +ObjectStack's objects do not inherit. The concepts page lists the only kinds of link between objects as "Lookups and master-detail links between objects" — relations between records, declared on the object that holds them. Every object's table, fields, permissions and security posture are stated on that object, so the file is the whole answer and the diff is the whole change. + +### No axioms + +An axiom is a constraint the reasoner uses to *derive*: from "every order has exactly one customer" and an order with two, it concludes the two customers are the same individual, or that the model is inconsistent. An executable ontology has constraints too — required fields, select options, lookup targets, CEL predicates, the security posture every object must declare — but they are used to *reject*, not to derive. The README's second gate is the sentence: `os validate` "rejects metadata that type-checks but would fail silently at runtime: dangling bindings, bad CEL predicates, missing security posture". A definition that violates a constraint does not acquire a consequence. It acquires an error, with a location. + +### No reasoner + +This is the refusal the other two serve. An inference engine is a second source of truth: the facts it derives at runtime were never written anywhere a reviewer could read them. "Nothing is reasoned over at runtime: what runs is what was validated" is the concepts page's rule, and its practical form is the one the pillar post gave: if a permission is not in the diff, it is not in the system. The runtime *derives* surfaces from the artifact — the schema, the API, the UI, the MCP tools — and *enforces* the definition on every call. It does not conclude anything the definition did not say. + +## The proof: a key that promised inheritance and did nothing + +Refusing a reasoner is cheap to say and expensive to keep, because the easy thing to do with a key that sounds like a feature is to leave it in the schema and let something, later, make it mean something. `abstract` is what that looks like, and what the opposite looks like. + +### Before: declared, parsed, unread + +In `@objectstack/spec@15.1.1` the object schema declared the key, with a description that is a textbook definition of an abstract class: + +```ts +abstract: z.boolean().optional().default(false).describe("Is abstract base object (cannot be instantiated)") +``` + +The Studio's object form listed it as a boolean field, with the help text "Abstract base (cannot be instantiated)". And the same package's own liveness ledger — the file the project keeps to say, key by key, whether a runtime reader exists — said this about it: + +```json +"abstract": { + "status": "dead", + "evidence": "no runtime reader", + "authorWarn": true, + "authorHint": "No runtime reader — an abstract object still gets a table and is instantiable. Object inheritance/abstraction is not implemented." +} +``` + +So in 15.1.1 the key parsed, the form offered it, the parsed output carried `abstract: true`, and — in the ledger's words — an abstract object "still gets a table and is instantiable", exactly as if the line were not there. In a knowledge ontology an abstract class is a statement the reasoner honours. Here it was a statement nothing honoured: valid syntax, a plausible line in a diff, and no reader. + +### The rule: enforced, experimental, or absent + +The decision record that governs this is [ADR-0049](https://github.com/objectstack-ai/objectstack/blob/8caa131e52717ef35fe71742d758f10b8f2b77ee/docs/adr/0049-no-unenforced-security-properties.md). It was written about security properties, after an audit that "found that **roughly half of all spec properties are dead**", and it states the rule as three states: + +> 1. **Enforced** — a runtime consumer reads it and changes a decision (`file:line`). +> 2. **`experimental`** — explicitly marked and documented as *not yet enforced*, so authoring it is a known no-op (roadmapped, not a promise). +> 3. **Absent** — removed from the spec. +> +> Shipping a security property in a fourth state — *parsed, unmarked, unenforced* — is prohibited. This is the **enforce-or-remove gate**. + +Its reason is the one that matters for any key, not only a security one: "A security property that parses but does nothing is **worse than absent**: it produces a *false sense of compliance*." And its default, where no feature is committed, is removal: "Removal is preferred over marking when there is no committed roadmap for the property — a smaller spec surface is the stronger default pre-MVP." `abstract` is not a security key; the ADR's own scope note records that the repository "cites this ADR as the enforce-or-remove policy for spec-property retirement generally", and the work order that retired it, [#2377](https://github.com/objectstack-ai/objectstack/issues/2377), says why that generality is deliberate: "Authoring them is a false affordance — especially dangerous for AI-authored metadata." + +### After: a tombstone that tells you what to do + +The removal shipped in 16.0.0. The package [changelog](https://github.com/objectstack-ai/objectstack/blob/8caa131e52717ef35fe71742d758f10b8f2b77ee/packages/spec/CHANGELOG.md#L98572-L98573) records it in one sentence: "object `tags`/`active`/`abstract`: `ObjectSchema.create()` now throws a located error naming the removal. None gated anything at runtime — remove them." Today the key is a tombstone in the object schema's unknown-key guidance, and the message every author who writes it will see is the one at the top of this post, from [`object.zod.ts`](https://github.com/objectstack-ai/objectstack/blob/8caa131e52717ef35fe71742d758f10b8f2b77ee/packages/spec/src/data/object.zod.ts#L1538-L1541): + +```ts +abstract: + '`abstract` was removed from the spec (ADR-0049) — object ' + + 'inheritance/abstraction is not implemented, so an abstract object still ' + + 'got a table and was instantiable. Remove the key.', +``` + +The [liveness ledger's object row](https://github.com/objectstack-ai/objectstack/blob/8caa131e52717ef35fe71742d758f10b8f2b77ee/packages/spec/liveness/README.md#L958) keeps the record: "tags/active/abstract REMOVED (#2377) — tombstoned in UNKNOWN_KEY_GUIDANCE". The before and after were measured on the published packages, by parsing the `Party` object above through each one: + +| `@objectstack/spec` | `ObjectSchema.create()` | `ObjectSchema.safeParse()` | +| :--- | :--- | :--- | +| **15.1.1** (2026-07-17) | Accepts; `abstract: true` kept | Accepts; `abstract: true` kept | +| **16.0.0** (2026-07-21) | Throws, naming the removal | Accepts; `abstract` silently stripped | +| **17.7.0** (2026-10-06) | Throws, naming the removal | Rejects: `unrecognized_keys`, with the prescription | + +The middle row is worth a sentence. In 16.0.0 the authoring path was closed and the parse path was not — `parse()` still dropped the key without a word, the behaviour the schema's own comments trace back to Zod's default `.strip()` and to [#1535](https://github.com/objectstack-ai/objectstack/issues/1535), where an object-level `workflows: [...]` "vanished with no error, no warning, and a green `tsc`". The 17.x message says so itself, in its last sentence: "Until this shape was closed these were dropped silently on the PARSE path — `ObjectSchema.create()` has rejected them, but `defineStack({ objects })`, `/api/v1/meta/types/object` and the Studio form all go through `parse()`, which did not." Closing a key is not one decision; it is one decision per entry point, and a gate that is complete on one and silent on another is the fourth state wearing a different coat. + +![A timeline of one key across three published versions of the spec. In 15.1.1 the key is declared, parses, keeps its value and has no runtime reader, so an abstract object still gets a table. ADR-0049 forces a choice between enforced, experimental and absent, and with no committed inheritance feature the answer is absent. In 16.0.0 create() refuses the key while parse() still strips it silently. In 17.7.0 both paths refuse it with the prescription: remove the key.](./abstract-before-after-en.svg) + +### Why this is the point + +At the moment the ledger said "no runtime reader", there were two honest moves. Build object inheritance, so that `abstract: true` has a reader and a meaning. Or remove the key. The knowledge-ontology habit — and it is a habit, not a mistake, in the tradition it comes from — is to keep the declaration, because in that tradition meaning lives in the model and the engine will honour it. The executable habit is the opposite: the set of keys you can write *is* the set of things the runtime does, and a key with no reader is not a feature in waiting, it is a reviewer's false belief in waiting. + +Call that **a reader for every key**. It is the concrete form of "validated rather than reasoned over". A validator can only be complete over a vocabulary where every term has a consumer; the moment a term is parsed and unread, "valid" stops meaning "will do what it says". The `abstract` removal did not make the ontology smaller for its own sake. It kept the promise that a definition which passes the gate is a definition the runtime will enforce. + +## What a reviewer gets, and what an agent gets + +For the human reviewing a change, the payoff is that the diff is the behaviour. There is no parent to open, no entailment to compute, no key whose effect depends on a feature that may or may not exist. If `sharingModel: 'private'` is in the diff, records are private; if a permission is not in the diff, nobody has it. Asking "what does this line do?" is answered by the runtime reader that line has, and a line with no reader cannot be in the diff at all. + +For the agent writing the change, the payoff is the same fact seen from the other side. The README's claim for why AI-written metadata works is that "an agent's errors become located, corrective text it can read and fix itself". A false affordance defeats exactly that: an agent that learns a format from its examples cannot tell an enforced key from a parsed one, because both validate, both look authoritative, and both appear in samples — the failure the [declared-vs-enforced](/en/glossary/declared-vs-enforced/) entry describes as valid, plausible, inert declarations. Removing the key, and leaving a tombstone that ends in "Remove the key.", turns that failure into a one-line fix the agent can perform without a human noticing there was a problem. + +## In defence of the reasoner: what this does not do + +An honest version of this argument has to say what the executable ontology gives up, because it gives up real things. + +**It does not reason.** No entailments, no classification by description, no consistency checking across a model. If a derived fact is the product you need — "which of these ten thousand records is, by what we know about it, an instance of a high-risk counterparty?" — a reasoner answers that and a validator does not. + +**It does not do open-world inference.** The closed world is the whole point of the design, and it is the wrong assumption for data that is partial by nature: a shared vocabulary across organisations, a record that is true even though your system has never seen the fact that makes it true. A knowledge ontology is built for that case and this one is not. + +**It does not do schema-level inheritance.** There is no base object. A field that six objects share is declared on six objects, or modelled explicitly as a lookup to a shared one. The repetition is deliberate — it is what makes each object's file the whole answer — and it is still repetition, with the maintenance cost repetition has. + +**It does not decide that two things are the same thing.** Whether the "customer" in one system and the "account" in another are one entity is a declaration someone has to make, not an inference the runtime draws; the [enterprise-ontology post](/en/blog/enterprise-ontology-race-open-vs-closed/) is about why that alignment is still work in any model. + +And one limit belongs to the discipline itself rather than to the format: "validated" is only as true as the validators. The audit behind ADR-0049 found half the spec's properties dead *before* the rule existed; the liveness ledger and the gates that read it are what keep the rule true afterwards, and that is a standing piece of maintenance, not a state reached once. The `abstract` story is evidence the discipline is practised, not evidence that it is finished. + +## Hosted, or on your own infrastructure + +The README describes this loop — an agent writes typed metadata in your repository, the gate refuses what would not run, the runtime executes what passed — and then says: "Want the same loop hosted, in the browser, nothing to install? That's ObjectOS, the commercial runtime environment built on ObjectStack." ObjectOS runs hosted in the browser with nothing to install (ObjectOS Cloud) or self-managed on your own infrastructure (ObjectOS Enterprise), and on either edition you can export your ontology and run it on the open-source ObjectStack runtime. The gate is the same on every host, and so is the ontology it guards. + +## Try the refusal yourself + +The README's agent path produces the artifact this post is about in a few minutes, and the refusal is the part worth seeing. + +```bash +npm create objectstack@latest my-app && cd my-app +``` + +Open the project in your coding agent and describe one business object — a customer, a ticket, a work order. Then ask for the line this post started with: add `abstract: true` to it, because it is going to be a base for other objects. Whichever gate reaches it first, the typed one in the editor or `ObjectSchema.create()` at build, refuses it with the message quoted above, and the message ends in the fix. Let the agent take the fix, then preview the result: + +```bash +npx os dev --ui # → http://localhost:3000/_console/ +``` + +What you will find in the diff is an object with no base, no inherited anything, and no key the runtime does not read — which is to say, a definition small enough to review and strict enough to run. If the agent needs steering, [how to point it at ObjectStack](/en/blog/how-to-point-your-agent-at-objectstack/) covers the rule file, the skills bundle and the tool server. The three refusals in the README are not what the format lacks. They are what it enforces. diff --git a/content/blog/executable-ontology-vs-knowledge-ontology/index.zh-Hans.mdx b/content/blog/executable-ontology-vs-knowledge-ontology/index.zh-Hans.mdx new file mode 100644 index 0000000..2a5a14b --- /dev/null +++ b/content/blog/executable-ontology-vs-knowledge-ontology/index.zh-Hans.mdx @@ -0,0 +1,200 @@ +--- +title: 可执行本体 vs 知识本体:为什么「没有继承、没有公理、没有推理器」正是重点 +description: 知识本体是为了被推理而建的;可执行业务本体是为了被校验、被运行而建的。证据是一个键:abstract 承诺了继承、什么都没做,于是它被移除,而不是被保留。 +author: ObjectStack Team +date: 2026-10-13 +status: published +topic: app-building +audience: developer +solutions: [] +industries: [] +cover: ./cover.svg +tags: + - 本体 + - 可执行业务本体 + - 知识图谱 +--- + +知识本体是为了被推理而建的。可执行业务本体是为了被校验、被审阅、被运行而建的。ObjectStack 的 README 把这个区别写成三个「不」——「no inheritance, no axioms, no reasoner」(没有继承、没有公理、没有推理器)——而这三个「不」不是路线图上的空缺。它们正是让每一份定义可检查、每一个 diff 可审阅的原因:你能写下的每一个键,要么在运行时里有一个读者,要么就被移除。本文把这个论点完整讲一遍,证据是一个键。 + +就是这个键。一个被标记为 abstract 的对象,就像知识本体里的一个类可以是抽象类那样: + +```ts +export const Party = ObjectSchema.create({ + name: 'crm_party', + label: 'Party', + abstract: true, + sharingModel: 'private', + fields: { name: Field.text({ label: 'Name', required: true }) }, +}); +``` + +在 2026-07-17 发布的 `@objectstack/spec@15.1.1` 上,这份定义能解析,`abstract` 原样返回 `true`。在当前版本 `@objectstack/spec@17.7.0` 上,它被拒绝: + +```text +Unrecognized key(s) on this object: `abstract`. + • `abstract` was removed from the spec (ADR-0049) — object inheritance/abstraction is not implemented, so an abstract object still got a table and was instantiable. Remove the key. +``` + +这中间什么都没有建。继承没有到来。变化只有一件:一个运行时从未读过的键被从规范中拿掉了,并附上一条说明原因的消息。下文全部是在论证:为什么对一种本体来说这是正确的做法,而对另一种本体来说会是错误的做法。来源是 README、它链接的 [Business Ontology](https://github.com/objectstack-ai/objectstack/blob/8caa131e52717ef35fe71742d758f10b8f2b77ee/content/docs/concepts/ontology.mdx) 概念页、管辖这次移除的决策记录,以及已发布的包——测量而非描述。 + +## 两种本体,两种职责 + +「本体」这个词在同一场对话里指着两种不同的东西,[那篇定义文章](/zh-Hans/blog/what-does-the-ontology-is-the-software-mean/)把它列为可执行业务本体「不是」的三样东西中的第一样。README 关于这件事的那一段短到可以整段引用: + +> Your app's definition — objects and fields, relations, actions, permissions, flows, and agent and tool definitions — is a **business ontology**: open, versioned, and yours, not code scattered across a framework. It is executable, not a knowledge-representation ontology: no inheritance, no axioms, no reasoner — validated rather than reasoned over — and it is not a semantic layer over your existing systems (federating an external datasource is read-only by default and early). +> +> —— objectstack `README.md`,[「What we mean by ontology」](https://github.com/objectstack-ai/objectstack/blob/8caa131e52717ef35fe71742d758f10b8f2b77ee/README.md#L79-L89) + +(大意:你的应用定义——对象与字段、关系、动作、权限、流程、Agent 与工具定义——是一份**业务本体**:开放、有版本、归你所有,不是散落在框架里的代码。它是可执行的,不是知识表示意义上的本体:没有继承、没有公理、没有推理器——先校验,不推理——它也不是架在你现有系统之上的语义层。) + +两种本体依次来看,而且要认真对待第一种,因为它更古老,也更严谨。 + +**知识本体是为推理而建的。** 在 W3C 的 [OWL 2](https://www.w3.org/TR/owl2-primer/) 及其底下的 RDF 技术栈里,你写的是类、属性和*公理*:这个类是那个类的子类;这两个类互斥;这个属性至多一个值;这条属性链蕴含另一个属性。然后由一个*推理器*读取整个模型,推导出随之成立的东西。类的层级是算出来的,不只是声明出来的。一个个体按照「关于它已知什么」被归类,而不是按照某人打上去的类名。公理之间的矛盾会被找出来。整件事运行在开放世界假设之下:一个缺席的事实是未知的,不是假的。当数据来自许多来源、词汇需要跨机构共享、要问的问题是「从我们知道的一切能推出什么」时,这正是对的设计。[与语义层、知识图谱的比较](/en/blog/ontology-vs-semantic-layer-vs-knowledge-graph/)给了这一传统应有的公道;本文只需要它的轮廓。 + +**可执行业务本体是为了让软件能被生成、校验、审阅和运行而建的。** 概念页用一行写出整条流水线: + +```text +objectstack.config.ts -> objectstack.json -> ObjectStack runtime -> database · REST / realtime API · UI · MCP server +``` + +再用一句写出管辖它的规则:「Nothing is reasoned over at runtime: what runs is what was validated.」(运行时不做任何推理:跑的就是被校验过的东西。)定义是一家企业的应用,以类型化元数据编写,由 Zod schema 和 `os validate` 检查,然后被执行。不存在第二份由推导事实构成的工件。世界按构造就是封闭的:定义说了什么,系统就做什么;定义没说的,系统就不做。 + +| | 知识本体 | 可执行业务本体 | +| :--- | :--- | :--- | +| **为什么而建** | 对一个被描述的领域做推理 | 生成、校验、审阅并运行一个应用 | +| **定义的单元** | 类、属性、个体、公理 | 对象与字段、关系、动作、权限、流程、Agent 与工具定义 | +| **谁来读定义** | 推理器,从整个模型推导出结论 | 每个键一个具名的运行时读者——数据库驱动、API、权限评估器、控制台、MCP 服务器 | +| **运行时存在、却没人写过的东西** | 推导出的事实:算出来的层级、分类、后果 | 没有。「跑的就是被校验过的东西。」 | +| **错误的定义如何失败** | 推理器报告一处不一致,或者推出一条没人预料到的结论 | 编写时就给出一个带位置的错误,在任何东西运行之前 | +| **想知道系统做什么,去看哪里** | 模型*加上*它蕴含的一切 | diff | + +这两列不是排名。它们是对两个问题的两个回答,而第二个问题——审阅者能否看出这次改动做了什么、运行时能否拒绝一份跑不起来的定义——是一个应用必须回答的那个。 + +![两条并排的流水线。知识本体把类、属性和公理交给推理器,产出在运行时推导出来的结论,它们不在 diff 里。可执行业务本体把类型化元数据交给校验(严格 TypeScript、Zod、os validate),然后执行成数据库、API、UI 和 MCP 服务器;运行时不做任何推理。下方是三个「不」:没有继承、没有公理、没有推理器。](./two-pipelines.svg) + +## 三个「不」,逐个来看 + +每一个「不」都拿掉了一样知识本体理应拥有的东西。每一个「不」都在那里,是因为它对审阅者和门禁意味着什么代价。 + +### 没有继承 + +在类层级里,子类继承父类的约束,而推理器负责告诉你某个类最终背负的全部约束。对一份词汇表来说这是功能:把一般情况声明一次,让引擎去传播。对一份应用定义来说,这是由读者支付的代价。父类上改一行,每个子类的行为都变了,而它们一个都不出现在 diff 里;「这个对象到底强制执行了什么?」这个问题,从对象自己的文件里答不出来。 + +ObjectStack 的对象不继承。概念页把对象之间唯一的几种链接列为「Lookups and master-detail links between objects」(对象之间的查找与主从链接)——记录之间的关系,声明在持有它的那个对象上。每个对象的表、字段、权限和安全姿态都写在那个对象上,所以文件就是完整的答案,diff 就是完整的改动。 + +### 没有公理 + +公理是推理器用来*推导*的约束:从「每张订单恰好有一个客户」和一张有两个客户的订单出发,它会得出两个客户是同一个个体,或者模型不一致。可执行本体也有约束——必填字段、选项列表、查找目标、CEL 谓词、每个对象必须声明的安全姿态——但它们用来*拒绝*,不用来推导。README 的第二道门就是这句话:`os validate`「rejects metadata that type-checks but would fail silently at runtime: dangling bindings, bad CEL predicates, missing security posture」(拒绝那些能通过类型检查、却会在运行时静默失败的元数据:悬空绑定、错误的 CEL 谓词、缺失的安全姿态)。一份违反约束的定义不会获得一个后果,它获得的是一个带位置的错误。 + +### 没有推理器 + +这是另外两个「不」所服务的那一个。推理引擎是第二个真相来源:它在运行时推导出的事实,从未写在任何审阅者读得到的地方。「Nothing is reasoned over at runtime: what runs is what was validated」是概念页的规则,它的实践形态就是那篇定义文章给出的那句:如果一条权限不在 diff 里,它就不在系统里。运行时从工件*推导*出各个表面——schema、API、UI、MCP 工具——并在每一次调用上*强制执行*这份定义。它不会得出任何定义没说过的结论。 + +## 证据:一个承诺了继承、却什么都没做的键 + +拒绝推理器说起来便宜,守起来昂贵,因为面对一个听起来像功能的键,最省事的做法就是把它留在 schema 里,等以后有什么东西来赋予它意义。`abstract` 就是那种做法的样子,也是相反做法的样子。 + +### 之前:被声明、被解析、无人读取 + +在 `@objectstack/spec@15.1.1` 里,对象 schema 声明了这个键,附带一句教科书式的抽象类定义: + +```ts +abstract: z.boolean().optional().default(false).describe("Is abstract base object (cannot be instantiated)") +``` + +Studio 的对象表单把它列为一个布尔字段,帮助文本是「Abstract base (cannot be instantiated)」。而同一个包自带的活性台账——项目用来逐键记录「是否存在运行时读者」的那个文件——是这么说它的: + +```json +"abstract": { + "status": "dead", + "evidence": "no runtime reader", + "authorWarn": true, + "authorHint": "No runtime reader — an abstract object still gets a table and is instantiable. Object inheritance/abstraction is not implemented." +} +``` + +所以在 15.1.1 里,这个键能解析,表单提供它,解析输出带着 `abstract: true`,而——用台账的原话——抽象对象「still gets a table and is instantiable」(照样建表、照样可以实例化),就像那一行根本不存在一样。在知识本体里,抽象类是一条推理器会遵守的陈述。在这里,它是一条无人遵守的陈述:合法的语法,diff 里一行看着像那么回事的内容,没有读者。 + +### 规则:已强制执行、实验性,或者不存在 + +管辖这件事的决策记录是 [ADR-0049](https://github.com/objectstack-ai/objectstack/blob/8caa131e52717ef35fe71742d758f10b8f2b77ee/docs/adr/0049-no-unenforced-security-properties.md)。它是针对安全属性写的,起因是一次审计「found that **roughly half of all spec properties are dead**」(发现规范里大约一半的属性是死的),它把规则写成三种状态: + +> 1. **Enforced** — a runtime consumer reads it and changes a decision (`file:line`). +> 2. **`experimental`** — explicitly marked and documented as *not yet enforced*, so authoring it is a known no-op (roadmapped, not a promise). +> 3. **Absent** — removed from the spec. +> +> Shipping a security property in a fourth state — *parsed, unmarked, unenforced* — is prohibited. This is the **enforce-or-remove gate**. + +(大意:一个键恰好处于三种状态之一——**已强制执行**:有运行时消费者读取它并改变某个决定;**实验性**:显式标注并记录为尚未强制执行,写它是已知的空操作;**不存在**:从规范中移除。以第四种状态——被解析、未标注、未强制执行——发布,是被禁止的。这就是「要么强制执行,要么移除」门禁。) + +它给出的理由对任何键都成立,不只是安全键:「A security property that parses but does nothing is **worse than absent**: it produces a *false sense of compliance*.」(一个能解析却什么都不做的安全属性,**比不存在更糟**:它制造一种*虚假的合规感*。)而它在没有已承诺功能时的默认选择是移除:「Removal is preferred over marking when there is no committed roadmap for the property — a smaller spec surface is the stronger default pre-MVP.」(当这个属性没有已承诺的路线图时,移除优于标注——更小的规范面是 MVP 之前更稳的默认值。)`abstract` 不是安全键;ADR 自己的范围说明记录了,仓库「cites this ADR as the enforce-or-remove policy for spec-property retirement generally」(把这份 ADR 作为规范属性退役的通用「要么强制执行、要么移除」政策来引用),而退役它的工单 [#2377](https://github.com/objectstack-ai/objectstack/issues/2377) 说明了这种推广为什么是有意的:「Authoring them is a false affordance — especially dangerous for AI-authored metadata.」(编写它们是一种虚假的可供性——对 AI 编写的元数据尤其危险。) + +### 之后:一块告诉你该怎么做的墓碑 + +这次移除随 16.0.0 发布。包的[变更日志](https://github.com/objectstack-ai/objectstack/blob/8caa131e52717ef35fe71742d758f10b8f2b77ee/packages/spec/CHANGELOG.md#L98572-L98573)用一句话记录了它:「object `tags`/`active`/`abstract`: `ObjectSchema.create()` now throws a located error naming the removal. None gated anything at runtime — remove them.」(`ObjectSchema.create()` 现在会抛出一个带位置的错误,点名这次移除。它们在运行时从未把关任何事——移除它们。)今天这个键是对象 schema 未知键指引里的一块墓碑,每个写下它的作者看到的消息,就是本文开头那一条,出自 [`object.zod.ts`](https://github.com/objectstack-ai/objectstack/blob/8caa131e52717ef35fe71742d758f10b8f2b77ee/packages/spec/src/data/object.zod.ts#L1538-L1541): + +```ts +abstract: + '`abstract` was removed from the spec (ADR-0049) — object ' + + 'inheritance/abstraction is not implemented, so an abstract object still ' + + 'got a table and was instantiable. Remove the key.', +``` + +[活性台账的 object 一行](https://github.com/objectstack-ai/objectstack/blob/8caa131e52717ef35fe71742d758f10b8f2b77ee/packages/spec/liveness/README.md#L958)保留着记录:「tags/active/abstract REMOVED (#2377) — tombstoned in UNKNOWN_KEY_GUIDANCE」。之前与之后是在已发布的包上实测的:把上面那个 `Party` 对象依次交给每个版本解析。 + +| `@objectstack/spec` | `ObjectSchema.create()` | `ObjectSchema.safeParse()` | +| :--- | :--- | :--- | +| **15.1.1**(2026-07-17) | 接受;`abstract: true` 保留 | 接受;`abstract: true` 保留 | +| **16.0.0**(2026-07-21) | 抛错,点名这次移除 | 接受;`abstract` 被静默丢弃 | +| **17.7.0**(2026-10-06) | 抛错,点名这次移除 | 拒绝:`unrecognized_keys`,附带处方 | + +中间那一行值得多说一句。在 16.0.0 里,编写路径关上了,解析路径没有——`parse()` 仍然一声不响地丢掉这个键,这正是 schema 自己的注释追溯到 Zod 默认 `.strip()` 和 [#1535](https://github.com/objectstack-ai/objectstack/issues/1535) 的那种行为:当时一个对象级的 `workflows: [...]`「vanished with no error, no warning, and a green `tsc`」(消失得无声无息,没有错误、没有警告,`tsc` 还是绿的)。17.x 的消息在它的最后一句里自己说了这件事:「Until this shape was closed these were dropped silently on the PARSE path — `ObjectSchema.create()` has rejected them, but `defineStack({ objects })`, `/api/v1/meta/types/object` and the Studio form all go through `parse()`, which did not.」(在这个形状被关闭之前,它们在解析路径上被静默丢弃——`ObjectSchema.create()` 已经拒绝它们,但 `defineStack({ objects })`、`/api/v1/meta/types/object` 和 Studio 表单都走 `parse()`,而 `parse()` 没有拒绝。)关闭一个键不是一个决定,而是每个入口各一个决定;在一个入口完整、在另一个入口沉默的门禁,就是换了件外套的第四种状态。 + +![一个键跨三个已发布版本的时间线。15.1.1 里这个键被声明、能解析、保留取值、没有运行时读者,抽象对象照样建表。ADR-0049 迫使在已强制执行、实验性、不存在之间做选择,没有已承诺的继承功能,答案就是不存在。16.0.0 里 create() 拒绝这个键,parse() 仍静默丢弃它。17.7.0 里两条路径都拒绝它,附带处方:Remove the key。](./abstract-before-after.svg) + +### 为什么这正是重点 + +在台账写下「no runtime reader」的那一刻,诚实的做法有两种。要么建出对象继承,让 `abstract: true` 有读者、有意义;要么移除这个键。知识本体的习惯——在它所来自的传统里,这是习惯,不是错误——是保留声明,因为在那个传统里意义住在模型中,引擎会遵守它。可执行的习惯正相反:你能写下的键的集合*就是*运行时做的事的集合,一个没有读者的键不是等待中的功能,而是等待中的、审阅者的错误信念。 + +姑且把这叫作**每个键都有读者**。它是「先校验,不推理」的具体形态。校验器只有在每个词都有消费者的词汇表上才可能完整;一旦某个词被解析却无人读取,「合法」就不再意味着「会照它说的去做」。移除 `abstract` 不是为了让本体更小而更小。它守住的是一个承诺:通过门禁的定义,就是运行时会强制执行的定义。 + +## 审阅者得到什么,Agent 得到什么 + +对审阅改动的人来说,回报是:diff 就是行为。没有需要打开的父类,没有需要计算的推导结论,没有哪个键的效果取决于某个可能存在也可能不存在的功能。如果 `sharingModel: 'private'` 在 diff 里,记录就是私有的;如果一条权限不在 diff 里,就没人拥有它。「这一行做什么?」由这一行的运行时读者来回答,而一行没有读者的内容根本进不了 diff。 + +对写改动的 Agent 来说,回报是同一件事的另一面。README 对「AI 编写的元数据为什么行得通」的主张是:「an agent's errors become located, corrective text it can read and fix itself」(Agent 的错误变成了有定位的、可纠正的文本,它自己能读、能改)。一个虚假的可供性恰恰打败这一点:从示例里学会一种格式的 Agent,分不清一个被强制执行的键和一个只被解析的键,因为两者都能通过校验、都看着权威、都出现在样本中——这正是[「声明 vs 强制执行」](/zh-Hans/glossary/declared-vs-enforced/)词条描述的失败:合法、可信、却无效的声明。移除这个键,并留下一块以「Remove the key.」结尾的墓碑,把那种失败变成一个 Agent 自己就能完成的一行修复,连人都不必注意到曾经有过问题。 + +## 为推理器辩护:这个本体不做什么 + +这个论点的诚实版本必须说清可执行本体放弃了什么,因为它放弃的是实打实的东西。 + +**它不推理。** 没有推导结论,没有按描述归类,没有跨整个模型的一致性检查。如果推导出来的事实正是你需要的产品——「这一万条记录里,按我们对它们的了解,哪些是高风险交易对手的实例?」——推理器能回答,校验器不能。 + +**它不做开放世界推理。** 封闭世界是整个设计的要点,而对于天然不完整的数据,它是错误的假设:一套跨组织共享的词汇;一条即便你的系统从未见过使它为真的那个事实、却依然为真的记录。知识本体是为那种情形而建的,这个不是。 + +**它不做 schema 层面的继承。** 没有基础对象。六个对象共有的一个字段,要在六个对象上声明,或者显式建模为指向某个共享对象的查找。这种重复是有意的——它正是让每个对象的文件成为完整答案的原因——但它仍然是重复,带着重复固有的维护成本。 + +**它不替你判定两样东西是同一样东西。** 一个系统里的「客户」和另一个系统里的「账户」是不是同一个实体,是某个人必须做出的声明,不是运行时得出的推论;[那篇企业本体文章](/zh-Hans/blog/enterprise-ontology-race-open-vs-closed/)讲的正是为什么这种对齐在任何模型下都仍然是工作。 + +还有一个限制属于这套纪律本身,而不属于格式:「校验过」只在校验器本身可信的程度上为真。ADR-0049 背后的审计是在这条规则存在*之前*发现规范里一半属性已死的;活性台账和读取它的门禁,才是此后让规则持续为真的东西,而那是一项常设的维护工作,不是一次到达的状态。`abstract` 的故事是这套纪律被践行的证据,不是它已经完成的证据。 + +## 托管,或在你自己的基础设施上 + +README 描述了这个循环——Agent 在你的仓库里写类型化元数据,门禁拒绝跑不起来的东西,运行时执行通过的东西——然后说:「想把同一个循环托管起来、在浏览器里用、什么都不用安装?那就是 ObjectOS——建立在 ObjectStack 之上的商业运行环境。」ObjectOS 既可托管在浏览器中、无需安装(ObjectOS Cloud),也可自管部署在你自己的基础设施上(ObjectOS Enterprise);无论哪个版本,你都可以导出本体,拿到开源的 ObjectStack 运行时上运行。门禁在每个宿主上都一样,它守护的本体也一样。 + +## 自己试一次这个拒绝 + +README 的 Agent 路径几分钟就能产出本文讨论的那份工件,而那个拒绝正是值得亲眼看一次的部分。 + +```bash +npm create objectstack@latest my-app && cd my-app +``` + +在你的编码 Agent 里打开项目,描述一个业务对象——客户、工单、工作订单。然后要求本文开头的那一行:给它加上 `abstract: true`,理由是它将来要做其他对象的基础。无论哪道门先碰到它——编辑器里的类型门,还是构建时的 `ObjectSchema.create()`——都会用上面引用的那条消息拒绝它,而消息以修复方法结尾。让 Agent 采纳这个修复,然后预览结果: + +```bash +npx os dev --ui # → http://localhost:3000/_console/ +``` + +你在 diff 里看到的会是一个没有基类、没有任何继承而来的东西、没有任何运行时不读的键的对象——也就是说,一份小到可以审阅、严到可以运行的定义。如果 Agent 需要引导,[如何让它对准 ObjectStack](/en/blog/how-to-point-your-agent-at-objectstack/)覆盖了规则文件、技能包和工具服务器。README 里的三个「不」不是这个格式缺少的东西,而是它强制执行的东西。 diff --git a/content/blog/executable-ontology-vs-knowledge-ontology/index.zh-Hant.mdx b/content/blog/executable-ontology-vs-knowledge-ontology/index.zh-Hant.mdx new file mode 100644 index 0000000..72eb91c --- /dev/null +++ b/content/blog/executable-ontology-vs-knowledge-ontology/index.zh-Hant.mdx @@ -0,0 +1,201 @@ +--- +# @generated zh-Hant from zh-Hans (s2twp) — edit the zh-Hans file; delete this line to hand-maintain. +title: 可執行本體 vs 知識本體:為什麼「沒有繼承、沒有公理、沒有推理器」正是重點 +description: 知識本體是為了被推理而建的;可執行業務本體是為了被校驗、被執行而建的。證據是一個鍵:abstract 承諾了繼承、什麼都沒做,於是它被移除,而不是被保留。 +author: ObjectStack Team +date: 2026-10-13 +status: published +topic: app-building +audience: developer +solutions: [] +industries: [] +cover: ./cover.svg +tags: + - 本體 + - 可執行業務本體 + - 知識圖譜 +--- + +知識本體是為了被推理而建的。可執行業務本體是為了被校驗、被審閱、被執行而建的。ObjectStack 的 README 把這個區別寫成三個「不」——「no inheritance, no axioms, no reasoner」(沒有繼承、沒有公理、沒有推理器)——而這三個「不」不是路線圖上的空缺。它們正是讓每一份定義可檢查、每一個 diff 可審閱的原因:你能寫下的每一個鍵,要麼在執行時裡有一個讀者,要麼就被移除。本文把這個論點完整講一遍,證據是一個鍵。 + +就是這個鍵。一個被標記為 abstract 的物件,就像知識本體裡的一個類可以是抽象類那樣: + +```ts +export const Party = ObjectSchema.create({ + name: 'crm_party', + label: 'Party', + abstract: true, + sharingModel: 'private', + fields: { name: Field.text({ label: 'Name', required: true }) }, +}); +``` + +在 2026-07-17 釋出的 `@objectstack/spec@15.1.1` 上,這份定義能解析,`abstract` 原樣返回 `true`。在當前版本 `@objectstack/spec@17.7.0` 上,它被拒絕: + +```text +Unrecognized key(s) on this object: `abstract`. + • `abstract` was removed from the spec (ADR-0049) — object inheritance/abstraction is not implemented, so an abstract object still got a table and was instantiable. Remove the key. +``` + +這中間什麼都沒有建。繼承沒有到來。變化只有一件:一個執行時從未讀過的鍵被從規範中拿掉了,並附上一條說明原因的訊息。下文全部是在論證:為什麼對一種本體來說這是正確的做法,而對另一種本體來說會是錯誤的做法。來源是 README、它連結的 [Business Ontology](https://github.com/objectstack-ai/objectstack/blob/8caa131e52717ef35fe71742d758f10b8f2b77ee/content/docs/concepts/ontology.mdx) 概念頁、管轄這次移除的決策記錄,以及已釋出的包——測量而非描述。 + +## 兩種本體,兩種職責 + +「本體」這個詞在同一場對話裡指著兩種不同的東西,[那篇定義文章](/zh-Hant/blog/what-does-the-ontology-is-the-software-mean/)把它列為可執行業務本體「不是」的三樣東西中的第一樣。README 關於這件事的那一段短到可以整段引用: + +> Your app's definition — objects and fields, relations, actions, permissions, flows, and agent and tool definitions — is a **business ontology**: open, versioned, and yours, not code scattered across a framework. It is executable, not a knowledge-representation ontology: no inheritance, no axioms, no reasoner — validated rather than reasoned over — and it is not a semantic layer over your existing systems (federating an external datasource is read-only by default and early). +> +> —— objectstack `README.md`,[「What we mean by ontology」](https://github.com/objectstack-ai/objectstack/blob/8caa131e52717ef35fe71742d758f10b8f2b77ee/README.md#L79-L89) + +(大意:你的應用定義——物件與欄位、關係、動作、權限、流程、Agent 與工具定義——是一份**業務本體**:開放、有版本、歸你所有,不是散落在框架裡的程式碼。它是可執行的,不是知識表示意義上的本體:沒有繼承、沒有公理、沒有推理器——先校驗,不推理——它也不是架在你現有系統之上的語義層。) + +兩種本體依次來看,而且要認真對待第一種,因為它更古老,也更嚴謹。 + +**知識本體是為推理而建的。** 在 W3C 的 [OWL 2](https://www.w3.org/TR/owl2-primer/) 及其底下的 RDF 技術棧裡,你寫的是類、屬性和*公理*:這個類是那個類的子類;這兩個類互斥;這個屬性至多一個值;這條屬性鏈蘊含另一個屬性。然後由一個*推理器*讀取整個模型,推匯出隨之成立的東西。類的層級是算出來的,不只是宣告出來的。一個個體按照「關於它已知什麼」被歸類,而不是按照某人打上去的類名。公理之間的矛盾會被找出來。整件事執行在開放世界假設之下:一個缺席的事實是未知的,不是假的。當資料來自許多來源、詞彙需要跨機構共享、要問的問題是「從我們知道的一切能推出什麼」時,這正是對的設計。[與語義層、知識圖譜的比較](/en/blog/ontology-vs-semantic-layer-vs-knowledge-graph/)給了這一傳統應有的公道;本文只需要它的輪廓。 + +**可執行業務本體是為了讓軟體能被生成、校驗、審閱和執行而建的。** 概念頁用一行寫出整條流水線: + +```text +objectstack.config.ts -> objectstack.json -> ObjectStack runtime -> database · REST / realtime API · UI · MCP server +``` + +再用一句寫出管轄它的規則:「Nothing is reasoned over at runtime: what runs is what was validated.」(執行時不做任何推理:跑的就是被校驗過的東西。)定義是一家企業的應用,以型別化後設資料編寫,由 Zod schema 和 `os validate` 檢查,然後被執行。不存在第二份由推導事實構成的工件。世界按構造就是封閉的:定義說了什麼,系統就做什麼;定義沒說的,系統就不做。 + +| | 知識本體 | 可執行業務本體 | +| :--- | :--- | :--- | +| **為什麼而建** | 對一個被描述的領域做推理 | 生成、校驗、審閱並執行一個應用 | +| **定義的單元** | 類、屬性、個體、公理 | 物件與欄位、關係、動作、權限、流程、Agent 與工具定義 | +| **誰來讀定義** | 推理器,從整個模型推匯出結論 | 每個鍵一個具名的執行時讀者——資料庫驅動、API、權限評估器、控制台、MCP 伺服器 | +| **執行時存在、卻沒人寫過的東西** | 推匯出的事實:算出來的層級、分類、後果 | 沒有。「跑的就是被校驗過的東西。」 | +| **錯誤的定義如何失敗** | 推理器報告一處不一致,或者推出一條沒人預料到的結論 | 編寫時就給出一個帶位置的錯誤,在任何東西執行之前 | +| **想知道系統做什麼,去看哪裡** | 模型*加上*它蘊含的一切 | diff | + +這兩列不是排名。它們是對兩個問題的兩個回答,而第二個問題——審閱者能否看出這次改動做了什麼、執行時能否拒絕一份跑不起來的定義——是一個應用必須回答的那個。 + +![兩條並排的流水線。知識本體把類、屬性和公理交給推理器,產出在執行時推匯出來的結論,它們不在 diff 裡。可執行業務本體把型別化後設資料交給校驗(嚴格 TypeScript、Zod、os validate),然後執行成資料庫、API、UI 和 MCP 伺服器;執行時不做任何推理。下方是三個「不」:沒有繼承、沒有公理、沒有推理器。](./two-pipelines.svg) + +## 三個「不」,逐個來看 + +每一個「不」都拿掉了一樣知識本體理應擁有的東西。每一個「不」都在那裡,是因為它對審閱者和門禁意味著什麼代價。 + +### 沒有繼承 + +在類層級裡,子類繼承父類的約束,而推理器負責告訴你某個類最終揹負的全部約束。對一份詞彙表來說這是功能:把一般情況宣告一次,讓引擎去傳播。對一份應用定義來說,這是由讀者支付的代價。父類上改一行,每個子類的行為都變了,而它們一個都不出現在 diff 裡;「這個物件到底強制執行了什麼?」這個問題,從物件自己的檔案裡答不出來。 + +ObjectStack 的物件不繼承。概念頁把物件之間唯一的幾種連結列為「Lookups and master-detail links between objects」(物件之間的查詢與主從連結)——記錄之間的關係,宣告在持有它的那個物件上。每個物件的表、欄位、權限和安全姿態都寫在那個物件上,所以檔案就是完整的答案,diff 就是完整的改動。 + +### 沒有公理 + +公理是推理器用來*推導*的約束:從「每張訂單恰好有一個客戶」和一張有兩個客戶的訂單出發,它會得出兩個客戶是同一個個體,或者模型不一致。可執行本體也有約束——必填欄位、選項列表、查詢目標、CEL 謂詞、每個物件必須宣告的安全姿態——但它們用來*拒絕*,不用來推導。README 的第二道門就是這句話:`os validate`「rejects metadata that type-checks but would fail silently at runtime: dangling bindings, bad CEL predicates, missing security posture」(拒絕那些能通過型別檢查、卻會在執行時靜默失敗的後設資料:懸空繫結、錯誤的 CEL 謂詞、缺失的安全姿態)。一份違反約束的定義不會獲得一個後果,它獲得的是一個帶位置的錯誤。 + +### 沒有推理器 + +這是另外兩個「不」所服務的那一個。推理引擎是第二個真相來源:它在執行時推匯出的事實,從未寫在任何審閱者讀得到的地方。「Nothing is reasoned over at runtime: what runs is what was validated」是概念頁的規則,它的實踐形態就是那篇定義文章給出的那句:如果一條權限不在 diff 裡,它就不在系統裡。執行時從工件*推導*出各個表面——schema、API、UI、MCP 工具——並在每一次呼叫上*強制執行*這份定義。它不會得出任何定義沒說過的結論。 + +## 證據:一個承諾了繼承、卻什麼都沒做的鍵 + +拒絕推理器說起來便宜,守起來昂貴,因為面對一個聽起來像功能的鍵,最省事的做法就是把它留在 schema 裡,等以後有什麼東西來賦予它意義。`abstract` 就是那種做法的樣子,也是相反做法的樣子。 + +### 之前:被宣告、被解析、無人讀取 + +在 `@objectstack/spec@15.1.1` 裡,物件 schema 聲明瞭這個鍵,附帶一句教科書式的抽象類定義: + +```ts +abstract: z.boolean().optional().default(false).describe("Is abstract base object (cannot be instantiated)") +``` + +Studio 的物件表單把它列為一個布林欄位,幫助文本是「Abstract base (cannot be instantiated)」。而同一個包自帶的活性臺賬——專案用來逐鍵記錄「是否存在執行時讀者」的那個檔案——是這麼說它的: + +```json +"abstract": { + "status": "dead", + "evidence": "no runtime reader", + "authorWarn": true, + "authorHint": "No runtime reader — an abstract object still gets a table and is instantiable. Object inheritance/abstraction is not implemented." +} +``` + +所以在 15.1.1 裡,這個鍵能解析,表單提供它,解析輸出帶著 `abstract: true`,而——用臺賬的原話——抽象物件「still gets a table and is instantiable」(照樣建表、照樣可以實例化),就像那一行根本不存在一樣。在知識本體裡,抽象類是一條推理器會遵守的陳述。在這裡,它是一條無人遵守的陳述:合法的語法,diff 裡一行看著像那麼回事的內容,沒有讀者。 + +### 規則:已強制執行、實驗性,或者不存在 + +管轄這件事的決策記錄是 [ADR-0049](https://github.com/objectstack-ai/objectstack/blob/8caa131e52717ef35fe71742d758f10b8f2b77ee/docs/adr/0049-no-unenforced-security-properties.md)。它是針對安全屬性寫的,起因是一次審計「found that **roughly half of all spec properties are dead**」(發現規範裡大約一半的屬性是死的),它把規則寫成三種狀態: + +> 1. **Enforced** — a runtime consumer reads it and changes a decision (`file:line`). +> 2. **`experimental`** — explicitly marked and documented as *not yet enforced*, so authoring it is a known no-op (roadmapped, not a promise). +> 3. **Absent** — removed from the spec. +> +> Shipping a security property in a fourth state — *parsed, unmarked, unenforced* — is prohibited. This is the **enforce-or-remove gate**. + +(大意:一個鍵恰好處於三種狀態之一——**已強制執行**:有執行時消費者讀取它並改變某個決定;**實驗性**:顯式標註並記錄為尚未強制執行,寫它是已知的空操作;**不存在**:從規範中移除。以第四種狀態——被解析、未標註、未強制執行——釋出,是被禁止的。這就是「要麼強制執行,要麼移除」門禁。) + +它給出的理由對任何鍵都成立,不只是安全鍵:「A security property that parses but does nothing is **worse than absent**: it produces a *false sense of compliance*.」(一個能解析卻什麼都不做的安全屬性,**比不存在更糟**:它製造一種*虛假的合規感*。)而它在沒有已承諾功能時的預設選擇是移除:「Removal is preferred over marking when there is no committed roadmap for the property — a smaller spec surface is the stronger default pre-MVP.」(當這個屬性沒有已承諾的路線圖時,移除優於標註——更小的規範面是 MVP 之前更穩的預設值。)`abstract` 不是安全鍵;ADR 自己的範圍說明記錄了,倉庫「cites this ADR as the enforce-or-remove policy for spec-property retirement generally」(把這份 ADR 作為規範屬性退役的通用「要麼強制執行、要麼移除」政策來引用),而退役它的工單 [#2377](https://github.com/objectstack-ai/objectstack/issues/2377) 說明了這種推廣為什麼是有意的:「Authoring them is a false affordance — especially dangerous for AI-authored metadata.」(編寫它們是一種虛假的可供性——對 AI 編寫的後設資料尤其危險。) + +### 之後:一塊告訴你該怎麼做的墓碑 + +這次移除隨 16.0.0 釋出。包的[變更日誌](https://github.com/objectstack-ai/objectstack/blob/8caa131e52717ef35fe71742d758f10b8f2b77ee/packages/spec/CHANGELOG.md#L98572-L98573)用一句話記錄了它:「object `tags`/`active`/`abstract`: `ObjectSchema.create()` now throws a located error naming the removal. None gated anything at runtime — remove them.」(`ObjectSchema.create()` 現在會丟擲一個帶位置的錯誤,點名這次移除。它們在執行時從未把關任何事——移除它們。)今天這個鍵是物件 schema 未知鍵指引裡的一塊墓碑,每個寫下它的作者看到的訊息,就是本文開頭那一條,出自 [`object.zod.ts`](https://github.com/objectstack-ai/objectstack/blob/8caa131e52717ef35fe71742d758f10b8f2b77ee/packages/spec/src/data/object.zod.ts#L1538-L1541): + +```ts +abstract: + '`abstract` was removed from the spec (ADR-0049) — object ' + + 'inheritance/abstraction is not implemented, so an abstract object still ' + + 'got a table and was instantiable. Remove the key.', +``` + +[活性臺賬的 object 一行](https://github.com/objectstack-ai/objectstack/blob/8caa131e52717ef35fe71742d758f10b8f2b77ee/packages/spec/liveness/README.md#L958)保留著記錄:「tags/active/abstract REMOVED (#2377) — tombstoned in UNKNOWN_KEY_GUIDANCE」。之前與之後是在已釋出的包上實測的:把上面那個 `Party` 物件依次交給每個版本解析。 + +| `@objectstack/spec` | `ObjectSchema.create()` | `ObjectSchema.safeParse()` | +| :--- | :--- | :--- | +| **15.1.1**(2026-07-17) | 接受;`abstract: true` 保留 | 接受;`abstract: true` 保留 | +| **16.0.0**(2026-07-21) | 拋錯,點名這次移除 | 接受;`abstract` 被靜默丟棄 | +| **17.7.0**(2026-10-06) | 拋錯,點名這次移除 | 拒絕:`unrecognized_keys`,附帶處方 | + +中間那一行值得多說一句。在 16.0.0 裡,編寫路徑關上了,解析路徑沒有——`parse()` 仍然一聲不響地丟掉這個鍵,這正是 schema 自己的註釋追溯到 Zod 預設 `.strip()` 和 [#1535](https://github.com/objectstack-ai/objectstack/issues/1535) 的那種行為:當時一個物件級的 `workflows: [...]`「vanished with no error, no warning, and a green `tsc`」(消失得無聲無息,沒有錯誤、沒有警告,`tsc` 還是綠的)。17.x 的訊息在它的最後一句裡自己說了這件事:「Until this shape was closed these were dropped silently on the PARSE path — `ObjectSchema.create()` has rejected them, but `defineStack({ objects })`, `/api/v1/meta/types/object` and the Studio form all go through `parse()`, which did not.」(在這個形狀被關閉之前,它們在解析路徑上被靜默丟棄——`ObjectSchema.create()` 已經拒絕它們,但 `defineStack({ objects })`、`/api/v1/meta/types/object` 和 Studio 表單都走 `parse()`,而 `parse()` 沒有拒絕。)關閉一個鍵不是一個決定,而是每個入口各一個決定;在一個入口完整、在另一個入口沉默的門禁,就是換了件外套的第四種狀態。 + +![一個鍵跨三個已釋出版本的時間線。15.1.1 裡這個鍵被宣告、能解析、保留取值、沒有執行時讀者,抽象物件照樣建表。ADR-0049 迫使在已強制執行、實驗性、不存在之間做選擇,沒有已承諾的繼承功能,答案就是不存在。16.0.0 裡 create() 拒絕這個鍵,parse() 仍靜默丟棄它。17.7.0 裡兩條路徑都拒絕它,附帶處方:Remove the key。](./abstract-before-after.svg) + +### 為什麼這正是重點 + +在臺賬寫下「no runtime reader」的那一刻,誠實的做法有兩種。要麼建出物件繼承,讓 `abstract: true` 有讀者、有意義;要麼移除這個鍵。知識本體的習慣——在它所來自的傳統裡,這是習慣,不是錯誤——是保留宣告,因為在那個傳統裡意義住在模型中,引擎會遵守它。可執行的習慣正相反:你能寫下的鍵的集合*就是*執行時做的事的集合,一個沒有讀者的鍵不是等待中的功能,而是等待中的、審閱者的錯誤信念。 + +姑且把這叫作**每個鍵都有讀者**。它是「先校驗,不推理」的具體形態。校驗器只有在每個詞都有消費者的詞彙表上才可能完整;一旦某個詞被解析卻無人讀取,「合法」就不再意味著「會照它說的去做」。移除 `abstract` 不是為了讓本體更小而更小。它守住的是一個承諾:通過門禁的定義,就是執行時會強制執行的定義。 + +## 審閱者得到什麼,Agent 得到什麼 + +對審閱改動的人來說,回報是:diff 就是行為。沒有需要開啟的父類,沒有需要計算的推導結論,沒有哪個鍵的效果取決於某個可能存在也可能不存在的功能。如果 `sharingModel: 'private'` 在 diff 裡,記錄就是私有的;如果一條權限不在 diff 裡,就沒人擁有它。「這一行做什麼?」由這一行的執行時讀者來回答,而一行沒有讀者的內容根本進不了 diff。 + +對寫改動的 Agent 來說,回報是同一件事的另一面。README 對「AI 編寫的後設資料為什麼行得通」的主張是:「an agent's errors become located, corrective text it can read and fix itself」(Agent 的錯誤變成了有定位的、可糾正的文本,它自己能讀、能改)。一個虛假的可供性恰恰打敗這一點:從示例裡學會一種格式的 Agent,分不清一個被強制執行的鍵和一個只被解析的鍵,因為兩者都能通過校驗、都看著權威、都出現在樣本中——這正是[「宣告 vs 強制執行」](/zh-Hant/glossary/declared-vs-enforced/)詞條描述的失敗:合法、可信、卻無效的宣告。移除這個鍵,並留下一塊以「Remove the key.」結尾的墓碑,把那種失敗變成一個 Agent 自己就能完成的一行修復,連人都不必注意到曾經有過問題。 + +## 為推理器辯護:這個本體不做什麼 + +這個論點的誠實版本必須說清可執行本體放棄了什麼,因為它放棄的是實打實的東西。 + +**它不推理。** 沒有推導結論,沒有按描述歸類,沒有跨整個模型的一致性檢查。如果推匯出來的事實正是你需要的產品——「這一萬條記錄裡,按我們對它們的瞭解,哪些是高風險交易對手的實例?」——推理器能回答,校驗器不能。 + +**它不做開放世界推理。** 封閉世界是整個設計的要點,而對於天然不完整的資料,它是錯誤的假設:一套跨組織共享的詞彙;一條即便你的系統從未見過使它為真的那個事實、卻依然為真的記錄。知識本體是為那種情形而建的,這個不是。 + +**它不做 schema 層面的繼承。** 沒有基礎物件。六個物件共有的一個欄位,要在六個物件上宣告,或者顯式建模為指向某個共享物件的查詢。這種重複是有意的——它正是讓每個物件的檔案成為完整答案的原因——但它仍然是重複,帶著重複固有的維護成本。 + +**它不替你判定兩樣東西是同一樣東西。** 一個系統裡的「客戶」和另一個系統裡的「賬戶」是不是同一個實體,是某個人必須做出的宣告,不是執行時得出的推論;[那篇企業本體文章](/zh-Hant/blog/enterprise-ontology-race-open-vs-closed/)講的正是為什麼這種對齊在任何模型下都仍然是工作。 + +還有一個限制屬於這套紀律本身,而不屬於格式:「校驗過」只在校驗器本身可信的程度上為真。ADR-0049 背後的審計是在這條規則存在*之前*發現規範裡一半屬性已死的;活性臺賬和讀取它的門禁,才是此後讓規則持續為真的東西,而那是一項常設的維護工作,不是一次到達的狀態。`abstract` 的故事是這套紀律被踐行的證據,不是它已經完成的證據。 + +## 託管,或在你自己的基礎設施上 + +README 描述了這個迴圈——Agent 在你的倉庫裡寫型別化後設資料,門禁拒絕跑不起來的東西,執行時執行通過的東西——然後說:「想把同一個迴圈託管起來、在瀏覽器裡用、什麼都不用安裝?那就是 ObjectOS——建立在 ObjectStack 之上的商業執行環境。」ObjectOS 既可託管在瀏覽器中、無需安裝(ObjectOS Cloud),也可自管部署在你自己的基礎設施上(ObjectOS Enterprise);無論哪個版本,你都可以匯出本體,拿到開源的 ObjectStack 執行時上執行。門禁在每個宿主上都一樣,它守護的本體也一樣。 + +## 自己試一次這個拒絕 + +README 的 Agent 路徑幾分鐘就能產出本文討論的那份工件,而那個拒絕正是值得親眼看一次的部分。 + +```bash +npm create objectstack@latest my-app && cd my-app +``` + +在你的編碼 Agent 裡開啟專案,描述一個業務物件——客戶、工單、工作訂單。然後要求本文開頭的那一行:給它加上 `abstract: true`,理由是它將來要做其他物件的基礎。無論哪道門先碰到它——編輯器裡的型別門,還是構建時的 `ObjectSchema.create()`——都會用上面引用的那條訊息拒絕它,而訊息以修復方法結尾。讓 Agent 採納這個修復,然後預覽結果: + +```bash +npx os dev --ui # → http://localhost:3000/_console/ +``` + +你在 diff 裡看到的會是一個沒有基類、沒有任何繼承而來的東西、沒有任何執行時不讀的鍵的物件——也就是說,一份小到可以審閱、嚴到可以執行的定義。如果 Agent 需要引導,[如何讓它對準 ObjectStack](/en/blog/how-to-point-your-agent-at-objectstack/)覆蓋了規則檔案、技能包和工具伺服器。README 裡的三個「不」不是這個格式缺少的東西,而是它強制執行的東西。 diff --git a/content/blog/executable-ontology-vs-knowledge-ontology/two-pipelines-en.svg b/content/blog/executable-ontology-vs-knowledge-ontology/two-pipelines-en.svg new file mode 100644 index 0000000..46890c0 --- /dev/null +++ b/content/blog/executable-ontology-vs-knowledge-ontology/two-pipelines-en.svg @@ -0,0 +1,106 @@ + + + + + + + Two ontologies, two pipelines + The first derives what nobody wrote. The second refuses what would not run — and derives nothing. + + + + + KNOWLEDGE ONTOLOGY — BUILT FOR INFERENCE + + + + Classes · properties · individuals · axioms + OWL 2 / RDF: subclass, disjointness, cardinality, property chains + + ↓ + + + + Reasoner + an inference engine reads the whole model and derives what follows + + ↓ + + + + Entailments + the computed hierarchy · classifications · consistency + derived at runtime — not in the diff + + + + + A second source of truth a reviewer cannot read + + + + + + EXECUTABLE BUSINESS ONTOLOGY — BUILT TO RUN + + + + Typed metadata + objects and fields · relations · actions · permissions · flows + agent and tool definitions + + ↓ + + + + Validate + strict TypeScript + Zod · os validate — a located error, or nothing + + ↓ + + + + Run + database · REST / realtime API · UI · MCP server + nothing is reasoned over at runtime + + + + + What runs is what was validated + + + + + + No inheritance + every object's file is the whole answer + + + + No axioms + constraints reject; they do not derive + + + + No reasoner + no fact exists that nobody wrote + + + Sources: the ObjectStack README, "What we mean by ontology"; the Business Ontology concepts page, "How the runtime executes it". + diff --git a/content/blog/executable-ontology-vs-knowledge-ontology/two-pipelines.svg b/content/blog/executable-ontology-vs-knowledge-ontology/two-pipelines.svg new file mode 100644 index 0000000..ec7515e --- /dev/null +++ b/content/blog/executable-ontology-vs-knowledge-ontology/two-pipelines.svg @@ -0,0 +1,106 @@ + + + + + + + 两种本体,两条流水线 + 前者推导出没人写过的东西;后者拒绝跑不起来的东西——并且什么都不推导。 + + + + + 知识本体——为推理而建 + + + + 类 · 属性 · 个体 · 公理 + OWL 2 / RDF:子类、互斥、基数、属性链 + + ↓ + + + + 推理器 + 推理引擎读取整个模型,推导出随之成立的事实 + + ↓ + + + + 推导结论 + 计算出的层级 · 分类 · 一致性 + 在运行时推导出来——不在 diff 里 + + + + + 审阅者读不到的第二个真相来源 + + + + + + 可执行业务本体——为运行而建 + + + + 类型化元数据 + 对象与字段 · 关系 · 动作 · 权限 · 流程 + Agent 与工具定义 + + ↓ + + + + 校验 + 严格 TypeScript + Zod · os validate——给出定位的错误,或者什么都没有 + + ↓ + + + + 运行 + 数据库 · REST / 实时 API · UI · MCP 服务器 + 运行时不做任何推理 + + + + + 跑的就是被校验过的东西 + + + + + + 没有继承 + 每个对象的文件就是完整答案 + + + + 没有公理 + 约束用来拒绝,不用来推导 + + + + 没有推理器 + 不存在没人写过的事实 + + + 来源:ObjectStack README「What we mean by ontology」;Business Ontology 概念页「How the runtime executes it」。 + From 555f307d68d75e218a13671e7bd4d7af91edb435 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 7 Oct 2026 03:09:01 +0000 Subject: [PATCH 2/6] fix(blog): keep the SVG root tag inside image-size's 1 KB probe window Astro's vendored image-size validates an SVG with a regex over the first 1,000 bytes only; the two abstract-before-after diagrams carried an aria-label longer than that, so the root tag never closed inside the window and `astro build` failed with NoImageMetadata. The root now holds a one-sentence aria-label (306 / 304 bytes) and the full description moved into /<desc>, referenced by aria-labelledby. Text-fit gate and the metadata probe both pass on all six SVGs. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FeA1nwBz1ohH65dvffUGKr --- .../abstract-before-after-en.svg | 4 +++- .../abstract-before-after.svg | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/content/blog/executable-ontology-vs-knowledge-ontology/abstract-before-after-en.svg b/content/blog/executable-ontology-vs-knowledge-ontology/abstract-before-after-en.svg index c6c073e..33ed9e5 100644 --- a/content/blog/executable-ontology-vs-knowledge-ontology/abstract-before-after-en.svg +++ b/content/blog/executable-ontology-vs-knowledge-ontology/abstract-before-after-en.svg @@ -1,4 +1,6 @@ -<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1200 620" role="img" aria-label="One key, three releases: abstract on the object schema, measured by parsing the same object through each published @objectstack/spec. 15.1.1, 2026-07-17, before: abstract: z.boolean().optional().default(false).describe('Is abstract base object (cannot be instantiated)'); the ledger says dead, no runtime reader, an abstract object still gets a table and is instantiable; verdict parsed, unread — create() and parse() both accept. 16.0.0, 2026-07-21: ADR-0049, enforce or remove, with no committed inheritance feature, so the key is removed; create() throws naming the removal while parse() still strips the key silently; verdict half closed, one entry point of two. 17.7.0, 2026-10-06, after: Unrecognized key(s) on this object: abstract — abstract was removed from the spec (ADR-0049), object inheritance/abstraction is not implemented, remove the key; verdict a reader for every key — create() throws and parse() rejects. Below, ADR-0049's three states: enforced, a runtime reader changes a decision; experimental, marked and documented as not enforced; absent, removed from the spec. The fourth state, parsed, unmarked, unenforced, is prohibited."> +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1200 620" role="img" aria-labelledby="t d" aria-label="One key, three releases: how the abstract key on the object schema went from parsed and unread in @objectstack/spec 15.1.1 to refused with a prescription in 17.7.0, and ADR-0049's three states"> + <title id="t">One key, three releases: abstract on the object schema + One key, three releases: abstract on the object schema, measured by parsing the same object through each published @objectstack/spec. 15.1.1, 2026-07-17, before: abstract: z.boolean().optional().default(false).describe('Is abstract base object (cannot be instantiated)'); the ledger says dead, no runtime reader, an abstract object still gets a table and is instantiable; verdict parsed, unread — create() and parse() both accept. 16.0.0, 2026-07-21: ADR-0049, enforce or remove, with no committed inheritance feature, so the key is removed; create() throws naming the removal while parse() still strips the key silently; verdict half closed, one entry point of two. 17.7.0, 2026-10-06, after: Unrecognized key(s) on this object: abstract — abstract was removed from the spec (ADR-0049), object inheritance/abstraction is not implemented, remove the key; verdict a reader for every key — create() throws and parse() rejects. Below, ADR-0049's three states: enforced, a runtime reader changes a decision; experimental, marked and documented as not enforced; absent, removed from the spec. The fourth state, parsed, unmarked, unenforced, is prohibited.