Skip to content

Commit 9821b1a

Browse files
committed
Merge remote-tracking branch 'origin/main' into worktree-fr-012-nested-prompt-expansion
2 parents 46260ba + 16ae824 commit 9821b1a

98 files changed

Lines changed: 3557 additions & 227 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: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"errors": [
3+
{
4+
"code": "ERR_BAD_ATTR_VALUE",
5+
"source": {
6+
"format": "json",
7+
"files": [
8+
"meta.assets.json"
9+
],
10+
"jsonPath": "$['metadata.root'].children[0]['object.entity'].children[1]['field.string']"
11+
}
12+
}
13+
],
14+
"warnings": []
15+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
{
2+
"metadata.root": {
3+
"package": "acme",
4+
"children": [
5+
{
6+
"object.entity": {
7+
"name": "Asset",
8+
"children": [
9+
{ "field.long": { "name": "id" } },
10+
{ "field.string": { "name": "recordedAt", "@dbColumnType": "timestamp_with_tz" } },
11+
{ "identity.primary": { "@fields": "id" } }
12+
]
13+
}
14+
}
15+
]
16+
}
17+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
["metaobjects-core-types", "metaobjects-db"]
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
{
2+
"metadata.root": {
3+
"package": "acme",
4+
"children": [
5+
{
6+
"object.entity": {
7+
"name": "Reading",
8+
"children": [
9+
{
10+
"field.uuid": {
11+
"name": "id"
12+
}
13+
},
14+
{
15+
"field.string": {
16+
"name": "label"
17+
}
18+
},
19+
{
20+
"identity.primary": {
21+
"@fields": [
22+
"id"
23+
]
24+
}
25+
}
26+
]
27+
}
28+
}
29+
]
30+
}
31+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
{
2+
"metadata.root": {
3+
"package": "acme",
4+
"children": [
5+
{
6+
"object.entity": {
7+
"name": "Reading",
8+
"children": [
9+
{ "field.uuid": { "name": "id" } },
10+
{ "field.string": { "name": "label" } },
11+
{ "identity.primary": { "@fields": "id" } }
12+
]
13+
}
14+
}
15+
]
16+
}
17+
}

fixtures/persistence-conformance/canonical/meta.fitness.json

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,18 @@
3737
{ "identity.primary": { "@fields": "id", "@generation": "increment" } }
3838
]
3939
}},
40+
{ "object.entity": {
41+
"name": "Asset",
42+
"children": [
43+
{ "source.rdb": { "@table": "assets" } },
44+
{ "field.uuid": { "name": "id" } },
45+
{ "field.uuid": { "name": "ownerId", "@required": true } },
46+
{ "field.string": { "name": "externalId", "@dbColumnType": "uuid" } },
47+
{ "field.string": { "name": "payload", "@dbColumnType": "jsonb" } },
48+
{ "field.timestamp": { "name": "recordedAt", "@required": true, "@dbColumnType": "timestamp_with_tz" } },
49+
{ "identity.primary": { "@fields": "id", "@generation": "uuid" } }
50+
]
51+
}},
4052
{ "object.entity": {
4153
"name": "ProgramView",
4254
"children": [
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
name: asset-native-column-types
2+
description: |
3+
R6 Plan 2a/2b — the Asset entity exercises native physical column types end to
4+
end against real Postgres. After the canonical schema is applied, the live
5+
catalog (information_schema.columns) must report:
6+
7+
id uuid (field.uuid PK, server-default gen_random_uuid())
8+
ownerId uuid (field.uuid, non-key)
9+
externalId uuid (field.string + @dbColumnType: uuid)
10+
payload jsonb (field.string + @dbColumnType: jsonb, genuinely-open JSON)
11+
recordedAt timestamp with time zone (field.timestamp + @dbColumnType: timestamp_with_tz)
12+
13+
Asserting against information_schema (not the DDL string) makes this assertion
14+
port-independent: every port's bootstrap DDL — whatever its exact spelling
15+
(UUID vs uuid, TIMESTAMPTZ vs TIMESTAMP WITH TIME ZONE) — must converge to the
16+
same live catalog type. This mirrors the R6 REAL/DOUBLE PRECISION column-type
17+
assertion pattern.
18+
19+
# Bootstrap the full canonical schema, then introspect the assets table.
20+
target-metadata: ../canonical/
21+
22+
expect:
23+
blocked: []
24+
apply-up-then-query:
25+
sql: |
26+
SELECT column_name, data_type
27+
FROM information_schema.columns
28+
WHERE table_schema = 'public' AND table_name = 'assets'
29+
ORDER BY column_name
30+
rows:
31+
- { column_name: "externalId", data_type: "uuid" }
32+
- { column_name: "id", data_type: "uuid" }
33+
- { column_name: "ownerId", data_type: "uuid" }
34+
- { column_name: "payload", data_type: "jsonb" }
35+
- { column_name: "recordedAt", data_type: "timestamp with time zone" }

fixtures/persistence-conformance/migrations/bootstrap-canonical-from-empty.yaml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,17 @@ expect:
2222
# REAL→DOUBLE regression.
2323
- '"tempC" REAL'
2424
- '"massKg" DOUBLE PRECISION'
25+
- 'CREATE TABLE "assets"'
26+
# R6 Plan 2a/2b native physical column types:
27+
# field.uuid → UUID (id PK + ownerId)
28+
# field.string @dbColumnType:uuid → UUID (externalId)
29+
# field.string @dbColumnType:jsonb → JSONB (payload)
30+
# PK uuid gets the server-side gen_random_uuid() default.
31+
- '"id" UUID'
32+
- 'DEFAULT gen_random_uuid()'
33+
- '"ownerId" UUID'
34+
- '"externalId" UUID'
35+
- '"payload" JSONB'
2536
- 'CREATE UNIQUE INDEX "byTitle" ON "programs" ("title");'
2637
- 'ALTER TABLE "weeks" ADD CONSTRAINT "weeks_programId_fk"'
2738
# Match both TS (`CREATE VIEW`) and C# (`CREATE OR REPLACE VIEW`, idempotent)
@@ -36,6 +47,7 @@ expect:
3647
WHERE table_schema = 'public' AND table_type = 'BASE TABLE'
3748
ORDER BY table_name
3849
rows:
50+
- { table_name: "assets" }
3951
- { table_name: "measurements" }
4052
- { table_name: "programs" }
4153
- { table_name: "weeks" }
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
name: asset-uuid-roundtrip
2+
description: |
3+
R6 Plan 2a/2b — uuid values round-trip lowercase-canonically and the
4+
server-side gen_random_uuid() default populates an unspecified PK.
5+
6+
Seed three assets:
7+
- two with EXPLICIT ids (one upper-case, one lower-case) → both must come
8+
back lowercase-canonical (the normalization contract for UUID).
9+
- one with NO id supplied → Postgres fills it via gen_random_uuid(); we
10+
assert it exists (count = 3) and that its generated id is a valid,
11+
non-null uuid by fetching it back through a non-uuid lookup column.
12+
13+
payload is a @dbColumnType:jsonb open-JSON column; jsonb re-serializes with
14+
sorted keys per the normalization contract.
15+
16+
recordedAt is a @dbColumnType:timestamp_with_tz column. Its native physical
17+
type (`timestamp with time zone`) is asserted port-independently in
18+
migrations/asset-native-column-types.yaml. Here it is seeded at exact-UTC
19+
midnight-aligned instants so every port's row normalizer yields the same
20+
value string; per the documented TIMESTAMPTZ normalization hazard
21+
(normalization.md) the uuid round-trip + generation are the load-bearing
22+
assertions of this scenario.
23+
seed-data: |
24+
INSERT INTO "assets" ("id", "ownerId", "externalId", "payload", "recordedAt") VALUES
25+
('11111111-1111-4111-8111-111111111111',
26+
'AAAAAAAA-AAAA-4AAA-8AAA-AAAAAAAAAAAA',
27+
'22222222-2222-4222-8222-222222222222',
28+
'{"b": 2, "a": 1}',
29+
'2026-05-30T14:30:00Z'),
30+
('33333333-3333-4333-8333-333333333333',
31+
'44444444-4444-4444-8444-444444444444',
32+
'55555555-5555-4555-8555-555555555555',
33+
'{"k": "v"}',
34+
'2026-05-30T15:00:00Z');
35+
INSERT INTO "assets" ("ownerId", "externalId", "payload", "recordedAt") VALUES
36+
('66666666-6666-4666-8666-666666666666',
37+
'77777777-7777-4777-8777-777777777777',
38+
'{"generated": true}',
39+
'2026-05-30T16:00:00Z');
40+
41+
queries:
42+
- name: get-asset-by-explicit-uppercase-id-roundtrips-lowercase
43+
op: get
44+
entity: Asset
45+
by: { id: "11111111-1111-4111-8111-111111111111" }
46+
expect:
47+
id: "11111111-1111-4111-8111-111111111111"
48+
ownerId: "aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa"
49+
externalId: "22222222-2222-4222-8222-222222222222"
50+
payload: { a: 1, b: 2 }
51+
recordedAt: "2026-05-30T14:30:00"
52+
53+
- name: list-assets-with-explicit-ids-by-owner-eq
54+
op: list
55+
entity: Asset
56+
filter: { ownerId: { eq: "44444444-4444-4444-8444-444444444444" } }
57+
sort: [{ field: id, dir: asc }]
58+
expect:
59+
- id: "33333333-3333-4333-8333-333333333333"
60+
ownerId: "44444444-4444-4444-8444-444444444444"
61+
externalId: "55555555-5555-4555-8555-555555555555"
62+
payload: { k: "v" }
63+
recordedAt: "2026-05-30T15:00:00"
64+
65+
# The third row is inserted WITHOUT an id: the INSERT only succeeds because the
66+
# PK carries a server-side gen_random_uuid() DEFAULT (a NOT NULL PK with no
67+
# default would reject the row). count = 3 is therefore the generation proof.
68+
- name: count-all-three-including-server-generated-pk
69+
op: count
70+
entity: Asset
71+
expect: 3
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{
2+
"metadata.root": {
3+
"package": "acme",
4+
"children": [
5+
{
6+
"object.entity": {
7+
"name": "Product",
8+
"children": [
9+
{
10+
"field.string": {
11+
"name": "sku"
12+
}
13+
}
14+
]
15+
}
16+
}
17+
]
18+
}
19+
}

0 commit comments

Comments
 (0)