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.
Files changed (181) hide show
  1. metaobjects/__init__.py +75 -0
  2. metaobjects/agent_context/__init__.py +55 -0
  3. metaobjects/agent_context/_content/README.md +14 -0
  4. metaobjects/agent_context/_content/servers/csharp.meta.json +5 -0
  5. metaobjects/agent_context/_content/servers/java.meta.json +5 -0
  6. metaobjects/agent_context/_content/servers/kotlin.meta.json +5 -0
  7. metaobjects/agent_context/_content/servers/python.meta.json +5 -0
  8. metaobjects/agent_context/_content/servers/typescript.meta.json +5 -0
  9. metaobjects/agent_context/_content/skills/metaobjects-authoring/SKILL.md +301 -0
  10. metaobjects/agent_context/_content/skills/metaobjects-codegen/SKILL.md +99 -0
  11. metaobjects/agent_context/_content/skills/metaobjects-codegen/references/csharp.md +87 -0
  12. metaobjects/agent_context/_content/skills/metaobjects-codegen/references/java.md +94 -0
  13. metaobjects/agent_context/_content/skills/metaobjects-codegen/references/kotlin.md +110 -0
  14. metaobjects/agent_context/_content/skills/metaobjects-codegen/references/typescript.md +135 -0
  15. metaobjects/agent_context/_content/skills/metaobjects-prompts/SKILL.md +148 -0
  16. metaobjects/agent_context/_content/skills/metaobjects-prompts/references/csharp.md +110 -0
  17. metaobjects/agent_context/_content/skills/metaobjects-prompts/references/java.md +108 -0
  18. metaobjects/agent_context/_content/skills/metaobjects-prompts/references/kotlin.md +130 -0
  19. metaobjects/agent_context/_content/skills/metaobjects-prompts/references/python.md +116 -0
  20. metaobjects/agent_context/_content/skills/metaobjects-prompts/references/typescript.md +150 -0
  21. metaobjects/agent_context/_content/skills/metaobjects-runtime-ui/SKILL.md +130 -0
  22. metaobjects/agent_context/_content/skills/metaobjects-runtime-ui/references/java.md +96 -0
  23. metaobjects/agent_context/_content/skills/metaobjects-runtime-ui/references/kotlin.md +99 -0
  24. metaobjects/agent_context/_content/skills/metaobjects-runtime-ui/references/react.md +86 -0
  25. metaobjects/agent_context/_content/skills/metaobjects-runtime-ui/references/tanstack.md +119 -0
  26. metaobjects/agent_context/_content/skills/metaobjects-runtime-ui/references/typescript.md +92 -0
  27. metaobjects/agent_context/_content/skills/metaobjects-verify/SKILL.md +107 -0
  28. metaobjects/agent_context/_content/skills/metaobjects-verify/references/migration.md +72 -0
  29. metaobjects/agent_context/_content/templates/always-on.md.mustache +27 -0
  30. metaobjects/agent_context/assemble.py +133 -0
  31. metaobjects/agent_context/content_root.py +54 -0
  32. metaobjects/agent_context/scaffold.py +191 -0
  33. metaobjects/agent_context/types.py +44 -0
  34. metaobjects/attr_class_map.py +23 -0
  35. metaobjects/cli.py +696 -0
  36. metaobjects/codegen/__init__.py +0 -0
  37. metaobjects/codegen/config.py +11 -0
  38. metaobjects/codegen/constants.py +13 -0
  39. metaobjects/codegen/extract_delegate_emitter.py +384 -0
  40. metaobjects/codegen/extract_schema_emitter.py +139 -0
  41. metaobjects/codegen/format.py +31 -0
  42. metaobjects/codegen/fr010_field_mapping.py +220 -0
  43. metaobjects/codegen/generator.py +62 -0
  44. metaobjects/codegen/generator_registry.py +163 -0
  45. metaobjects/codegen/generators/__init__.py +0 -0
  46. metaobjects/codegen/generators/entity_model.py +263 -0
  47. metaobjects/codegen/generators/extractor_generator.py +317 -0
  48. metaobjects/codegen/generators/filter_allowlist_generator.py +309 -0
  49. metaobjects/codegen/generators/m2m_codegen.py +192 -0
  50. metaobjects/codegen/generators/output_parser_generator.py +272 -0
  51. metaobjects/codegen/generators/output_prompt_generator.py +192 -0
  52. metaobjects/codegen/generators/payload_vo_generator.py +672 -0
  53. metaobjects/codegen/generators/render_helper_generator.py +451 -0
  54. metaobjects/codegen/generators/router_generator.py +635 -0
  55. metaobjects/codegen/generators/template_generator.py +70 -0
  56. metaobjects/codegen/generators/tph_plan.py +120 -0
  57. metaobjects/codegen/generators/trace_helper_generator.py +336 -0
  58. metaobjects/codegen/instance_artifacts.py +15 -0
  59. metaobjects/codegen/output_format_spec_emitter.py +79 -0
  60. metaobjects/codegen/overwrite_policy.py +27 -0
  61. metaobjects/codegen/runner.py +110 -0
  62. metaobjects/codegen/runtime/__init__.py +6 -0
  63. metaobjects/codegen/runtime/filter_parser.py +193 -0
  64. metaobjects/codegen/type_map.py +84 -0
  65. metaobjects/core_types.py +809 -0
  66. metaobjects/datatype.py +19 -0
  67. metaobjects/documentation/__init__.py +28 -0
  68. metaobjects/documentation/doc_constants.py +20 -0
  69. metaobjects/documentation/doc_provider.py +20 -0
  70. metaobjects/documentation/doc_schema.py +24 -0
  71. metaobjects/errors.py +124 -0
  72. metaobjects/loader/__init__.py +0 -0
  73. metaobjects/loader/merge.py +287 -0
  74. metaobjects/loader/meta_data_loader.py +245 -0
  75. metaobjects/loader/sources/__init__.py +24 -0
  76. metaobjects/loader/sources/directory_source.py +50 -0
  77. metaobjects/loader/sources/file_source.py +41 -0
  78. metaobjects/loader/sources/meta_data_source.py +67 -0
  79. metaobjects/loader/sources/uri_source.py +56 -0
  80. metaobjects/loader/validate_discriminator.py +181 -0
  81. metaobjects/loader/validate_field_readonly.py +146 -0
  82. metaobjects/loader/validate_source_parameter_ref.py +159 -0
  83. metaobjects/loader/validate_source_physical_names.py +140 -0
  84. metaobjects/loader/validation_passes.py +1513 -0
  85. metaobjects/meta/__init__.py +1 -0
  86. metaobjects/meta/core/__init__.py +0 -0
  87. metaobjects/meta/core/attr/__init__.py +0 -0
  88. metaobjects/meta/core/attr/attr_constants.py +31 -0
  89. metaobjects/meta/core/attr/meta_attr.py +136 -0
  90. metaobjects/meta/core/field/__init__.py +0 -0
  91. metaobjects/meta/core/field/field_constants.py +105 -0
  92. metaobjects/meta/core/field/meta_field.py +76 -0
  93. metaobjects/meta/core/identity/__init__.py +0 -0
  94. metaobjects/meta/core/identity/identity_constants.py +19 -0
  95. metaobjects/meta/core/identity/meta_identity.py +8 -0
  96. metaobjects/meta/core/object/__init__.py +0 -0
  97. metaobjects/meta/core/object/meta_object.py +65 -0
  98. metaobjects/meta/core/object/meta_object_aware.py +43 -0
  99. metaobjects/meta/core/object/object_class_registry.py +56 -0
  100. metaobjects/meta/core/object/object_constants.py +13 -0
  101. metaobjects/meta/core/object/object_extract.py +400 -0
  102. metaobjects/meta/core/object/value_object.py +70 -0
  103. metaobjects/meta/core/relationship/__init__.py +0 -0
  104. metaobjects/meta/core/relationship/derive_m2m_fields.py +180 -0
  105. metaobjects/meta/core/relationship/meta_relationship.py +54 -0
  106. metaobjects/meta/core/relationship/relationship_constants.py +51 -0
  107. metaobjects/meta/core/validator/__init__.py +0 -0
  108. metaobjects/meta/core/validator/validator_constants.py +18 -0
  109. metaobjects/meta/meta_data.py +206 -0
  110. metaobjects/meta/meta_root.py +8 -0
  111. metaobjects/meta/persistence/__init__.py +0 -0
  112. metaobjects/meta/persistence/db/__init__.py +1 -0
  113. metaobjects/meta/persistence/db/db_constants.py +41 -0
  114. metaobjects/meta/persistence/db/db_provider.py +60 -0
  115. metaobjects/meta/persistence/origin/__init__.py +0 -0
  116. metaobjects/meta/persistence/origin/meta_origin.py +8 -0
  117. metaobjects/meta/persistence/origin/origin_constants.py +20 -0
  118. metaobjects/meta/persistence/source/__init__.py +0 -0
  119. metaobjects/meta/persistence/source/meta_source.py +137 -0
  120. metaobjects/meta/persistence/source/source_constants.py +115 -0
  121. metaobjects/meta/presentation/__init__.py +0 -0
  122. metaobjects/meta/presentation/layout/__init__.py +0 -0
  123. metaobjects/meta/presentation/layout/layout_constants.py +13 -0
  124. metaobjects/meta/presentation/layout/meta_layout.py +8 -0
  125. metaobjects/meta/presentation/view/__init__.py +0 -0
  126. metaobjects/meta/presentation/view/meta_view.py +8 -0
  127. metaobjects/meta/presentation/view/view_constants.py +22 -0
  128. metaobjects/meta/template/__init__.py +0 -0
  129. metaobjects/meta/template/meta_template.py +46 -0
  130. metaobjects/meta/template/template_constants.py +112 -0
  131. metaobjects/meta/template/template_provider.py +43 -0
  132. metaobjects/parser.py +380 -0
  133. metaobjects/parser_yaml.py +82 -0
  134. metaobjects/provider.py +111 -0
  135. metaobjects/py.typed +0 -0
  136. metaobjects/registry.py +210 -0
  137. metaobjects/registry_manifest.py +223 -0
  138. metaobjects/render/__init__.py +74 -0
  139. metaobjects/render/email_document.py +14 -0
  140. metaobjects/render/escapers.py +109 -0
  141. metaobjects/render/extract/__init__.py +59 -0
  142. metaobjects/render/extract/coerce.py +279 -0
  143. metaobjects/render/extract/extract.py +211 -0
  144. metaobjects/render/extract/extract_map.py +61 -0
  145. metaobjects/render/extract/json_forgiving_reader.py +203 -0
  146. metaobjects/render/extract/locate.py +65 -0
  147. metaobjects/render/extract/normalize.py +96 -0
  148. metaobjects/render/extract/strip.py +20 -0
  149. metaobjects/render/extract/types.py +332 -0
  150. metaobjects/render/extract/xml_forgiving_reader.py +162 -0
  151. metaobjects/render/filesystem_provider.py +51 -0
  152. metaobjects/render/prompt/__init__.py +32 -0
  153. metaobjects/render/prompt/output_format_renderer.py +340 -0
  154. metaobjects/render/prompt/output_format_spec.py +28 -0
  155. metaobjects/render/prompt/prompt_field.py +29 -0
  156. metaobjects/render/prompt/prompt_overrides.py +29 -0
  157. metaobjects/render/prompt/prompt_style.py +38 -0
  158. metaobjects/render/renderer.py +358 -0
  159. metaobjects/render/verify.py +266 -0
  160. metaobjects/runtime/__init__.py +39 -0
  161. metaobjects/runtime/llm_recorder.py +210 -0
  162. metaobjects/runtime/n2m_resolver.py +155 -0
  163. metaobjects/runtime/object_manager.py +715 -0
  164. metaobjects/runtime/tph.py +50 -0
  165. metaobjects/serializer_json.py +172 -0
  166. metaobjects/shared/__init__.py +0 -0
  167. metaobjects/shared/base_types.py +16 -0
  168. metaobjects/shared/separators.py +4 -0
  169. metaobjects/shared/structural.py +9 -0
  170. metaobjects/source/__init__.py +79 -0
  171. metaobjects/source/error_source.py +266 -0
  172. metaobjects/source/json_path.py +106 -0
  173. metaobjects/source/semantic_diff.py +98 -0
  174. metaobjects/source/yaml_positions.py +174 -0
  175. metaobjects/super_resolve.py +128 -0
  176. metaobjects/yaml_desugar.py +481 -0
  177. metaobjects-0.9.0.dist-info/METADATA +97 -0
  178. metaobjects-0.9.0.dist-info/RECORD +181 -0
  179. metaobjects-0.9.0.dist-info/WHEEL +4 -0
  180. metaobjects-0.9.0.dist-info/entry_points.txt +2 -0
  181. metaobjects-0.9.0.dist-info/licenses/LICENSE +189 -0
