Skip to content

Commit d6e0eae

Browse files
committed
Merge origin/main into claude/issue-21830-record-change-credential
Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018zT8d8NpiQ1ExhuNd5TxY6
2 parents e899b80 + 0fe0a59 commit d6e0eae

43 files changed

Lines changed: 2783 additions & 365 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: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
---
2+
"@objectstack/cloud-connection": minor
3+
---
4+
5+
An install-local reseed over sample rows that are all still in place answers success, with the loader's `skipped` count, instead of a refusal naming a false cause (#21776).
6+
7+
Clause-②: yes (widening)
8+
9+
- **Intact baseline.** `POST /api/v1/marketplace/install-local/:manifestId/reseed-sample-data`, run while every seed record the package declares is already present, answers `200 { success: true, data: { manifestId, inserted: 0, updated: 0, skipped: N, errors: 0, withSampleData: true } }`. Before, it answered `422 RESEED_NO_ROWS`, "Reseed wrote no rows. The package declares no seedable records for this runtime.", over a package that declares them. The reseed is idempotent, so a run that finds every row in place has reached its goal. The install's record of sample data is set the same way as when rows land.
10+
- **`skipped` on every success.** A successful reseed now answers all four of the loader's counts: `inserted`, `updated`, `skipped` and `errors`. Before, `skipped` was not in the response.
11+
- **Unchanged refusals.** `422 RESEED_NO_ROWS` still answers a run that wrote nothing because records failed, with the error count and the first error, and its `details` are still `{ inserted, updated, errors }`. It also still answers, with the same text, a run in which the loader had no record to process for this runtime: for example, every dataset is scoped to another environment (`Seed.env`). That text now states a true cause. A package with no seed dataset at all still answers `400 RESEED_SKIPPED` (`no-datasets`).
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
---
2+
"@objectstack/metadata-protocol": minor
3+
---
4+
5+
fix(metadata-protocol)!: an item's lock is the strictest among the installed packages that ship its name, at the write doors and on both reads (#21803)
6+
7+
Clause-②: no (narrowing)
8+
9+
ADR-0048 lets two installed code packages ship one `(type, name)`. The ADR-0010 `_lock` gate looked the packaged artifact up with no package, so it bound the artifact of whichever package was registered first, while `getMetaItem`, `getMetaItemLayered`, the metadata list and the `getMetaDiagnostics` locked count looked it up with the request's package. One item had two lock answers, and the door's answer depended on registration order.
10+
11+
Now every caller takes the artifact layer from one selection: the artifact of every installed package that ships the name, a disabled package included (it is still installed). The lock is resolved once per shipping package, that package's artifact over the stored rows in scope exactly as before, and the item's lock is the strictest of those answers. With one package shipping the name, or none, nothing changes.
12+
13+
**What moves for consumers.** The door now refuses where it used to depend on registration order:
14+
15+
- one package ships a lock and another ships none: a save or delete, with or without `?package=`, is refused `403 ITEM_LOCKED` under both registration orders, where it was admitted when the unlocked package was registered first;
16+
- one package ships no lock, the stored row in scope declares a lock, and another package ships a lock refusing the other verb (for example `no-overlay` on the row and `no-delete` on the artifact): both a save and a delete are refused, where each was admitted under the registration order that bound the other answer;
17+
- a disabled package's packaged lock binds under both registration orders, where it bound only when that package was registered first.
18+
19+
No write the door refused before is admitted now: the artifact the door bound before is always one of the shipping packages. Both reads report the same lock as the door in `lock`, `editable` and `deletable`, under both orders: a read naming a package that ships no lock now reports `editable: false` when another installed package ships one. The refusal and the reads carry the prose of the binding package (the request's own package first, then the others by package id), and no prose when no single package's answer is the strictest. Content stays prefer-local: a read naming a package is still served that package's own artifact, under that package's provenance.
20+
21+
A served body (`getMetaItem`'s `item`, `getMetaItemLayered`'s `effective`, the list items) now carries exactly the lock family of the resolution's answer, and no `_lock` key when nothing binds. Before, a body served from a stored row outside the lock's scope kept that row's `_lock` while the envelope reported `none` (an organization holding only another package's row, with the request's package served its env-wide row), and an explicit `_lock: 'none'` stayed on a body. No key, export, status or error code changes.
22+
23+
<!-- adr-0087: not-required (no-migration-prescription) the write doors refuse, by the strictest lock among the installed packages that ship an item's name, writes they used to admit when an unlocked package was registered first. No authorable key, spelling, export or stored shape moves: every artifact and stored row keeps parsing, nothing is read or rewritten at rest, and which package an administrator meant to lock is not something a ledger entry can rewrite. The other categories are closed on facts: the package publishes (not unpublished); no ADR-0087 id covers the lock gate (not already-registered); and the change is a door verdict, not a declaration (not runtime-interface-only or type-surface-only). -->
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
---
2+
"@objectstack/cloud-connection": patch
3+
---
4+
5+
`GET /api/v1/marketplace/install-local` now marks an installed package that this runtime refused to load. Before, after a restart whose rehydrate refused a package built for another protocol major, the listing served it like any loaded package, and the console's Installed Apps showed it as installed.
6+
7+
Clause-②: no
8+
9+
- **What was wrong.** On a restart, a ledger entry whose `engines.protocol` range excludes this runtime is not loaded, and the boot logs `OS_PROTOCOL_INCOMPATIBLE` at `error`. The entry stays in the ledger, so `DELETE` and a compatible re-install still act on it. The listing served it with the same fields as a loaded package. Each request also tried to read its seed rows from objects that were never registered, and logged a `warn` saying it could not.
10+
- **What it does now.** That entry is listed with `"notLoaded": { "code": "OS_PROTOCOL_INCOMPATIBLE", "requiredRange": "^16" }` (the range the package declares) in place of `withSampleData`. No seed row is read for it, so the per-request `warn` is gone. `notLoaded` has exactly these two members, and every authenticated caller sees it.
11+
- **Unchanged.** A loaded package's entry is exactly as before, with no `notLoaded` key. `DELETE /api/v1/marketplace/install-local/:manifestId` removes a marked entry as before, and once a compatible version is installed over it, the entry is listed as loaded.
12+
- **Where the marker comes from.** The rehydrate records each entry it refuses, and the listing reads that record. The listing does not run the protocol check again.
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
---
2+
'@objectstack/metadata': patch
3+
---
4+
5+
`DatabaseLoader` now persists a `register` whose only change is the order of an object's `fields` (#21828). Before, the loader's checksum sorted every map, so a field reorder hashed equal to the stored row and was never written. The running process still saw the new order, but the persisted `sys_metadata` row kept the old one.
6+
7+
Clause-②: no
8+
9+
- **One hash vocabulary in `sys_metadata.checksum`.** The loader now stamps the hash `SysMetadataRepository` stamps on the same column: `hashSpec(body, type)` from `@objectstack/metadata-core`, written as `sha256:` + 64 hex. It is hashed as the item's metadata type, so a reorder of an object's `fields` is a change and every other map is still key-order independent. Its history rows carry the same value as the row they record. Before, the loader wrote bare hex from `calculateChecksum`. That function is unchanged and still exported, but nothing writes the column with it.
10+
- **Rows stamped before this release.** Whether a `register` is a no-op is now decided by re-hashing the stored body, not by comparing the stored checksum. A row with an unchanged body is not rewritten: no version bump, no history row, and it keeps its old checksum until its content next changes. The first real change rewrites it with the new stamp. A reorder into sorted key order is written too, even against a stored checksum that sorted every map.
11+
- **ETags.** `load()` and `stat()` report the row's checksum as `etag`, so a row the loader writes from this release on reports a `sha256:` value.

‎README.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,7 @@ graph held inside a vendor's system.
201201
> The ontology is the software. Your objects, relations, actions, permissions,
202202
> flows, and agent and tool definitions are your business ontology — and the
203203
> definition layer of the AI era should be an open protocol you own.
204-
> [Read why](https://www.objectos.ai/en/blog/ai-ontology-open-protocol/).
204+
> [Read why](https://objectstack.ai/blog/the-ontology-is-the-software).
205205
206206
## Ship it
207207

‎apps/docs/app/[lang]/page.tsx‎

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -455,14 +455,14 @@ export default function HomePage() {
455455
The ontology is the software. Your objects, relations, actions, permissions, and
456456
flows are your business ontology — views, dashboards, apps, and translations are
457457
projections of it, and the definition layer of the AI era should be an open
458-
protocol you own.{' '}
459-
<a
460-
href="https://www.objectos.ai/en/blog/ai-ontology-open-protocol/"
458+
protocol you own. Read the long form:{' '}
459+
<Link
460+
href="/blog/the-ontology-is-the-software"
461461
className="inline-flex items-center gap-1 font-medium text-fd-foreground underline underline-offset-4 transition-colors hover:text-fd-primary"
462462
>
463-
Read why
463+
The Ontology Is the Software
464464
<ArrowRight className="size-3.5" />
465-
</a>
465+
</Link>
466466
</p>
467467
<p className="mt-3 text-sm text-fd-muted-foreground">
468468
ObjectStack is build &amp; ask with Claude Code. Rather build &amp; ask online —

‎apps/docs/lib/layout.shared.tsx‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@ export function baseOptions(): BaseLayoutProps {
2323
</div>
2424
),
2525
},
26+
// Every layout spreads baseOptions() — DocsLayout (docs sidebar), and HomeLayout on
27+
// the home page and on /blog — so this one entry is the blog's link on all of them.
28+
links: [{ text: 'Blog', url: '/blog', active: 'nested-url' }],
2629
githubUrl: `https://github.com/${gitConfig.user}/${gitConfig.repo}`,
2730
};
2831
}

‎content/blog/metadata-driven-architecture.mdx‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ date: 2024-01-20
66
tags: [architecture, metadata, enterprise, analysis]
77
---
88

9+
*Editor's note, October 2026: this post predates the project's current positioning, the ontology is the software, and uses the metadata-driven vocabulary of its time. Read it as the lineage; the current statement is [The Ontology Is the Software](/blog/the-ontology-is-the-software).*
910

1011
## Introduction: The Metadata Revolution
1112

‎content/blog/protocol-first-development.mdx‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ date: 2024-01-22
66
tags: [protocol, architecture, open-source, philosophy, technical]
77
---
88

9+
*Editor's note, October 2026: this post predates the project's current positioning, the ontology is the software, and argues the protocol-first half of it. Read it as the lineage; the current statement is [The Ontology Is the Software](/blog/the-ontology-is-the-software).*
910

1011
## Introduction: The Platform Trap
1112

0 commit comments

Comments
 (0)