Skip to content

Commit 23196df

Browse files
sam-lippertclaude
andcommitted
fix: RMAP correctly maps value types as inline columns, not separate tables
- createProperty defaults valueType to 'string' for value-type nouns without explicit valueType (fixes value nouns becoming FK references) - ensureTableExists skips value-type nouns (only entity types get tables) - Pre-parse constraint text in createWithHook to set kind before insert - Add 'schema' to VALID_FORMATS in generate handler - Cache invalidation before applySchema generation Tables reduced from 21 (with value type tables) to 10 (entity types only). createEntity now inserts one row into the 3NF table. The RMAP loop works: Readings -> OpenAPI -> SQLite DDL -> DO tables -> entity CRUD. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent f87e8f7 commit 23196df

4 files changed

Lines changed: 21 additions & 1 deletion

File tree

src/api/generate.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { json, error } from 'itty-router'
22
import type { Env } from '../types'
33

4-
const VALID_FORMATS = ['openapi', 'sqlite', 'xstate', 'ilayer', 'readings', 'constraint-ir', 'mdxui'] as const
4+
const VALID_FORMATS = ['openapi', 'sqlite', 'xstate', 'ilayer', 'readings', 'constraint-ir', 'mdxui', 'schema'] as const
55

66
export async function handleGenerate(request: Request, env: Env): Promise<Response> {
77
const body = await request.json() as Record<string, any>

src/do.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -964,6 +964,7 @@ export class GraphDLDB extends DurableObject {
964964
*/
965965
async applySchema(domainId: string): Promise<{ tableMap: Record<string, string>; fieldMap: Record<string, Record<string, string>> }> {
966966
const model = this.getModel(domainId)
967+
model.invalidate() // Clear cache to ensure fresh data
967968
const openapi = await (await import('./generate/openapi')).generateOpenAPI(model)
968969
const { ddl, tableMap, fieldMap } = (await import('./generate/sqlite')).generateSQLite(openapi)
969970

src/generate/schema-builder.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,11 @@ export function createProperty({
8383
superType = superType?.superType
8484
}
8585

86+
// Value type nouns default to string when valueType isn't explicitly set
87+
if (!valueType && object.objectType === 'value') {
88+
valueType = 'string'
89+
}
90+
8691
if (valueType) {
8792
// ---- Value type → primitive property ----
8893
property.type = valueType
@@ -162,6 +167,9 @@ export function ensureTableExists({
162167
nouns: NounDef[]
163168
jsonExamples: Record<string, JSONSchemaType>
164169
}): void {
170+
// Value types don't get their own tables — they become inline columns
171+
if (subject.objectType === 'value') return
172+
165173
const title = subject.name || ''
166174
const key = nameToKey(title)
167175
if (tables[key]) return

src/hooks/index.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,17 @@ export async function createWithHook(
5050
data: Record<string, any>,
5151
context: HookContext,
5252
): Promise<{ doc: Record<string, any>; hookResult: HookResult }> {
53+
// Pre-process: constraints with text need kind before insert (NOT NULL)
54+
if (collection === 'constraints' && data.text && !data.kind) {
55+
const { parseConstraintText } = await import('./parse-constraint')
56+
const parsed = parseConstraintText(data.text)
57+
if (parsed && parsed.length > 0) {
58+
data.kind = parsed[0].kind
59+
data.modality = data.modality || parsed[0].modality
60+
} else {
61+
data.kind = 'UC' // default
62+
}
63+
}
5364
const doc = await db.createInCollection(collection, data)
5465
const hook = COLLECTION_HOOKS[collection]
5566
if (hook) {

0 commit comments

Comments
 (0)