metaobjects 0.9.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- metaobjects/__init__.py +75 -0
- metaobjects/agent_context/__init__.py +55 -0
- metaobjects/agent_context/_content/README.md +14 -0
- metaobjects/agent_context/_content/servers/csharp.meta.json +5 -0
- metaobjects/agent_context/_content/servers/java.meta.json +5 -0
- metaobjects/agent_context/_content/servers/kotlin.meta.json +5 -0
- metaobjects/agent_context/_content/servers/python.meta.json +5 -0
- metaobjects/agent_context/_content/servers/typescript.meta.json +5 -0
- metaobjects/agent_context/_content/skills/metaobjects-authoring/SKILL.md +301 -0
- metaobjects/agent_context/_content/skills/metaobjects-codegen/SKILL.md +99 -0
- metaobjects/agent_context/_content/skills/metaobjects-codegen/references/csharp.md +87 -0
- metaobjects/agent_context/_content/skills/metaobjects-codegen/references/java.md +94 -0
- metaobjects/agent_context/_content/skills/metaobjects-codegen/references/kotlin.md +110 -0
- metaobjects/agent_context/_content/skills/metaobjects-codegen/references/typescript.md +135 -0
- metaobjects/agent_context/_content/skills/metaobjects-prompts/SKILL.md +148 -0
- metaobjects/agent_context/_content/skills/metaobjects-prompts/references/csharp.md +110 -0
- metaobjects/agent_context/_content/skills/metaobjects-prompts/references/java.md +108 -0
- metaobjects/agent_context/_content/skills/metaobjects-prompts/references/kotlin.md +130 -0
- metaobjects/agent_context/_content/skills/metaobjects-prompts/references/python.md +116 -0
- metaobjects/agent_context/_content/skills/metaobjects-prompts/references/typescript.md +150 -0
- metaobjects/agent_context/_content/skills/metaobjects-runtime-ui/SKILL.md +130 -0
- metaobjects/agent_context/_content/skills/metaobjects-runtime-ui/references/java.md +96 -0
- metaobjects/agent_context/_content/skills/metaobjects-runtime-ui/references/kotlin.md +99 -0
- metaobjects/agent_context/_content/skills/metaobjects-runtime-ui/references/react.md +86 -0
- metaobjects/agent_context/_content/skills/metaobjects-runtime-ui/references/tanstack.md +119 -0
- metaobjects/agent_context/_content/skills/metaobjects-runtime-ui/references/typescript.md +92 -0
- metaobjects/agent_context/_content/skills/metaobjects-verify/SKILL.md +107 -0
- metaobjects/agent_context/_content/skills/metaobjects-verify/references/migration.md +72 -0
- metaobjects/agent_context/_content/templates/always-on.md.mustache +27 -0
- metaobjects/agent_context/assemble.py +133 -0
- metaobjects/agent_context/content_root.py +54 -0
- metaobjects/agent_context/scaffold.py +191 -0
- metaobjects/agent_context/types.py +44 -0
- metaobjects/attr_class_map.py +23 -0
- metaobjects/cli.py +696 -0
- metaobjects/codegen/__init__.py +0 -0
- metaobjects/codegen/config.py +11 -0
- metaobjects/codegen/constants.py +13 -0
- metaobjects/codegen/extract_delegate_emitter.py +384 -0
- metaobjects/codegen/extract_schema_emitter.py +139 -0
- metaobjects/codegen/format.py +31 -0
- metaobjects/codegen/fr010_field_mapping.py +220 -0
- metaobjects/codegen/generator.py +62 -0
- metaobjects/codegen/generator_registry.py +163 -0
- metaobjects/codegen/generators/__init__.py +0 -0
- metaobjects/codegen/generators/entity_model.py +263 -0
- metaobjects/codegen/generators/extractor_generator.py +317 -0
- metaobjects/codegen/generators/filter_allowlist_generator.py +309 -0
- metaobjects/codegen/generators/m2m_codegen.py +192 -0
- metaobjects/codegen/generators/output_parser_generator.py +272 -0
- metaobjects/codegen/generators/output_prompt_generator.py +192 -0
- metaobjects/codegen/generators/payload_vo_generator.py +672 -0
- metaobjects/codegen/generators/render_helper_generator.py +451 -0
- metaobjects/codegen/generators/router_generator.py +635 -0
- metaobjects/codegen/generators/template_generator.py +70 -0
- metaobjects/codegen/generators/tph_plan.py +120 -0
- metaobjects/codegen/generators/trace_helper_generator.py +336 -0
- metaobjects/codegen/instance_artifacts.py +15 -0
- metaobjects/codegen/output_format_spec_emitter.py +79 -0
- metaobjects/codegen/overwrite_policy.py +27 -0
- metaobjects/codegen/runner.py +110 -0
- metaobjects/codegen/runtime/__init__.py +6 -0
- metaobjects/codegen/runtime/filter_parser.py +193 -0
- metaobjects/codegen/type_map.py +84 -0
- metaobjects/core_types.py +809 -0
- metaobjects/datatype.py +19 -0
- metaobjects/documentation/__init__.py +28 -0
- metaobjects/documentation/doc_constants.py +20 -0
- metaobjects/documentation/doc_provider.py +20 -0
- metaobjects/documentation/doc_schema.py +24 -0
- metaobjects/errors.py +124 -0
- metaobjects/loader/__init__.py +0 -0
- metaobjects/loader/merge.py +287 -0
- metaobjects/loader/meta_data_loader.py +245 -0
- metaobjects/loader/sources/__init__.py +24 -0
- metaobjects/loader/sources/directory_source.py +50 -0
- metaobjects/loader/sources/file_source.py +41 -0
- metaobjects/loader/sources/meta_data_source.py +67 -0
- metaobjects/loader/sources/uri_source.py +56 -0
- metaobjects/loader/validate_discriminator.py +181 -0
- metaobjects/loader/validate_field_readonly.py +146 -0
- metaobjects/loader/validate_source_parameter_ref.py +159 -0
- metaobjects/loader/validate_source_physical_names.py +140 -0
- metaobjects/loader/validation_passes.py +1513 -0
- metaobjects/meta/__init__.py +1 -0
- metaobjects/meta/core/__init__.py +0 -0
- metaobjects/meta/core/attr/__init__.py +0 -0
- metaobjects/meta/core/attr/attr_constants.py +31 -0
- metaobjects/meta/core/attr/meta_attr.py +136 -0
- metaobjects/meta/core/field/__init__.py +0 -0
- metaobjects/meta/core/field/field_constants.py +105 -0
- metaobjects/meta/core/field/meta_field.py +76 -0
- metaobjects/meta/core/identity/__init__.py +0 -0
- metaobjects/meta/core/identity/identity_constants.py +19 -0
- metaobjects/meta/core/identity/meta_identity.py +8 -0
- metaobjects/meta/core/object/__init__.py +0 -0
- metaobjects/meta/core/object/meta_object.py +65 -0
- metaobjects/meta/core/object/meta_object_aware.py +43 -0
- metaobjects/meta/core/object/object_class_registry.py +56 -0
- metaobjects/meta/core/object/object_constants.py +13 -0
- metaobjects/meta/core/object/object_extract.py +400 -0
- metaobjects/meta/core/object/value_object.py +70 -0
- metaobjects/meta/core/relationship/__init__.py +0 -0
- metaobjects/meta/core/relationship/derive_m2m_fields.py +180 -0
- metaobjects/meta/core/relationship/meta_relationship.py +54 -0
- metaobjects/meta/core/relationship/relationship_constants.py +51 -0
- metaobjects/meta/core/validator/__init__.py +0 -0
- metaobjects/meta/core/validator/validator_constants.py +18 -0
- metaobjects/meta/meta_data.py +206 -0
- metaobjects/meta/meta_root.py +8 -0
- metaobjects/meta/persistence/__init__.py +0 -0
- metaobjects/meta/persistence/db/__init__.py +1 -0
- metaobjects/meta/persistence/db/db_constants.py +41 -0
- metaobjects/meta/persistence/db/db_provider.py +60 -0
- metaobjects/meta/persistence/origin/__init__.py +0 -0
- metaobjects/meta/persistence/origin/meta_origin.py +8 -0
- metaobjects/meta/persistence/origin/origin_constants.py +20 -0
- metaobjects/meta/persistence/source/__init__.py +0 -0
- metaobjects/meta/persistence/source/meta_source.py +137 -0
- metaobjects/meta/persistence/source/source_constants.py +115 -0
- metaobjects/meta/presentation/__init__.py +0 -0
- metaobjects/meta/presentation/layout/__init__.py +0 -0
- metaobjects/meta/presentation/layout/layout_constants.py +13 -0
- metaobjects/meta/presentation/layout/meta_layout.py +8 -0
- metaobjects/meta/presentation/view/__init__.py +0 -0
- metaobjects/meta/presentation/view/meta_view.py +8 -0
- metaobjects/meta/presentation/view/view_constants.py +22 -0
- metaobjects/meta/template/__init__.py +0 -0
- metaobjects/meta/template/meta_template.py +46 -0
- metaobjects/meta/template/template_constants.py +112 -0
- metaobjects/meta/template/template_provider.py +43 -0
- metaobjects/parser.py +380 -0
- metaobjects/parser_yaml.py +82 -0
- metaobjects/provider.py +111 -0
- metaobjects/py.typed +0 -0
- metaobjects/registry.py +210 -0
- metaobjects/registry_manifest.py +223 -0
- metaobjects/render/__init__.py +74 -0
- metaobjects/render/email_document.py +14 -0
- metaobjects/render/escapers.py +109 -0
- metaobjects/render/extract/__init__.py +59 -0
- metaobjects/render/extract/coerce.py +279 -0
- metaobjects/render/extract/extract.py +211 -0
- metaobjects/render/extract/extract_map.py +61 -0
- metaobjects/render/extract/json_forgiving_reader.py +203 -0
- metaobjects/render/extract/locate.py +65 -0
- metaobjects/render/extract/normalize.py +96 -0
- metaobjects/render/extract/strip.py +20 -0
- metaobjects/render/extract/types.py +332 -0
- metaobjects/render/extract/xml_forgiving_reader.py +162 -0
- metaobjects/render/filesystem_provider.py +51 -0
- metaobjects/render/prompt/__init__.py +32 -0
- metaobjects/render/prompt/output_format_renderer.py +340 -0
- metaobjects/render/prompt/output_format_spec.py +28 -0
- metaobjects/render/prompt/prompt_field.py +29 -0
- metaobjects/render/prompt/prompt_overrides.py +29 -0
- metaobjects/render/prompt/prompt_style.py +38 -0
- metaobjects/render/renderer.py +358 -0
- metaobjects/render/verify.py +266 -0
- metaobjects/runtime/__init__.py +39 -0
- metaobjects/runtime/llm_recorder.py +210 -0
- metaobjects/runtime/n2m_resolver.py +155 -0
- metaobjects/runtime/object_manager.py +715 -0
- metaobjects/runtime/tph.py +50 -0
- metaobjects/serializer_json.py +172 -0
- metaobjects/shared/__init__.py +0 -0
- metaobjects/shared/base_types.py +16 -0
- metaobjects/shared/separators.py +4 -0
- metaobjects/shared/structural.py +9 -0
- metaobjects/source/__init__.py +79 -0
- metaobjects/source/error_source.py +266 -0
- metaobjects/source/json_path.py +106 -0
- metaobjects/source/semantic_diff.py +98 -0
- metaobjects/source/yaml_positions.py +174 -0
- metaobjects/super_resolve.py +128 -0
- metaobjects/yaml_desugar.py +481 -0
- metaobjects-0.9.0.dist-info/METADATA +97 -0
- metaobjects-0.9.0.dist-info/RECORD +181 -0
- metaobjects-0.9.0.dist-info/WHEEL +4 -0
- metaobjects-0.9.0.dist-info/entry_points.txt +2 -0
- metaobjects-0.9.0.dist-info/licenses/LICENSE +189 -0
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# Java codegen specifics
|
|
2
|
+
|
|
3
|
+
The Java port targets Spring Boot consumers on Maven. Codegen runs as a build-time
|
|
4
|
+
Maven plugin goal — there is no standalone `meta` binary on the Java side (the Node
|
|
5
|
+
`meta` is for schema migrations only; see the migration reference).
|
|
6
|
+
|
|
7
|
+
## Maven coordinates
|
|
8
|
+
|
|
9
|
+
All artifacts share `groupId` `com.metaobjects` and one version property:
|
|
10
|
+
|
|
11
|
+
| Artifact | Purpose |
|
|
12
|
+
|---|---|
|
|
13
|
+
| `metaobjects-metadata` | metamodel + loader |
|
|
14
|
+
| `metaobjects-omdb` | runtime persistence (ObjectManagerDb) |
|
|
15
|
+
| `metaobjects-render` | FR-004 render + `Verify` (prompt/template drift) |
|
|
16
|
+
| `metaobjects-codegen-spring` | the Spring codegen generators |
|
|
17
|
+
| `metaobjects-maven-plugin` | the build-time codegen plugin |
|
|
18
|
+
| `metaobjects-core-spring` | optional Spring wiring |
|
|
19
|
+
|
|
20
|
+
## Plugin config in `pom.xml`
|
|
21
|
+
|
|
22
|
+
The plugin's `generate` goal binds to the `generate-sources` phase. Point its
|
|
23
|
+
`<loader><sourceDir>` at your metadata and list one `<generator>` per output you
|
|
24
|
+
want, by fully-qualified class name.
|
|
25
|
+
|
|
26
|
+
```xml
|
|
27
|
+
<build>
|
|
28
|
+
<plugins>
|
|
29
|
+
<plugin>
|
|
30
|
+
<groupId>com.metaobjects</groupId>
|
|
31
|
+
<artifactId>metaobjects-maven-plugin</artifactId>
|
|
32
|
+
<version>${metaobjects.version}</version>
|
|
33
|
+
<executions>
|
|
34
|
+
<execution>
|
|
35
|
+
<id>generate</id>
|
|
36
|
+
<phase>generate-sources</phase>
|
|
37
|
+
<goals><goal>generate</goal></goals>
|
|
38
|
+
<configuration>
|
|
39
|
+
<loader>
|
|
40
|
+
<sourceDir>src/main/metaobjects</sourceDir>
|
|
41
|
+
</loader>
|
|
42
|
+
<generators>
|
|
43
|
+
<generator>
|
|
44
|
+
<classname>com.metaobjects.generator.spring.SpringControllerGenerator</classname>
|
|
45
|
+
<args>
|
|
46
|
+
<outputDir>${project.build.directory}/generated-sources/java</outputDir>
|
|
47
|
+
</args>
|
|
48
|
+
</generator>
|
|
49
|
+
<generator>
|
|
50
|
+
<classname>com.metaobjects.generator.spring.SpringDtoGenerator</classname>
|
|
51
|
+
<args><outputDir>${project.build.directory}/generated-sources/java</outputDir></args>
|
|
52
|
+
</generator>
|
|
53
|
+
<generator>
|
|
54
|
+
<classname>com.metaobjects.generator.spring.SpringRepositoryGenerator</classname>
|
|
55
|
+
<args><outputDir>${project.build.directory}/generated-sources/java</outputDir></args>
|
|
56
|
+
</generator>
|
|
57
|
+
</generators>
|
|
58
|
+
</configuration>
|
|
59
|
+
</execution>
|
|
60
|
+
</executions>
|
|
61
|
+
</plugin>
|
|
62
|
+
</plugins>
|
|
63
|
+
</build>
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Run
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
mvn metaobjects:generate # the codegen goal directly (goalPrefix=metaobjects, goal=generate)
|
|
70
|
+
mvn compile # also runs it (the goal is bound to generate-sources)
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
A `metaobjects:verify` Maven goal exists for **codegen-drift** (re-generate and diff vs
|
|
74
|
+
committed output). Schema migration and live-DB drift are NOT Java goals — they run
|
|
75
|
+
through the Node `meta` tool (see the migration reference).
|
|
76
|
+
|
|
77
|
+
## `codegen-spring` generators
|
|
78
|
+
|
|
79
|
+
All live in `metaobjects-codegen-spring` under
|
|
80
|
+
`com.metaobjects.generator.spring.*`; wire any subset, typically all three of the
|
|
81
|
+
first group together:
|
|
82
|
+
|
|
83
|
+
| Generator | Output |
|
|
84
|
+
|---|---|
|
|
85
|
+
| `SpringControllerGenerator` | `<Entity>Controller.java` per writable entity (`source.rdb` `@kind="table"`) — Spring Web MVC, five CRUD endpoints on the cross-port REST contract (`?sort`, `?limit`/`?offset`, `?withCount=1` envelope, 404/400 envelopes) |
|
|
86
|
+
| `SpringDtoGenerator` | `<Entity>Dto.java` as a Java 21 `record`; wrapped primitives (`Long`/`Integer`/`Boolean`) so missing JSON props deserialise to `null`; currency = `Long` (integer minor units) |
|
|
87
|
+
| `SpringRepositoryGenerator` | `<Entity>Repository.java` — a hand-stubbed `interface` the consumer implements with their persistence layer (Spring Data JPA / jOOQ / JDBC) |
|
|
88
|
+
| `SpringPayloadGenerator` | a Java 21 `record` per template payload VO |
|
|
89
|
+
| `SpringOutputParserGenerator` | the `template.output` parser-on-receipt (see the prompts reference) |
|
|
90
|
+
| `SpringFilterAllowlistGenerator` | per-entity filter allowlist |
|
|
91
|
+
|
|
92
|
+
Metadata lives under `src/main/metaobjects/` in the same canonical JSON the other
|
|
93
|
+
ports read — fused-key form, `source.rdb` + `@table`, `@column` for a renamed
|
|
94
|
+
physical column.
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# Kotlin codegen specifics
|
|
2
|
+
|
|
3
|
+
The Kotlin port is a **codegen tier built on top of the Java port**. The loader,
|
|
4
|
+
render engine, and Maven plugin are all Java; `codegen-kotlin` emits idiomatic
|
|
5
|
+
Kotlin (`@Serializable data class`, Exposed `Table` objects, Spring
|
|
6
|
+
`@RestController`/`@Configuration`) via KotlinPoet. Codegen runs as the same
|
|
7
|
+
build-time Maven plugin goal the Java port uses — there is no standalone `meta`
|
|
8
|
+
binary on the JVM side (the Node `meta` is for schema migrations only; see the
|
|
9
|
+
migration reference).
|
|
10
|
+
|
|
11
|
+
## Contents
|
|
12
|
+
- Maven coordinates
|
|
13
|
+
- Plugin config in `pom.xml`
|
|
14
|
+
- Run
|
|
15
|
+
- `codegen-kotlin` generators
|
|
16
|
+
|
|
17
|
+
## Maven coordinates
|
|
18
|
+
|
|
19
|
+
All artifacts share `groupId` `com.metaobjects` and one version property:
|
|
20
|
+
|
|
21
|
+
| Artifact | Purpose |
|
|
22
|
+
|---|---|
|
|
23
|
+
| `metaobjects-metadata` | metamodel + loader |
|
|
24
|
+
| `metaobjects-metadata-ktx` | thin Kotlin facade over the Java loader + render engine |
|
|
25
|
+
| `metaobjects-render` | render + `Verify` + tolerant `extract` (prompt/template) |
|
|
26
|
+
| `metaobjects-codegen-kotlin` | the Kotlin (KotlinPoet) generators |
|
|
27
|
+
| `metaobjects-maven-plugin` | the build-time codegen plugin |
|
|
28
|
+
|
|
29
|
+
Generated entities and Exposed tables need the consumer's own runtime deps —
|
|
30
|
+
`org.jetbrains.exposed:exposed-core` and
|
|
31
|
+
`org.jetbrains.kotlinx:kotlinx-serialization-json` (the latter for
|
|
32
|
+
`@Serializable`/parser output).
|
|
33
|
+
|
|
34
|
+
## Plugin config in `pom.xml`
|
|
35
|
+
|
|
36
|
+
The plugin's `generate` goal binds to the `generate-sources` phase. Point its
|
|
37
|
+
`<loader><sourceDir>` at your metadata and list one `<generator>` per output you
|
|
38
|
+
want, by fully-qualified class name. The Kotlin generators run through the same
|
|
39
|
+
Java plugin via the shared SPI:
|
|
40
|
+
|
|
41
|
+
```xml
|
|
42
|
+
<build>
|
|
43
|
+
<plugins>
|
|
44
|
+
<plugin>
|
|
45
|
+
<groupId>com.metaobjects</groupId>
|
|
46
|
+
<artifactId>metaobjects-maven-plugin</artifactId>
|
|
47
|
+
<version>${metaobjects.version}</version>
|
|
48
|
+
<executions>
|
|
49
|
+
<execution>
|
|
50
|
+
<id>generate</id>
|
|
51
|
+
<phase>generate-sources</phase>
|
|
52
|
+
<goals><goal>generate</goal></goals>
|
|
53
|
+
<configuration>
|
|
54
|
+
<loader>
|
|
55
|
+
<sourceDir>src/main/metaobjects</sourceDir>
|
|
56
|
+
</loader>
|
|
57
|
+
<generators>
|
|
58
|
+
<generator>
|
|
59
|
+
<classname>com.metaobjects.generator.kotlin.KotlinEntityGenerator</classname>
|
|
60
|
+
<args><outputDir>${project.build.directory}/generated-sources/kotlin</outputDir></args>
|
|
61
|
+
</generator>
|
|
62
|
+
<generator>
|
|
63
|
+
<classname>com.metaobjects.generator.kotlin.KotlinExposedTableGenerator</classname>
|
|
64
|
+
<args><outputDir>${project.build.directory}/generated-sources/kotlin</outputDir></args>
|
|
65
|
+
</generator>
|
|
66
|
+
<generator>
|
|
67
|
+
<classname>com.metaobjects.generator.kotlin.KotlinSpringControllerGenerator</classname>
|
|
68
|
+
<args><outputDir>${project.build.directory}/generated-sources/kotlin</outputDir></args>
|
|
69
|
+
</generator>
|
|
70
|
+
</generators>
|
|
71
|
+
</configuration>
|
|
72
|
+
</execution>
|
|
73
|
+
</executions>
|
|
74
|
+
</plugin>
|
|
75
|
+
</plugins>
|
|
76
|
+
</build>
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Run
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
mvn metaobjects:generate # the codegen goal directly (goalPrefix=metaobjects, goal=generate)
|
|
83
|
+
mvn compile # also runs it (the goal is bound to generate-sources)
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
A `metaobjects:verify` Maven goal exists for **codegen-drift** (re-generate and diff
|
|
87
|
+
vs committed output). Schema migration and live-DB drift are NOT JVM goals — they run
|
|
88
|
+
through the Node `meta` tool (see the migration reference).
|
|
89
|
+
|
|
90
|
+
## `codegen-kotlin` generators
|
|
91
|
+
|
|
92
|
+
All live in `metaobjects-codegen-kotlin` under
|
|
93
|
+
`com.metaobjects.generator.kotlin.*`; wire the subset you need by FQ class name:
|
|
94
|
+
|
|
95
|
+
| Generator | Output |
|
|
96
|
+
|---|---|
|
|
97
|
+
| `KotlinEntityGenerator` | `<Entity>.kt` — `@Serializable data class` per `object.entity` / `object.value` |
|
|
98
|
+
| `KotlinExposedTableGenerator` | `<Entity>Table.kt` — Exposed `Table` object (PK + FK + `@storage` columns) for entities with `source.rdb` |
|
|
99
|
+
| `KotlinRelationsGenerator` | `<Entity>Relations.kt` — extension fns for `@cardinality="many"` query helpers |
|
|
100
|
+
| `KotlinSpringControllerGenerator` | `<Entity>Controller.kt` — Spring `@RestController`, five CRUD endpoints on the cross-port REST contract, for writable entities (`source.rdb` `@kind="table"`) |
|
|
101
|
+
| `KotlinPayloadGenerator` | `<Template>Payload.kt` — `@Serializable` payload data class from a template's `@payloadRef` |
|
|
102
|
+
| `KotlinOutputParserGenerator` | the `template.output` parser-on-receipt (see the prompts reference) |
|
|
103
|
+
| `KotlinValidatorGenerator` | `MetadataStartupValidator.kt` + `ExposedTableValidator.kt` (once per project) |
|
|
104
|
+
| `KotlinSpringConfigGenerator` | `MetadataExposedConfig.kt` — `@Configuration` wiring `Database.connect()` + the startup validator (once per project) |
|
|
105
|
+
| `KotlinStoredProcGenerator` | stored-procedure call wrappers for `source.rdb` `@kind="storedProc"` |
|
|
106
|
+
| `KotlinFilterAllowlistGenerator` | per-entity filter allowlist |
|
|
107
|
+
|
|
108
|
+
Metadata lives under `src/main/metaobjects/` in the same canonical JSON the other
|
|
109
|
+
ports read — fused-key form, `source.rdb` + `@table`, `@column` for a renamed
|
|
110
|
+
physical column.
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
# TypeScript codegen specifics
|
|
2
|
+
|
|
3
|
+
The TS port is the reference implementation, published to npm as `@metaobjectsdev/*`
|
|
4
|
+
packages. Codegen runs through the Node `meta` CLI (`@metaobjectsdev/cli`, binary
|
|
5
|
+
`meta`).
|
|
6
|
+
|
|
7
|
+
## Contents
|
|
8
|
+
- Install
|
|
9
|
+
- `metaobjects.config.ts`
|
|
10
|
+
- The generators
|
|
11
|
+
- Run
|
|
12
|
+
- Multiple output targets
|
|
13
|
+
- Field subtype → column mapping
|
|
14
|
+
|
|
15
|
+
## Install
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm install --save-dev @metaobjectsdev/cli @metaobjectsdev/codegen-ts
|
|
19
|
+
npm install @metaobjectsdev/metadata @metaobjectsdev/runtime-ts
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
For the React + TanStack codegen packages, also:
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
npm install --save-dev @metaobjectsdev/codegen-ts-react @metaobjectsdev/codegen-ts-tanstack
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## `metaobjects.config.ts`
|
|
29
|
+
|
|
30
|
+
Codegen is wired in a type-checked TS config at the project root. `defineConfig`
|
|
31
|
+
comes from `@metaobjectsdev/cli`; the generators come from their packages.
|
|
32
|
+
|
|
33
|
+
```ts
|
|
34
|
+
import { defineConfig } from "@metaobjectsdev/cli";
|
|
35
|
+
import { entityFile, queriesFile, routesFile, barrel } from "@metaobjectsdev/codegen-ts/generators";
|
|
36
|
+
import { formFile } from "@metaobjectsdev/codegen-ts-react";
|
|
37
|
+
import { tanstackQuery, tanstackGrid } from "@metaobjectsdev/codegen-ts-tanstack";
|
|
38
|
+
|
|
39
|
+
export default defineConfig({
|
|
40
|
+
outDir: "src/generated",
|
|
41
|
+
dialect: "postgres", // "postgres" | "sqlite" | "d1" (D1 is TS-only)
|
|
42
|
+
apiPrefix: "/api", // flows to routes AND client fetch URLs
|
|
43
|
+
columnNamingStrategy: "snake_case", // "snake_case" (default) | "literal" | "kebab-case"
|
|
44
|
+
generators: [
|
|
45
|
+
entityFile(), queriesFile(), routesFile(), barrel(),
|
|
46
|
+
formFile(), tanstackQuery(), tanstackGrid(),
|
|
47
|
+
],
|
|
48
|
+
});
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
A second file, `.metaobjects/config.json`, holds static project state parseable by
|
|
52
|
+
non-TS tooling; `meta init` scaffolds both plus the `metaobjects/` source dir.
|
|
53
|
+
|
|
54
|
+
## The generators
|
|
55
|
+
|
|
56
|
+
From `@metaobjectsdev/codegen-ts/generators` (server-side, framework-neutral):
|
|
57
|
+
|
|
58
|
+
| Generator | Emits per entity |
|
|
59
|
+
|---|---|
|
|
60
|
+
| `entityFile()` | `<Entity>.ts` — Drizzle table + FK `.references()` + `relations()` + inferred types + Zod insert/update schemas + `<Entity>FilterAllowlist` / `<Entity>SortAllowlist` |
|
|
61
|
+
| `queriesFile()` | `<Entity>.queries.ts` — typed CRUD (`findPostById`, `listPosts`, `createPost`, `updatePost`, `deletePostById`) |
|
|
62
|
+
| `routesFile()` | `<Entity>.routes.ts` — Fastify CRUD routes on the cross-port REST contract. `routesFileHono()` is the Hono/Workers variant |
|
|
63
|
+
| `barrel()` | `index.ts` re-exporting each `<Entity>.ts` (one-shot, not per-entity) |
|
|
64
|
+
| `promptRender()` | `render<Name>()` per `template.prompt` |
|
|
65
|
+
| `outputParser()` | `<Name>.output.ts` (`parse*` / `safeParse*`) per `template.output` |
|
|
66
|
+
|
|
67
|
+
## Docs — `meta docs` (one door, two surfaces)
|
|
68
|
+
|
|
69
|
+
Documentation is NOT a `meta gen` generator. The single door is the `meta docs`
|
|
70
|
+
command, which emits two cross-linked **surfaces** under one output dir (default
|
|
71
|
+
`./docs`):
|
|
72
|
+
|
|
73
|
+
- **model surface** (`./docs/<Entity>.md`, `./docs/<Template>.md`) — the neutral
|
|
74
|
+
metadata reference: one page per entity and per template, including the linked
|
|
75
|
+
template-source section.
|
|
76
|
+
- **api surface** (`./docs/api/<Entity>.md`, `./docs/api/README.md`,
|
|
77
|
+
`./docs/api/AGENT-API.md`) — the SDK/API reference: the concrete imports,
|
|
78
|
+
function signatures, payload field shapes, and runnable examples for *this*
|
|
79
|
+
project's generated code.
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
npx meta docs # both surfaces → ./docs (model) + ./docs/api (api)
|
|
83
|
+
npx meta docs --model # model surface only
|
|
84
|
+
npx meta docs --api # api surface only
|
|
85
|
+
npx meta docs --out ./site-docs # write under a different root
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Other flags: `--layout flat|package`, `--base-url <url>`. Configure defaults in a
|
|
89
|
+
`docs:` block in `metaobjects.config.ts` (`outDir`, `layout`, `baseUrl`,
|
|
90
|
+
`surfaces`); CLI flags override it. The api surface needs the gen config
|
|
91
|
+
(it documents what the codegen produced); with no config it is skipped with a note,
|
|
92
|
+
and the model surface still emits from metadata alone.
|
|
93
|
+
|
|
94
|
+
**Before calling any generated code, read `./docs/api/AGENT-API.md`** — it has the
|
|
95
|
+
exact imports, signatures, payload field shapes, and runnable examples for this
|
|
96
|
+
project's generated API, so you don't have to guess them.
|
|
97
|
+
|
|
98
|
+
From `@metaobjectsdev/codegen-ts-react`: `formFile()` → `<Entity>.form.tsx`.
|
|
99
|
+
From `@metaobjectsdev/codegen-ts-tanstack`: `tanstackQuery()` → `<Entity>.hooks.ts`
|
|
100
|
+
(5 React Query hooks), `tanstackGrid()` → `<Entity>.columns.tsx`,
|
|
101
|
+
`tanstackGridHook()` → `<Entity>.grid.tsx`.
|
|
102
|
+
|
|
103
|
+
`entityFile({ allowlists: false })` drops the `runtime-ts/drizzle-fastify` import
|
|
104
|
+
for edge/worker consumers that don't mount server routes. Per-entity opt-out:
|
|
105
|
+
`@emitTanstack: false` on the entity skips its hook + column files.
|
|
106
|
+
|
|
107
|
+
## Run
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
npx meta gen # load metadata → render → 3-way merge → write
|
|
111
|
+
npx meta gen --dry-run # preview without writing
|
|
112
|
+
npx meta gen --watch # re-run on metadata file change
|
|
113
|
+
npx meta gen Author Post # scope to named entities
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
Generated files carry an `@generated by @metaobjectsdev/codegen-ts` header; the
|
|
117
|
+
runner overwrites those and refuses to touch files without it. Hand-customizations
|
|
118
|
+
that metadata can't express live in sibling `<Entity>.extra.ts` files.
|
|
119
|
+
|
|
120
|
+
## Multiple output targets
|
|
121
|
+
|
|
122
|
+
A `targets: { web: { outDir }, api: { outDir } }` registry plus a per-generator
|
|
123
|
+
`target` routes each artifact to its own package (model → database package, routes →
|
|
124
|
+
API app, hooks/forms → web app). The top-level `outDir` is the implicit `default`
|
|
125
|
+
(entity-module) target; set `entityModuleImportBase` on it when generators route
|
|
126
|
+
elsewhere so cross-target imports resolve. With no `targets`, output is
|
|
127
|
+
byte-identical to a single-`outDir` project.
|
|
128
|
+
|
|
129
|
+
## Field subtype → column mapping
|
|
130
|
+
|
|
131
|
+
Deterministic per dialect: `field.string` + `@maxLength` → `varchar(N)`,
|
|
132
|
+
`field.currency` → integer minor units (`bigint`), `field.uuid` → native `uuid`
|
|
133
|
+
(Postgres) + `gen_random_uuid()`, `field.enum` → `varchar` + `CHECK`. Override a
|
|
134
|
+
field's physical column name with `@column` on the field; the DB schema name lives
|
|
135
|
+
on `source.rdb` via `@schema`.
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: metaobjects-prompts
|
|
3
|
+
description: Use when declaring or using MetaObjects prompt construction: template.prompt/template.output, typed payload projections, provider-resolved text, deterministic render, prompt-drift verify, and parser-on-receipt.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# MetaObjects prompt construction
|
|
7
|
+
|
|
8
|
+
The fourth pillar: **a prompt is code, not a string scattered across services.**
|
|
9
|
+
Declare a prompt's payload as a typed projection (payload bloat becomes a diff),
|
|
10
|
+
keep its text external and provider-resolved, and render it deterministically —
|
|
11
|
+
snapshot-testable, cache-stable, and drift-checked at build time. The same
|
|
12
|
+
machinery renders any text artifact: emails, exports, docs, `llms.txt`.
|
|
13
|
+
|
|
14
|
+
This skill is port-agnostic. The exact render/parse API for *this* project's server
|
|
15
|
+
language lives in a reference fragment (pointed to at the bottom).
|
|
16
|
+
|
|
17
|
+
## The two template subtypes
|
|
18
|
+
|
|
19
|
+
A **template** is a typed pair: a logical reference to external text + a payload
|
|
20
|
+
value-object declaring exactly what data the text expects.
|
|
21
|
+
|
|
22
|
+
| Subtype | Use | Extra attrs |
|
|
23
|
+
|---|---|---|
|
|
24
|
+
| `template.prompt` | LLM-targeted | `@maxTokens`, `@requiredSlots`, `@model` |
|
|
25
|
+
| `template.output` | email / docs / config / export | (generic only) |
|
|
26
|
+
|
|
27
|
+
Both carry the generic attrs:
|
|
28
|
+
|
|
29
|
+
| Attr | Required | Purpose |
|
|
30
|
+
|---|---|---|
|
|
31
|
+
| `@payloadRef` | yes | the `object.value` declaring the payload shape |
|
|
32
|
+
| `@textRef` | yes | the 2-layer logical text reference `group/source`, resolved by a provider |
|
|
33
|
+
| `@format` | no | `text` (default) / `html` / `xml` / `csv` / `json` / `markdown` / `spreadsheet` — drives the escaper |
|
|
34
|
+
| `@maxChars` | no | build-time size budget |
|
|
35
|
+
|
|
36
|
+
## The payload is an `object.value` projection
|
|
37
|
+
|
|
38
|
+
The payload is **not** an entity — it's an `object.value` whose every field carries
|
|
39
|
+
an `origin.*` child saying where its value comes from. Three origin subtypes:
|
|
40
|
+
|
|
41
|
+
| Origin | Behavior |
|
|
42
|
+
|---|---|
|
|
43
|
+
| `origin.passthrough @from "Entity.field"` | payload property matches the source field |
|
|
44
|
+
| `origin.aggregate @agg <count\|sum\|avg\|min\|max>` | `count`→long, `avg`→double, others match source |
|
|
45
|
+
| `origin.collection @via "Parent.rel"` | a list of a nested payload, assembled from a relationship |
|
|
46
|
+
|
|
47
|
+
Declaring the payload as a projection is what makes payload bloat visible: adding a
|
|
48
|
+
field to the prompt is a diff on the `object.value`, and a renamed source field
|
|
49
|
+
breaks the build instead of silently degrading the prompt.
|
|
50
|
+
|
|
51
|
+
```json
|
|
52
|
+
{
|
|
53
|
+
"metadata.root": {
|
|
54
|
+
"package": "acme::blog",
|
|
55
|
+
"children": [
|
|
56
|
+
{
|
|
57
|
+
"object.value": {
|
|
58
|
+
"name": "WelcomePayload",
|
|
59
|
+
"children": [
|
|
60
|
+
{ "field.string": { "name": "displayName",
|
|
61
|
+
"children": [ { "origin.passthrough": { "@from": "Author.name" } } ] } },
|
|
62
|
+
{ "field.long": { "name": "postCount",
|
|
63
|
+
"children": [ { "origin.aggregate": { "@agg": "count", "@of": "Post.id", "@via": "Author.posts" } } ] } },
|
|
64
|
+
{ "field.object": { "name": "posts", "@objectRef": "PostSummary",
|
|
65
|
+
"children": [ { "origin.collection": { "@via": "Author.posts" } } ] } }
|
|
66
|
+
]
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
"object.value": {
|
|
71
|
+
"name": "PostSummary",
|
|
72
|
+
"children": [
|
|
73
|
+
{ "field.string": { "name": "title",
|
|
74
|
+
"children": [ { "origin.passthrough": { "@from": "Post.title" } } ] } }
|
|
75
|
+
]
|
|
76
|
+
}
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
"template.prompt": {
|
|
80
|
+
"name": "WelcomePrompt",
|
|
81
|
+
"@payloadRef": "WelcomePayload",
|
|
82
|
+
"@textRef": "lobby/welcome",
|
|
83
|
+
"@format": "xml",
|
|
84
|
+
"@maxTokens": 500
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
]
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## Prompt text is external + provider-resolved (never inlined)
|
|
93
|
+
|
|
94
|
+
`@textRef` is a 2-layer logical reference `group/source` (folder/file,
|
|
95
|
+
table/key, collection/document). The prompt text itself **never lives in
|
|
96
|
+
metadata** — at runtime a configured **provider** resolves the reference to the
|
|
97
|
+
actual Mustache text:
|
|
98
|
+
|
|
99
|
+
- a filesystem provider (L1 = folder, L2 = file) — the dev default;
|
|
100
|
+
- an in-memory provider (a string map) — tests;
|
|
101
|
+
- a classpath/resource provider on the JVM;
|
|
102
|
+
- or a consumer-supplied provider (RDB / vector store / …).
|
|
103
|
+
|
|
104
|
+
Locale, A/B, dynamic, and evolutionary prompt variants all live behind the
|
|
105
|
+
provider seam without touching metadata.
|
|
106
|
+
|
|
107
|
+
## `render()` is deterministic + byte-stable
|
|
108
|
+
|
|
109
|
+
Rendering is a pure function: `(payload VO, resolved text) → string`. Same inputs,
|
|
110
|
+
byte-identical output — across runs and across every language port. That stability
|
|
111
|
+
is what protects **exact-prefix prompt-cache hits**: a stray whitespace change can't
|
|
112
|
+
silently break a cache prefix because the output doesn't drift. Determinism rules
|
|
113
|
+
the engine enforces:
|
|
114
|
+
|
|
115
|
+
- arrays only for iteration (no object-key iteration);
|
|
116
|
+
- no locale/number/date formatting in the engine — pre-format on the payload;
|
|
117
|
+
- pinned trailing-newline + Mustache standalone-tag whitespace;
|
|
118
|
+
- `@format` drives an engine-owned escaper (CSV/spreadsheet escapers neutralize a
|
|
119
|
+
leading `= + - @ \t \r` per the OWASP CSV-injection guard).
|
|
120
|
+
|
|
121
|
+
For the `xml`-format example above with payload `{ displayName: "Ada", postCount:
|
|
122
|
+
12, posts: [{title:"Hello"}, {title:"Mustache"}] }`, every port renders the same
|
|
123
|
+
bytes. You render the prompt, call your LLM client (provider-agnostic — codegen
|
|
124
|
+
emits no provider-side schema), then parse the response.
|
|
125
|
+
|
|
126
|
+
## `verify` fails the build on prompt-drift
|
|
127
|
+
|
|
128
|
+
For every template, the verify step resolves the text, parses each `{{...}}`
|
|
129
|
+
reference, and checks it exists on the payload VO. If the text references
|
|
130
|
+
`{{authorName}}` but the payload only has `displayName`, **the build fails.** This
|
|
131
|
+
is the prompt-vs-payload drift gate — run it in CI. It walks both `template.prompt`
|
|
132
|
+
and `template.output` nodes the same way.
|
|
133
|
+
|
|
134
|
+
## `template.output` also generates a parser-on-receipt
|
|
135
|
+
|
|
136
|
+
For every `template.output`, codegen emits a **typed parser** that turns an LLM/raw
|
|
137
|
+
response back into the `@payloadRef` value-object — the reverse direction, reusing
|
|
138
|
+
the same payload VO (no new authoring). Each port emits it idiomatically: a
|
|
139
|
+
throw-on-invalid parse plus, where the language has the precedent, a Result-style
|
|
140
|
+
"safe" variant that doesn't throw. The parser file is a companion to the payload-VO
|
|
141
|
+
file; `verify` catches payload-VO ↔ parser drift at build time too.
|
|
142
|
+
|
|
143
|
+
The three-step consumer pattern is identical everywhere: render the prompt → call
|
|
144
|
+
your LLM client → parse the response with the generated parser.
|
|
145
|
+
|
|
146
|
+
---
|
|
147
|
+
|
|
148
|
+
For this project's server-language parser specifics, read every `references/*.md` file in this skill's directory (one per server language in this project's stack).
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# C# parser-on-receipt
|
|
2
|
+
|
|
3
|
+
For every `template.output`, `MetaObjects.Codegen`'s `OutputParserGenerator` emits a
|
|
4
|
+
**typed parser** that validates an LLM/raw response against the template's
|
|
5
|
+
`@payloadRef` payload record. This is the receive side only — codegen emits **no**
|
|
6
|
+
provider/LLM-call layer; you compose the call yourself. The payload record comes from
|
|
7
|
+
the payload generator, so the parser and the payload VO can't silently drift.
|
|
8
|
+
|
|
9
|
+
## Contents
|
|
10
|
+
- Wire the generator
|
|
11
|
+
- What it emits
|
|
12
|
+
- The three-step consumer pattern
|
|
13
|
+
- Recommended LLM caller (bring-your-own)
|
|
14
|
+
- Consumer dependency
|
|
15
|
+
- Drift gate
|
|
16
|
+
|
|
17
|
+
## Wire the generator
|
|
18
|
+
|
|
19
|
+
`OutputParserGenerator` (stable name `output-parser`) runs as part of
|
|
20
|
+
`dotnet meta gen`, alongside the payload generator that emits the record it parses
|
|
21
|
+
into:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
dotnet meta gen ./metadata --out ./Generated --namespace Acme.Blog
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## What it emits
|
|
28
|
+
|
|
29
|
+
Per `template.output`, `dotnet meta gen` writes one `<TemplateName>.output.cs` with a
|
|
30
|
+
static `<TemplateName>Parser` following the .NET BCL `Parse`/`TryParse` dual API —
|
|
31
|
+
`Parse` throws, `TryParse` returns a bool plus an out-error:
|
|
32
|
+
|
|
33
|
+
```csharp
|
|
34
|
+
// generated <TemplateName>.output.cs (shape)
|
|
35
|
+
public static class NpcResponseParser
|
|
36
|
+
{
|
|
37
|
+
/// <exception cref="JsonException">malformed JSON or schema mismatch.</exception>
|
|
38
|
+
public static NpcResponse Parse(string text) =>
|
|
39
|
+
JsonSerializer.Deserialize<NpcResponse>(text, Options)
|
|
40
|
+
?? throw new JsonException("deserialized to null");
|
|
41
|
+
|
|
42
|
+
public static bool TryParse(string text,
|
|
43
|
+
[NotNullWhen(true)] out NpcResponse? value,
|
|
44
|
+
[NotNullWhen(false)] out string? error) { /* ... */ }
|
|
45
|
+
}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
The `[NotNullWhen]` attrs let nullable-flow analysis use `value` without a null-check
|
|
49
|
+
after a `true` return (and `error` after `false`). For `@format: json|xml` outputs the
|
|
50
|
+
generator also emits a tolerant `Extract(string[, ExtractOptions])` (self-contained) +
|
|
51
|
+
`Extract(MetaObject, string, ...)` (runtime-delegating, fully populating nested
|
|
52
|
+
components) returning an `ExtractionResult` with a nullable `<Payload>Extracted` mirror
|
|
53
|
+
— a classified per-field report rather than a throw.
|
|
54
|
+
|
|
55
|
+
## The three-step consumer pattern
|
|
56
|
+
|
|
57
|
+
Render the prompt → call your LLM client (provider-agnostic; nothing is generated
|
|
58
|
+
here) → parse the response with the generated parser:
|
|
59
|
+
|
|
60
|
+
```csharp
|
|
61
|
+
string llmResponse = await myLlmClient.CompleteAsync(promptText); // YOUR code — no generated provider
|
|
62
|
+
|
|
63
|
+
// Throwing path
|
|
64
|
+
var npc = NpcResponseParser.Parse(llmResponse);
|
|
65
|
+
|
|
66
|
+
// TryParse for explicit error handling
|
|
67
|
+
if (NpcResponseParser.TryParse(llmResponse, out var npc, out var error))
|
|
68
|
+
return Ok(npc);
|
|
69
|
+
return BadRequest(new { error });
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Recommended LLM caller (bring-your-own)
|
|
73
|
+
|
|
74
|
+
`dotnet meta gen` emits **no** provider/LLM-call layer and never will — calling is a
|
|
75
|
+
commodity the ecosystem already solves (ADR-0024). You bring the caller; MetaObjects
|
|
76
|
+
owns the typed render → parse (above) → record. For the call step use the idiomatic
|
|
77
|
+
.NET library:
|
|
78
|
+
|
|
79
|
+
```csharp
|
|
80
|
+
using Microsoft.Extensions.AI; // recommended — IChatClient is provider-agnostic
|
|
81
|
+
|
|
82
|
+
IChatClient client = /* an Anthropic / OpenAI / Azure IChatClient */;
|
|
83
|
+
ChatResponse resp = await client.GetResponseAsync(promptText);
|
|
84
|
+
string text = resp.Text;
|
|
85
|
+
|
|
86
|
+
var npc = NpcResponseParser.Parse(text); // the generated parser, above
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
**Recommended: Microsoft.Extensions.AI** (`IChatClient`) — the emerging
|
|
90
|
+
provider-agnostic .NET standard; its one-method shape matches MetaObjects' call seam.
|
|
91
|
+
For higher-level agent orchestration, **Semantic Kernel** layers on top of the same
|
|
92
|
+
`IChatClient`.
|
|
93
|
+
|
|
94
|
+
> The typed-trace recorder + render→call→record convenience loop ship in TypeScript
|
|
95
|
+
> today; the C# port is planned (ADR-0024). Until then the call is your code and the
|
|
96
|
+
> generated parser is the typed receive side.
|
|
97
|
+
|
|
98
|
+
## Consumer dependency
|
|
99
|
+
|
|
100
|
+
`System.Text.Json` ships in the .NET 8 BCL — no NuGet to add. The generated parser
|
|
101
|
+
uses strict (case-sensitive) options.
|
|
102
|
+
|
|
103
|
+
## Drift gate
|
|
104
|
+
|
|
105
|
+
`MetaObjects.Render`'s `Verify.Check(string templateText, IReadOnlyList<PayloadField>
|
|
106
|
+
fields, VerifyOptions? options = null) -> IReadOnlyList<VerifyError>` walks a Mustache
|
|
107
|
+
template's tokens against the payload field tree — each `{{...}}` that doesn't resolve
|
|
108
|
+
yields a `VerifyError(Code, Path)` (empty list = no drift). Assert it is empty in an
|
|
109
|
+
xUnit test to fail the build on prompt/payload drift; `dotnet meta verify` additionally
|
|
110
|
+
catches a stale committed parser.
|