Skip to content

Commit a031884

Browse files
committed
2 parents 6458cf1 + 587ea2e commit a031884

2 files changed

Lines changed: 59 additions & 15 deletions

File tree

README.md

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -298,21 +298,6 @@ For the browser, the typed client SDK and React hooks (`useQuery` / `useMutation
298298

299299
</details>
300300

301-
## Codebase Metrics
302-
303-
| Metric | Value |
304-
| :--- | :--- |
305-
| Source packages | 72 |
306-
| Apps | 2 (account, docs) |
307-
| Framework adapters | 7 (Express, Fastify, Hono, NestJS, Next.js, Nuxt, SvelteKit) |
308-
| Database drivers | 3 (Memory, SQL, MongoDB) |
309-
| Zod schema files | 200 |
310-
| Exported schemas | 1,600+ |
311-
| `.describe()` annotations | 8,750+ |
312-
| Service contracts | 27 |
313-
| Test files | 676 |
314-
| Tests passing | 6,507 |
315-
316301
## Architecture
317302

318303
ObjectStack uses a **microkernel architecture** where the kernel provides only the essential infrastructure (DI, EventBus, lifecycle), and all capabilities are delivered as plugins. The three protocol layers sit above the kernel:
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.
2+
//
3+
// GOLDEN REGRESSION — ADR-0070 package-first authoring, exercised end-to-end
4+
// through the real booted stack (not a mocked unit). The kernel must REJECT a
5+
// runtime-only create that targets a read-only code/installed package with
6+
// `writable_package_required` (D1/D2) instead of silently coercing it to a
7+
// package-less orphan (the pre-ADR #2252 behavior). This is the contract the
8+
// Studio + AI surfaces rely on as the backstop.
9+
//
10+
// Reverting D1 (the writable_package_required throw in saveMetaItem) turns this
11+
// red — that is the point of the gate.
12+
13+
import { describe, it, expect, beforeAll, afterAll } from 'vitest';
14+
import crmStack from '@objectstack/example-crm';
15+
import { bootStack, type VerifyStack } from '@objectstack/verify';
16+
17+
describe('dogfood: package-first authoring rejects runtime creates into read-only packages (ADR-0070 D1/D2)', () => {
18+
let stack: VerifyStack;
19+
20+
beforeAll(async () => {
21+
stack = await bootStack(crmStack);
22+
});
23+
afterAll(async () => {
24+
await stack?.stop?.();
25+
});
26+
27+
it('saveMetaItem(runtime-only) into a loaded code package throws writable_package_required', async () => {
28+
// The objectql engine records every booted code package in its manifest map;
29+
// any one of them is a read-only authoring target (isWritablePackage=false).
30+
const ql = await stack.kernel.getServiceAsync<any>('objectql');
31+
const manifests = (ql?.manifests ?? ql?.engine?.manifests) as Map<string, unknown> | undefined;
32+
const codePkgId = manifests && typeof manifests.keys === 'function' ? [...manifests.keys()][0] : undefined;
33+
expect(codePkgId, 'expected at least one loaded code package in the booted stack').toBeTruthy();
34+
35+
const protocol = await stack.kernel.getServiceAsync<any>('protocol');
36+
await expect(
37+
protocol.saveMetaItem({
38+
type: 'object',
39+
name: 'dogfood_pkgfirst_probe',
40+
item: { name: 'dogfood_pkgfirst_probe', label: 'Probe', fields: { name: { type: 'text', label: 'Name' } } },
41+
packageId: codePkgId,
42+
mode: 'draft',
43+
}),
44+
).rejects.toMatchObject({ code: 'writable_package_required' });
45+
});
46+
47+
it('the same create into a fresh writable base id is NOT rejected (control)', async () => {
48+
const protocol = await stack.kernel.getServiceAsync<any>('protocol');
49+
// A bare, unregistered project-base id is writable — the write must succeed.
50+
const res = await protocol.saveMetaItem({
51+
type: 'object',
52+
name: 'dogfood_pkgfirst_ok',
53+
item: { name: 'dogfood_pkgfirst_ok', label: 'OK', fields: { name: { type: 'text', label: 'Name' } } },
54+
packageId: 'app.dogfood_probe_base',
55+
mode: 'draft',
56+
});
57+
expect(res?.success ?? true).toBeTruthy();
58+
});
59+
});

0 commit comments

Comments
 (0)