@@ -0,0 +1,75 @@
1
+ """metaobjects — Python implementation of the MetaObjects standard.
2
+
3
+ Top-level exports cover the cross-language loader API (MetaDataLoader +
4
+ LoadResult, the four MetaDataSource impls, and Pythonic module-level
5
+ shortcuts ``load_directory`` / ``load_uris`` / ``load_string`` that
6
+ alias the class factories ala ``requests.get`` over ``Session().get``).
7
+ """
8
+ from __future__ import annotations
9
+
10
+ from .errors import ErrorCode, MetaError
11
+ from .loader.meta_data_loader import LoadResult, MetaDataLoader
12
+ from .loader.sources import (
13
+ DirectorySource,
14
+ FileSource,
15
+ InMemoryStringSource,
16
+ MetaDataFormat,
17
+ MetaDataSource,
18
+ UriSource,
19
+ )
20
+ from .meta.core.object.meta_object_aware import (
21
+ MetaObjectAware,
22
+ is_meta_object_aware,
23
+ )
24
+ from .meta.core.object.object_class_registry import (
25
+ ObjectClassRegistry,
26
+ ObjectFactory,
27
+ default_object_class_registry,
28
+ )
29
+ from .meta.core.object.object_extract import (
30
+ MAX_NEST_DEPTH,
31
+ ExtractError,
32
+ assemble,
33
+ or_throw,
34
+ extract_object,
35
+ extract_schema_for,
36
+ )
37
+ from .meta.core.object.value_object import ValueObject
38
+
39
+ # Module-level shortcuts: the 99% case for callers who don't need a
40
+ # long-lived loader. Signatures + docstrings come straight from the
41
+ # classmethods — no wrapping layer to drift.
42
+ load_directory = MetaDataLoader.from_directory
43
+ load_uris = MetaDataLoader.from_uris
44
+ load_string = MetaDataLoader.from_string
45
+
46
+
47
+ __all__ = [
48
+ "MetaDataLoader",
49
+ "LoadResult",
50
+ "MetaDataSource",
51
+ "MetaDataFormat",
52
+ "FileSource",
53
+ "DirectorySource",
54
+ "UriSource",
55
+ "InMemoryStringSource",
56
+ "ErrorCode",
57
+ "MetaError",
58
+ "load_directory",
59
+ "load_uris",
60
+ "load_string",
61
+ # Runtime object model (Phase A)
62
+ "ValueObject",
63
+ "MetaObjectAware",
64
+ "is_meta_object_aware",
65
+ "ObjectClassRegistry",
66
+ "ObjectFactory",
67
+ "default_object_class_registry",
68
+ # Phase B metadata-driven extract bridge
69
+ "extract_object",
70
+ "extract_schema_for",
71
+ "assemble",
72
+ "or_throw",
73
+ "ExtractError",
74
+ "MAX_NEST_DEPTH",
75
+ ]
@@ -0,0 +1,55 @@
1
+ """Agent-context assembler — scaffold the slim MetaObjects Claude Code context.
2
+
3
+ Reproduces the TypeScript reference assembler (``server/typescript/packages/sdk/
4
+ src/agent-context/``) BYTE-FOR-BYTE: given the repo-root ``agent-context/``
5
+ content tree and a resolved :class:`Stack`, emit the consumer files
6
+ (``.metaobjects/AGENTS.md`` + ``CLAUDE.md`` and the five ``metaobjects-*`` Claude
7
+ Code skills, carrying only the reference fragments for the project's stack).
8
+
9
+ The assembly is pure given the content tree — the only computed content is the
10
+ two always-on template substitutions; every other file is a verbatim UTF-8 copy.
11
+ """
12
+
13
+ from __future__ import annotations
14
+
15
+ from .assemble import (
16
+ AssembledFile,
17
+ assemble,
18
+ make_stack,
19
+ )
20
+ from .content_root import resolve_agent_context_root
21
+ from .scaffold import (
22
+ AGENT_CONTEXT_MANIFEST_PATH,
23
+ Manifest,
24
+ ScaffoldDecision,
25
+ agent_context_staleness,
26
+ hash_contents,
27
+ installed_metaobjects_version,
28
+ plan_scaffold,
29
+ )
30
+ from .types import (
31
+ CLIENT_FRAMEWORKS,
32
+ MIGRATION_TOKEN,
33
+ SERVER_LANGS,
34
+ SKILL_NAMES,
35
+ Stack,
36
+ )
37
+
38
+ __all__ = [
39
+ "AGENT_CONTEXT_MANIFEST_PATH",
40
+ "AssembledFile",
41
+ "CLIENT_FRAMEWORKS",
42
+ "MIGRATION_TOKEN",
43
+ "Manifest",
44
+ "SERVER_LANGS",
45
+ "SKILL_NAMES",
46
+ "ScaffoldDecision",
47
+ "Stack",
48
+ "agent_context_staleness",
49
+ "assemble",
50
+ "hash_contents",
51
+ "installed_metaobjects_version",
52
+ "make_stack",
53
+ "plan_scaffold",
54
+ "resolve_agent_context_root",
55
+ ]
@@ -0,0 +1,14 @@
1
+ # agent-context — source of truth for downstream AI-assistant context
2
+
3
+ This tree is the single source the assembler (`@metaobjectsdev/sdk`,
4
+ `src/agent-context/`) turns into the files scaffolded into a consumer project:
5
+ the slim always-on Markdown (`.metaobjects/AGENTS.md` + `CLAUDE.md`) and the five
6
+ `metaobjects-*` Claude skills (each a universal `SKILL.md` plus the
7
+ `references/<token>.md` fragments matching the project's resolved stack).
8
+
9
+ - `servers/<lang>.meta.json` — per-server install + codegen command (drives the always-on).
10
+ - `templates/always-on.md.mustache` — the slim always-on body (`{{stackLine}}`, `{{codegenCommand}}`).
11
+ - `skills/<skill>/SKILL.md` — universal skill body.
12
+ - `skills/<skill>/references/<token>.md` — language fragment; installed iff `<token>` is in the stack.
13
+
14
+ Design: `docs/superpowers/specs/2026-06-02-downstream-agent-context-design.md`.
@@ -0,0 +1,5 @@
1
+ {
2
+ "displayName": "C#",
3
+ "install": "install the MetaObjects .NET tool (dotnet tool install) + add the MetaObjects.Codegen package",
4
+ "codegenCommand": "dotnet meta gen"
5
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "displayName": "Java",
3
+ "install": "add com.metaobjects:metaobjects-metadata + metaobjects-codegen-spring + metaobjects-maven-plugin to your pom.xml",
4
+ "codegenCommand": "mvn metaobjects:generate"
5
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "displayName": "Kotlin",
3
+ "install": "add com.metaobjects:metaobjects-codegen-kotlin + metaobjects-metadata-ktx + metaobjects-maven-plugin to your pom.xml",
4
+ "codegenCommand": "mvn metaobjects:generate"
5
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "displayName": "Python",
3
+ "install": "pip install metaobjects",
4
+ "codegenCommand": "metaobjects gen"
5
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "displayName": "TypeScript",
3
+ "install": "npm install -D @metaobjectsdev/cli @metaobjectsdev/codegen-ts",
4
+ "codegenCommand": "npx meta gen"
5
+ }
@@ -0,0 +1,301 @@
1
+ ---
2
+ name: metaobjects-authoring
3
+ description: Use when authoring or modifying MetaObjects metadata — fields, entities, relationships, sources, enums, abstracts/inheritance — in YAML or canonical JSON.
4
+ ---
5
+
6
+ # Authoring MetaObjects metadata
7
+
8
+ MetaObjects metadata is the durable spine of your app: typed entity declarations
9
+ that drive code generation, runtime behavior, and drift detection. You author it,
10
+ the loader reads it, the codegen emits idiomatic per-language code from it. This
11
+ skill is the procedure for writing it correctly.
12
+
13
+ Metadata lives in files under `metaobjects/` at project root, one file per domain
14
+ concept (`meta.commerce.json`, `meta.users.yaml`, …). Each file declares a
15
+ `package` on its root node. Files in the same `package` with the same object
16
+ `name` are merged by the loader.
17
+
18
+ Two on-disk formats, one shape:
19
+
20
+ - **Canonical JSON** — the on-disk interchange. Every node is a single-key map
21
+ whose key fuses the type and subtype.
22
+ - **YAML** — the sigil-free authoring front-end. Lowered to canonical JSON at load
23
+ time, so it shares the entire downstream pipeline.
24
+
25
+ Author in whichever fits the project. Prefer YAML for new hand-authored metadata
26
+ (it's less noisy); JSON is the format conformance fixtures and tooling pin.
27
+
28
+ ## The fused-key encoding (non-negotiable)
29
+
30
+ Every node is `{ "<type>.<subType>": { <body> } }`. The wrapper key fuses type and
31
+ subtype — there is **no** separate `subType` body key.
32
+
33
+ ```json
34
+ { "object.entity": { "name": "User" } }
35
+ { "field.string": { "name": "email", "@required": true } }
36
+ { "field.enum": { "name": "status", "@values": ["OPEN", "CLOSED"] } }
37
+ { "identity.primary": { "@fields": ["id"] } }
38
+ ```
39
+
40
+ A complete entity in canonical JSON:
41
+
42
+ ```json
43
+ {
44
+ "metadata.root": {
45
+ "package": "acme::blog",
46
+ "children": [
47
+ {
48
+ "object.entity": {
49
+ "name": "Author",
50
+ "children": [
51
+ { "source.rdb": { "@table": "authors" } },
52
+ { "field.long": { "name": "id" } },
53
+ { "field.string": { "name": "name", "@required": true, "@maxLength": 200 } },
54
+ { "field.string": { "name": "bio", "@maxLength": 2000 } },
55
+ { "identity.primary": { "@fields": ["id"], "@generation": "increment" } }
56
+ ]
57
+ }
58
+ }
59
+ ]
60
+ }
61
+ }
62
+ ```
63
+
64
+ The same entity in sigil-free YAML:
65
+
66
+ ```yaml
67
+ metadata:
68
+ package: acme::blog
69
+ children:
70
+ - object.entity:
71
+ name: Author
72
+ children:
73
+ - source.rdb: { table: authors }
74
+ - field.long: { name: id }
75
+ - field.string: { name: name, required: true, maxLength: 200 }
76
+ - field.string: { name: bio, maxLength: 2000 }
77
+ - identity.primary: { fields: id, generation: increment }
78
+ ```
79
+
80
+ ## Reserved structural keys vs. attributes
81
+
82
+ There is one closed set of **reserved structural keys**. Everything else is an
83
+ attribute.
84
+
85
+ ```
86
+ name package extends abstract overlay isArray children value
87
+ ```
88
+
89
+ - In **canonical JSON**: reserved keys are bare (`"name"`, `"extends"`); every
90
+ other key is `@`-prefixed (`"@required"`, `"@maxLength"`, `"@table"`).
91
+ - In **YAML**: reserved keys are bare AND attributes are bare too — the desugar
92
+ re-adds the `@` when lowering.
93
+ - `@`-prefixing a reserved word (e.g. `"@isArray": true`) is invalid and fails the
94
+ load with `ERR_RESERVED_ATTR`. Use the bare `isArray: true` (YAML) or the `[]`
95
+ key-suffix sugar (`field.long[]: weekIds`).
96
+
97
+ ## Two violation rules — internalize these
98
+
99
+ 1. **Attribute-name uniqueness within a node.** A node body must not declare the
100
+ same attribute name twice. `{ "field.string": { "name": "x", "@maxLength": 10,
101
+ "@maxLength": 20 } }` is malformed.
102
+
103
+ 2. **An inline `@attr` IS an `attr` child — never both.** An inline attribute and
104
+ a child `attr.*` node with the same name are the same slot expressed two ways.
105
+ Declare a given attribute once, in one form. Don't set `@required` inline AND
106
+ also add an `attr.boolean` child named `required` — that's a double-declaration.
107
+
108
+ ## Field subtypes (closed vocabulary)
109
+
110
+ | Subtype | Stores | Notes |
111
+ |---|---|---|
112
+ | `field.string` | text | `@maxLength` drives `varchar(N)` |
113
+ | `field.int` | 32-bit integer | |
114
+ | `field.long` | 64-bit integer | |
115
+ | `field.double` | float | |
116
+ | `field.boolean` | true/false | |
117
+ | `field.date` | calendar date | ISO 8601 `YYYY-MM-DD` on the wire |
118
+ | `field.timestamp` | instant | ISO 8601 with timezone on the wire |
119
+ | `field.decimal` | exact decimal | `@precision` / `@scale`; lossless money/quantity |
120
+ | `field.currency` | integer minor units | see Currency below |
121
+ | `field.enum` | string member | `@values` required; see Enum below |
122
+ | `field.uuid` | UUID | canonical lowercase hex on the wire |
123
+ | `field.object` | embedded value object | `@objectRef` + `@storage`; see below |
124
+
125
+ Common field attributes: `@required`, `@maxLength`, `@column` (physical column
126
+ name), `@default`, `@filterable`, `@sortable`.
127
+
128
+ ### Currency
129
+
130
+ `field.currency` stores money as **integer minor units** (cents for USD, yen for
131
+ JPY) — never a float. `@currency` is ISO 4217; `@locale` (on a `view.currency`
132
+ child) is BCP 47. The server never formats currency; formatting is client-side.
133
+
134
+ ```json
135
+ { "field.currency": {
136
+ "name": "priceCents", "@currency": "USD", "@required": true,
137
+ "children": [ { "view.currency": { "@locale": "en-US" } } ]
138
+ }}
139
+ ```
140
+
141
+ ### Enum
142
+
143
+ `field.enum` is string-backed. `@values` is **required**: a non-empty set of
144
+ unique members, each matching `^[A-Za-z_][A-Za-z0-9_]*$`. Missing `@values` →
145
+ `ERR_MISSING_REQUIRED_ATTR`; a bad member → `ERR_BAD_ATTR_VALUE`.
146
+
147
+ ```json
148
+ { "field.enum": { "name": "status", "@required": true,
149
+ "@values": ["DRAFT", "PUBLISHED", "ARCHIVED"] } }
150
+ ```
151
+
152
+ Reuse a constraint set across entities with an abstract `field.enum` + `extends`.
153
+
154
+ ### Embedded value objects — `field.object` + `@storage`
155
+
156
+ `field.object` embeds another `object` declaration. `@objectRef` names it;
157
+ `@storage` controls persistence:
158
+
159
+ - `flattened` — one DB column per sub-field (`address_street`, `address_city`, …).
160
+ Illegal on array fields.
161
+ - `jsonb` — one `jsonb` column.
162
+ - `subdocument` (default, back-compat) — single jsonb column.
163
+
164
+ ```json
165
+ { "field.object": { "name": "address", "@objectRef": "Address", "@storage": "flattened" } }
166
+ ```
167
+
168
+ ## YAML sigil-free authoring + the coercion footgun
169
+
170
+ In YAML, write the fused `type.subType` key with a **map body**, bare reserved
171
+ keys, bare attributes. Two house-style rules:
172
+
173
+ 1. **Always write the explicit `type.subType`** (`field.string`, not `field`).
174
+ Defaults change; the explicit form survives registry edits.
175
+
176
+ 2. **Quote any scalar that looks like a boolean, number, date, or null.** YAML
177
+ silently coerces unquoted `yes` / `no` / `on` / `off` to booleans and bare
178
+ `2026-05-25` to a date. The loader's coercion guard rejects a coerced value in
179
+ a slot that declares a different type (`ERR_YAML_COERCION`) — but quoting is how
180
+ you *prevent* the surprise. Enum members are the classic trap:
181
+
182
+ ```yaml
183
+ # Rejected — Y and N coerce to booleans
184
+ field.enum: { name: flag, values: [Y, N] }
185
+ # Correct — quote domain-data members
186
+ field.enum: { name: flag, values: ["Y", "N"] }
187
+ ```
188
+
189
+ The `[]` key-suffix declares an array field: `field.long[]: weekIds` lowers to
190
+ `{ "field.long": { "name": "weekIds", "isArray": true } }`.
191
+
192
+ ## Identities
193
+
194
+ | Subtype | Purpose | Key attrs |
195
+ |---|---|---|
196
+ | `identity.primary` | the PK field(s) | `@fields`, `@generation` |
197
+ | `identity.secondary` | a unique secondary index | `@fields` |
198
+ | `identity.reference` | an inbound FK from this entity to another | `@fields`, `@references`, `@enforce` |
199
+
200
+ `@generation` on a primary controls value generation (e.g. `increment`).
201
+ `@fields` accepts a single string in authoring; it normalizes to an array in
202
+ canonical JSON. `@enforce` on a reference (default `true`) controls whether the
203
+ backend physically enforces it (a SQL FK constraint); set `false` for a logical
204
+ reference for navigation/typing/codegen only. Referential actions
205
+ (`@onDelete`/`@onUpdate`) are NOT on `identity.reference` — they live on the
206
+ `relationship.*` node (see Relationships below).
207
+
208
+ ```json
209
+ { "identity.primary": { "@fields": ["id"], "@generation": "increment" } }
210
+ { "identity.secondary": { "@fields": ["email"] } }
211
+ { "identity.reference": { "name": "fkAuthor", "@fields": ["authorId"], "@references": "Author", "@enforce": true } }
212
+ ```
213
+
214
+ ## Relationships
215
+
216
+ `relationship.composition` is the "this entity owns / aggregates instances of
217
+ that entity" side; `identity.reference` (above) is the FK-column side. They are
218
+ the two halves of one FK.
219
+
220
+ | Attr | On | Values |
221
+ |---|---|---|
222
+ | `@objectRef` | composition | target entity name |
223
+ | `@cardinality` | composition | `one` / `many` |
224
+ | `@onDelete` / `@onUpdate` | `relationship.*` only | `cascade` / `set-null` / `restrict` / `no-action` |
225
+
226
+ ```json
227
+ { "relationship.composition": {
228
+ "name": "posts", "@objectRef": "Post",
229
+ "@cardinality": "many", "@onDelete": "cascade" } }
230
+ ```
231
+
232
+ ## Sources — `source.rdb` + `@kind`
233
+
234
+ `source.rdb` declares where an entity's data lives. Read-only-ness derives from
235
+ `@kind` (it is NOT a separate subtype):
236
+
237
+ | `@kind` | Read-only | Default? |
238
+ |---|---|---|
239
+ | `table` | no | yes (when `@kind` omitted) |
240
+ | `view` | yes | – |
241
+ | `materializedView` | yes | – |
242
+ | `storedProc` | yes | – |
243
+ | `tableFunction` | yes | – |
244
+
245
+ The physical name is `@table` (NOT `@name`). The physical column name on a field
246
+ is `@column`. `@schema` namespaces the DB schema (Postgres default `public`;
247
+ SQLite rejects non-default values). Multi-source: multiple `source.rdb` children,
248
+ each with a `@role`, exactly one `primary`.
249
+
250
+ ```json
251
+ { "source.rdb": { "@kind": "view", "@table": "v_author", "@schema": "blog" } }
252
+ ```
253
+
254
+ A `view`-kind entity's fields carry `origin.*` children (`passthrough` /
255
+ `aggregate` / `collection`) declaring where each value comes from.
256
+
257
+ ## Abstracts + `extends` (deferred resolution) + `overlay`
258
+
259
+ An **abstract** node (`abstract: true`) describes a shape but is never emitted as
260
+ a concrete entity. A concrete node references it via `extends:` to inherit its
261
+ children + attrs. This is the lightest reuse mechanism — pure data, no codegen
262
+ change.
263
+
264
+ ```yaml
265
+ - object.entity:
266
+ name: BaseEntity
267
+ abstract: true
268
+ children:
269
+ - field.long: { name: id }
270
+ - field.timestamp: { name: createdAt, required: true }
271
+
272
+ - object.entity:
273
+ name: Author
274
+ extends: BaseEntity
275
+ children:
276
+ - source.rdb: { table: authors }
277
+ - field.string: { name: name, required: true }
278
+ - identity.primary: { fields: id }
279
+ ```
280
+
281
+ Resolution facts:
282
+
283
+ - **Deferred.** `extends:` resolves *after all files load* — abstracts can live in
284
+ any file, forward references are fine.
285
+ - **Multi-level chains flatten** (`Author extends BaseEntity extends Auditable`).
286
+ - **Cross-package** refs use the fully-qualified name (`extends: "shared::auditable"`);
287
+ same-package refs use the bare name.
288
+ - An unresolved reference fails with `ERR_UNKNOWN_EXTENDS`.
289
+
290
+ `abstract` and `extends` are **structural keys** (bare, no `@`).
291
+
292
+ **`overlay` is a different concept.** `extends:` is an IS-A relationship between
293
+ two distinct nodes. `overlay: true` re-opens the *same* named node to amend it
294
+ across files (same `package` + same `name` → merged; last-writer-wins on attr
295
+ conflicts, structural children accumulate). Use `extends` to share shape between
296
+ distinct entities; use `overlay` to split one entity's declaration across files.
297
+
298
+ ---
299
+
300
+ For non-trivial schema design, use `/superpowers:brainstorming` if installed;
301
+ otherwise proceed.
@@ -0,0 +1,99 @@
1
+ ---
2
+ name: metaobjects-codegen
3
+ description: Use when configuring or running MetaObjects code generation: generators/targets/dialect config, the gen command, and hand-edit-preserving regeneration.
4
+ ---
5
+
6
+ # MetaObjects code generation
7
+
8
+ Codegen is the first pillar: MetaObjects reads your typed metadata and emits
9
+ **idiomatic per-language code** — entity types, DB tables/schemas, query helpers,
10
+ REST routes, validators, payload value-objects, output parsers. The metadata is the
11
+ durable spine; the generated code is a disposable artifact. It runs at runtime
12
+ **without any MetaObjects dependency** — if the libraries disappeared tomorrow, you
13
+ keep working code.
14
+
15
+ This skill is the port-agnostic procedure. The exact config file, generator names,
16
+ and command for *this* project's server language live in a reference fragment
17
+ (pointed to at the bottom).
18
+
19
+ ## What codegen does
20
+
21
+ You run a `gen` step. The runner:
22
+
23
+ 1. Loads all metadata under `metaobjects/` (the same loader the runtime uses).
24
+ 2. Resolves output targets and precomputes shared render state.
25
+ 3. Runs each configured **generator** — most emit one file per entity; some emit a
26
+ single shared file (a barrel, a DB-context, an app-config).
27
+ 4. Refuses to overwrite any file that does NOT carry the `@generated` header;
28
+ overwrites the ones that do.
29
+
30
+ The output is normal idiomatic code in your language — you import it and use it
31
+ like any hand-written module.
32
+
33
+ ## The `@generated` header + hand-edit-preserving regen
34
+
35
+ Every emitted file carries a `@generated` header. This is load-bearing:
36
+
37
+ - **Never hand-edit a file with a `@generated` header for a change you want to
38
+ keep.** The next `gen` run overwrites it. If you need different output, change
39
+ the metadata (or the template), not the generated file.
40
+ - **Hand-written regions are preserved by three-way merge.** Where the codegen
41
+ supports designated hand-editable regions, regeneration runs a three-way merge
42
+ (base → yours → newly-generated) so your edits survive a regen. Code review is
43
+ the backstop: a diff on a `@generated` file that wasn't produced by `gen` is a
44
+ smell.
45
+
46
+ Practical rule: **pattern-derivable-from-metadata = regenerate; business logic =
47
+ hand-write in a non-generated file.** FK columns, CRUD, validator chains,
48
+ type-safe finders, `relations()` blocks — all derived, never hand-coded. Custom
49
+ SQL views, regex from outside metadata, and domain logic are what you hand-write.
50
+
51
+ ## Selecting generators by stable name
52
+
53
+ Codegen is a set of named generators you opt into. Each generator has a **stable
54
+ name** (kebab-case) that surfaces in diagnostics — reference generators by that
55
+ name, never by inlining what they emit. Typical generators cover: the entity
56
+ type/model, the DB table/schema, query/finder helpers, REST routes, client
57
+ form/grid/hook artifacts, filter + sort allowlists, payload value-objects, and
58
+ `template.output` parsers. You enable the subset your project needs; an abstract
59
+ entity never emits instance/write artifacts regardless.
60
+
61
+ Per-entity opt-outs exist (e.g. skipping client-side artifacts for a given
62
+ entity) and are set as attributes on the entity in metadata, not in code.
63
+
64
+ ## Dialects
65
+
66
+ Generated DB schema/DDL targets a SQL **dialect**:
67
+
68
+ - `postgres` — the default, fullest-featured.
69
+ - `sqlite` — supported; rejects non-default DB schemas.
70
+ - `d1` (Cloudflare D1) — **TypeScript-only**. It is SQLite at the SQL level; the
71
+ non-TS server ports have no analogue, so it never appears in their config.
72
+
73
+ Set the dialect once in the project's codegen config. Field subtypes map to the
74
+ dialect's column types deterministically (`field.string` + `@maxLength` →
75
+ `varchar(N)`, `field.currency` → integer, `field.uuid` → native `uuid` on
76
+ Postgres, `field.enum` → `varchar` + `CHECK`, etc.).
77
+
78
+ ## Per-target output
79
+
80
+ Generated code can be routed to **multiple output directories/packages** so each
81
+ artifact lands with its runtime concern: the entity model in a database package,
82
+ routes in the API app, client hooks/forms/grids in the web app. Each generator can
83
+ declare which named target it writes to; same-target references stay relative,
84
+ cross-target references go through the target's configured import base. With no
85
+ targets configured, everything lands in a single output directory — output is
86
+ byte-identical to the single-directory case. Use multiple targets only when the
87
+ project's package boundaries justify it.
88
+
89
+ ## Running gen
90
+
91
+ The shape is always the same — a `gen` verb that loads metadata, renders, merges,
92
+ and writes — but the binary differs per server language (the Node `meta`, a
93
+ language-native console tool, or a build-plugin goal). A dry-run mode previews
94
+ without writing; a watch mode re-runs on metadata changes where supported. Pass
95
+ specific entity names to scope a run to those entities.
96
+
97
+ ---
98
+
99
+ For this project's server-language codegen specifics, read every `references/*.md` file in this skill's directory (one per server language in this project's stack).
@@ -0,0 +1,87 @@
1
+ # C# codegen specifics
2
+
3
+ The C# port targets .NET consumers (EF Core + ASP.NET). Codegen runs through the
4
+ **`dotnet meta` .NET tool** — there is no Maven plugin and the Node `meta` binary
5
+ is **schema-migrations only** on the C# side (ADR-0015): `meta migrate` /
6
+ `meta verify --db` are Node-`meta`-owned; everything below is `dotnet meta`.
7
+
8
+ ## Install
9
+
10
+ Per the always-on descriptor:
11
+
12
+ ```bash
13
+ dotnet tool install --global MetaObjects.Cli # provides `dotnet meta`
14
+ dotnet add package MetaObjects.Codegen # the codegen generators
15
+ ```
16
+
17
+ `dotnet meta` is a .NET tool invoked as `dotnet meta <command>` (the underlying
18
+ command is `dotnet-meta`).
19
+
20
+ ## Run
21
+
22
+ ```bash
23
+ dotnet meta gen \
24
+ --metadata-dir metaobjects \
25
+ --output-dir Generated \
26
+ --namespace Acme.Generated
27
+ dotnet meta gen --list # list registered generators
28
+ dotnet meta gen --generators entity,db-context,routes # select a subset
29
+ dotnet meta verify --codegen # codegen-drift gate (regenerate + diff vs committed)
30
+ ```
31
+
32
+ `dotnet meta verify` defaults to `--templates` (the FR-004 prompt/template drift
33
+ gate, see the prompts reference); `--codegen` is the codegen-output drift gate.
34
+ **Schema migration + live-DB drift are NOT `dotnet meta`** — they run through the
35
+ Node `meta` tool (see the migration reference).
36
+
37
+ ## `MetaObjects.Codegen` generators
38
+
39
+ Wire generators by their stable name (`dotnet meta gen --generators <names>`),
40
+ or run the default set. Output lands under `--namespace` in `--output-dir`.
41
+
42
+ | Stable name | Output |
43
+ |---|---|
44
+ | `entity` | `<Entity>.g.cs` — an EF Core entity class per `object.entity` / projection: PascalCase props mapped via `[Column]`, `[Table]`, `[Key]` (or class-level `[PrimaryKey]` for composites), `[MaxLength]`/validators, nullability from `@required`. Enum fields → a nested (or shared) C# `enum`; object fields → owned-type navigations; value objects → POCOs. A TPH `@discriminator` base is emitted `abstract` with `: Base` subtypes (single-table). |
45
+ | `db-context` | one `AppDbContext` — a `DbSet<T>` per entity + `OnModelCreating`: `.HasConversion<string>()` (enums), `.OwnsOne(...)`/`.ToJson(...)` (owned/jsonb object fields), `.HasPrecision(p,s)` (decimals), `.UsingEntity<>(...)` (M:N), `.HasDiscriminator(e => e.Type).HasValue<Sub>(...)` (TPH). |
46
+ | `routes` | `<Entity>Routes.cs` — ASP.NET **Minimal API** CRUD per writable entity (`source.rdb @kind="table"`) on the cross-port REST contract (`?filter[field][op]=`, `?sort=field:asc`, `?limit`/`?offset`, `?withCount=1` envelope, 400/404 envelopes). A TPH base emits polymorphic `GET /<base>(+/{id})` + a per-subtype CRUD set at `/<base>/<discriminatorValue lowercased>` (create injects the discriminator, cross-subtype get/update/delete → 404). |
47
+ | `filter-allowlist` | per-entity `<Entity>FilterAllowlist` (FR-009 — the server-side field+operator allowlist the routes validate against). |
48
+ | `callable` | `<Entity>.callable.g.cs` — an FR-015 calling method for a `source.rdb @kind="storedProc"|"tableFunction"`, via EF `FromSqlInterpolated` (args from the `@parameterRef` value object in declaration order). |
49
+ | `output-parser` / `extractor` / `output-prompt` / `render-helper` | the `template.output` prompt-pillar artifacts (strict parser, tolerant `extract`, output-format prompt fragment, typed render helper) — see the **prompts** reference. |
50
+ | `template` | the generic Mustache `templateGenerator()` primitive. |
51
+
52
+ Metadata lives under `metaobjects/` (or wherever you point `--metadata-dir`) in the
53
+ same canonical JSON every port reads — fused-key form, `source.rdb` + `@table`,
54
+ `@column` for a renamed physical column.
55
+
56
+ ## Persistence + routes are the deployed artifact
57
+
58
+ C# generates a *complete* server stack: the entity classes + `AppDbContext` ARE the
59
+ persistence layer (EF Core), and the minimal-API routes mount on your `WebApplication`.
60
+ There is no runtime "ObjectManager" layer to wire — the generated EF Core code is
61
+ what runs. (The other ports leave a repository seam; C# does not.)
62
+
63
+ ## Extending the generators (open-for-extension, ADR-0002)
64
+
65
+ The generators are subclassable: the per-class emit methods are `protected virtual`,
66
+ plus finer hooks — `EmitClassHeader` / `EmitClassDeclarationLine` (class declaration:
67
+ `partial`, marker interfaces), `EmitPropertyAttributes` (per-property C# attributes),
68
+ `EmitFileUsings` (extra usings), `EmitClassBodyTrailer` (extra members). Subclass a
69
+ generator and override only the seam you need rather than forking. `@default` on a
70
+ scalar emits a literal initializer; an `object.value`'s default storage is jsonb.
71
+
72
+ **Shared + externally-provided enums (FR-019).** A package-level abstract
73
+ `field.enum` (`abstract: true`, `@values`) extended by concrete entity fields is
74
+ materialized **once** (`Enums.g.cs`) and referenced — no per-entity nested enum.
75
+ Adding `@provided: true` to that declaration suppresses materialization entirely:
76
+ consuming fields reference a hand-written/third-party enum, and the C# namespace
77
+ **binds to the enum's declaring metadata package** via
78
+ `GenConfig.PackageNamespaces["<pkg>"] = "Your.Namespace"` (one entry per namespace;
79
+ `ProvidedEnumNamespace` is the single fallback). The `@values` still drive the DB
80
+ `CHECK` + validation. This replaces the retired C#-only `@csEnumType` FQN attr
81
+ (ADR-0026) — no language FQN ever lives in metadata (ADR-0001).
82
+
83
+ ## Re-scaffold this context
84
+
85
+ `dotnet meta agent-docs --server csharp [--out <dir>]` (re)scaffolds the slim
86
+ always-on Markdown + these `metaobjects-*` skills into the project — the C# tool
87
+ bundles the agent-context tree, so a C# consumer needs no Node `meta`.