@metaobjectsdev/sdk 0.23.2 → 0.24.1

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 (75) hide show
  1. package/agent-context/skills/metaobjects-audit/SKILL.md +20 -7
  2. package/agent-context/skills/metaobjects-audit/references/capability-checklist.md +65 -19
  3. package/agent-context/skills/metaobjects-audit/references/csharp.md +2 -1
  4. package/agent-context/skills/metaobjects-audit/references/java.md +3 -2
  5. package/agent-context/skills/metaobjects-audit/references/kotlin.md +2 -1
  6. package/agent-context/skills/metaobjects-audit/references/requirements.md +3 -4
  7. package/agent-context/skills/metaobjects-audit/references/typescript.md +1 -1
  8. package/agent-context/skills/metaobjects-authoring/SKILL.md +53 -10
  9. package/agent-context/skills/metaobjects-authoring/references/requirements.md +49 -23
  10. package/agent-context/skills/metaobjects-codegen/SKILL.md +4 -3
  11. package/agent-context/skills/metaobjects-codegen/references/csharp.md +12 -2
  12. package/agent-context/skills/metaobjects-codegen/references/java.md +3 -3
  13. package/agent-context/skills/metaobjects-codegen/references/kotlin.md +4 -4
  14. package/agent-context/skills/metaobjects-codegen/references/python.md +12 -2
  15. package/agent-context/skills/metaobjects-codegen/references/typescript.md +14 -3
  16. package/agent-context/skills/metaobjects-fit-assessment/SKILL.md +4 -4
  17. package/agent-context/skills/metaobjects-prompts/SKILL.md +53 -32
  18. package/agent-context/skills/metaobjects-prompts/references/csharp.md +26 -21
  19. package/agent-context/skills/metaobjects-prompts/references/java.md +24 -18
  20. package/agent-context/skills/metaobjects-prompts/references/kotlin.md +25 -20
  21. package/agent-context/skills/metaobjects-prompts/references/python.md +68 -26
  22. package/agent-context/skills/metaobjects-prompts/references/typescript.md +29 -23
  23. package/agent-context/skills/metaobjects-verify/references/migration.md +1 -1
  24. package/agent-context/skills/metaobjects-verify/references/requirements.md +36 -22
  25. package/agent-context/templates/always-on.md.mustache +3 -1
  26. package/dist/agent-docs/body.d.ts +1 -1
  27. package/dist/agent-docs/body.d.ts.map +1 -1
  28. package/dist/agent-docs/body.js +24 -2
  29. package/dist/agent-docs/body.js.map +1 -1
  30. package/dist/collection.d.ts +71 -0
  31. package/dist/collection.d.ts.map +1 -0
  32. package/dist/collection.js +203 -0
  33. package/dist/collection.js.map +1 -0
  34. package/dist/config.d.ts +51 -21
  35. package/dist/config.d.ts.map +1 -1
  36. package/dist/config.js +76 -10
  37. package/dist/config.js.map +1 -1
  38. package/dist/discovery.d.ts +43 -0
  39. package/dist/discovery.d.ts.map +1 -0
  40. package/dist/discovery.js +100 -0
  41. package/dist/discovery.js.map +1 -0
  42. package/dist/index.d.ts +10 -1
  43. package/dist/index.d.ts.map +1 -1
  44. package/dist/index.js +17 -4
  45. package/dist/index.js.map +1 -1
  46. package/dist/memory.d.ts +42 -19
  47. package/dist/memory.d.ts.map +1 -1
  48. package/dist/memory.js +38 -95
  49. package/dist/memory.js.map +1 -1
  50. package/dist/metadata-files.d.ts +67 -0
  51. package/dist/metadata-files.d.ts.map +1 -0
  52. package/dist/metadata-files.js +155 -0
  53. package/dist/metadata-files.js.map +1 -0
  54. package/dist/records/any.d.ts +8 -8
  55. package/dist/records/decision.d.ts +2 -2
  56. package/dist/records/glossary.d.ts +4 -4
  57. package/dist/records/principle.d.ts +2 -2
  58. package/dist/scope.d.ts +16 -0
  59. package/dist/scope.d.ts.map +1 -0
  60. package/dist/scope.js +75 -0
  61. package/dist/scope.js.map +1 -0
  62. package/dist/sources.d.ts +86 -0
  63. package/dist/sources.d.ts.map +1 -0
  64. package/dist/sources.js +130 -0
  65. package/dist/sources.js.map +1 -0
  66. package/package.json +2 -2
  67. package/src/agent-docs/body.ts +24 -2
  68. package/src/collection.ts +268 -0
  69. package/src/config.ts +80 -12
  70. package/src/discovery.ts +110 -0
  71. package/src/index.ts +26 -9
  72. package/src/memory.ts +65 -99
  73. package/src/metadata-files.ts +158 -0
  74. package/src/scope.ts +95 -0
  75. package/src/sources.ts +160 -0
@@ -114,9 +114,9 @@ All live in `metaobjects-codegen-kotlin` under
114
114
  | `KotlinSpringControllerGenerator` | `<Entity>Controller.kt` — Spring `@RestController`, five CRUD endpoints on the cross-port REST contract, for writable entities (`source.rdb` `@kind="table"`). A TPH `@discriminator` base emits ONE controller: polymorphic `GET /<base>(+/{id})` plus a per-subtype CRUD set at `/<base>/<discriminatorValue lowercased>` — create injects the discriminator from the URL (never the body); get/update/delete are scoped to the subtype (cross-subtype → 404); the discriminator is immutable. |
115
115
  | `KotlinRepositoryGenerator` | `<Entity>RepositoryBase.kt` — an `open class` consumer persistence seam per writable entity (the Kotlin peer of Java's `SpringRepositoryGenerator`, but with method bodies). Carries the #203 `@autoSet` CRUD stamping: `onCreate` columns stamped once at insert, `onUpdate` columns re-stamped on every write, both excluded from the caller-supplied set. |
116
116
  | `KotlinPayloadGenerator` | `<Template>Payload.kt` — `@Serializable` payload data class from a template's `@payloadRef` |
117
- | `KotlinOutputParserGenerator` | the `template.output` strict parser-on-receipt (see the prompts reference) |
118
- | `KotlinExtractorGenerator` | the FR-010 tolerant `extract` mapper for a `template.output` (all-nullable mirror → strict payload) |
119
- | `KotlinOutputPromptGenerator` | the FR-010 output-format prompt fragment for a `template.output` (presentation via `@promptStyle: guide`/`inline`/`exampleOnly`) |
117
+ | `KotlinOutputParserGenerator` | the strict parser-on-receipt for a **responding `template.prompt`** — one carrying `@responseRef` (ADR-0052: INBOUND; a `template.output` emits no parser). See the prompts reference. |
118
+ | `KotlinExtractorGenerator` | the FR-010 tolerant `extract` mapper for a responding `template.prompt` (all-nullable mirror → strict payload) |
119
+ | `KotlinOutputPromptGenerator` | the FR-010 output-format prompt fragment for a responding `template.prompt` (presentation via `@promptStyle: guide`/`inline`/`exampleOnly`) |
120
120
  | `KotlinRenderHelperGenerator` | the typed render helper for a `template.prompt` payload |
121
121
  | `KotlinValidatorGenerator` | `MetadataStartupValidator.kt` + `ExposedTableValidator.kt` (once per project) |
122
122
  | `KotlinSpringConfigGenerator` | `MetadataExposedConfig.kt` — `@Configuration` wiring `Database.connect()` + the startup validator (once per project) |
@@ -129,7 +129,7 @@ wrapper (same column mapping, no write path). The `CREATE VIEW` DDL is emitted b
129
129
  Node `meta migrate` from the projection's `origin.*` children — `origin.passthrough`,
130
130
  `origin.aggregate` (`@agg` `count`/`sum`/`avg`/`min`/`max`, plus the #195 `any`/`all`
131
131
  quantifiers over a `@filter` and `collect` array-rollup with `@distinct`/`@orderBy`),
132
- `origin.collection`, `origin.computed` (`@expr`), `origin.first`; an object-level
132
+ `origin.computed` (`@expr`), `origin.first`; an object-level
133
133
  `@filter` scopes the whole view's rows (#207 — lowers to the outer `WHERE`). Never
134
134
  hand-author the view SQL for a shape origins can express (an unmodeled view is unmanaged
135
135
  and drifts silently); carry a genuinely irreducible body (recursive CTE, window function,
@@ -38,6 +38,16 @@ metaobjects docs ./metadata --out ./docs # → ./docs/api/python (AGENT-API.md
38
38
  `AGENT-API.md` — the exact imports, signatures, and payload field shapes for the
39
39
  generated code. **Before calling any generated code, read `api/python/AGENT-API.md`.**
40
40
 
41
+ **The two `docs` positionals are NOT the same argument.** This one is the METADATA
42
+ directory. The Node `meta docs` positional — used by every stack, since `migrate`,
43
+ `verify --db` and the neutral model docs are Node-only — is the PROJECT ROOT that
44
+ CONTAINS the metadata. Run `meta docs` with no positional, from the project root:
45
+
46
+ ```bash
47
+ metaobjects docs ./metadata --out ./docs # Python: the METADATA dir
48
+ meta docs --out ./docs # Node: run from the PROJECT ROOT (no positional)
49
+ ```
50
+
41
51
  ## Generators
42
52
 
43
53
  Wire generators by their stable name (`--generators <names>`), or run the default set.
@@ -50,13 +60,13 @@ a renamed physical column).
50
60
  | `entity` | one **Pydantic model** per `object.entity` / projection (the `entity-model` generator): typed fields from the metadata, nullability from `@required`, `@maxLength`/validators, enum fields → a Python `Enum`. This is the typed data model. A TPH concrete subtype (`@discriminatorValue`) pins the inherited `@discriminator` field to a `Literal[...]` so the model rejects a foreign-subtype tag. |
51
61
  | `routes` | a **FastAPI `APIRouter`** 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). The router declares a repository **`Protocol`** you implement and inject. A TPH `@discriminator` base emits ONE polymorphic router: `GET /<base>(+/{id})` plus a per-subtype CRUD set at `/<base>/<discriminatorValue lowercased>` — create injects the discriminator from the URL (never the body); get/update/delete scoped to the subtype (cross-subtype → 404); discriminator immutable. Its repository `Protocol` is subtype-keyed (`subtype=None` for the polymorphic base) so your implementation applies the single-table discriminator scope. |
52
62
  | `filter-allowlist` | per-entity filter allowlist (FR-009 — the server-side field+operator allowlist the routes validate against). |
53
- | `payload` / `output-parser` / `output-prompt` / `extractor` / `render-helper` / `trace-helper` | the `template.output` prompt-pillar artifacts the payload VO, the strict parser, the **output-format prompt fragment** (`output-prompt`; presentation via `@promptStyle: guide`/`inline`/`exampleOnly`), the tolerant `extract`, the typed render helper, and the LLM-trace helper. See the **prompts** reference. |
63
+ | `payload` / `output-parser` / `output-prompt` / `extractor` / `render-helper` / `trace-helper` | the prompt-pillar artifacts for a **responding `template.prompt`** — one carrying `@responseRef` (ADR-0052: these tiers are INBOUND; `template.output` is outbound only and emits no parser). The payload VO, the strict parser (`<template>_response_parser`), the **output-format prompt fragment** (`<template>_response_format`; presentation via `@promptStyle: guide`/`inline`/`exampleOnly`), the tolerant `extract`, the typed render helper, and the LLM-trace helper. See the **prompts** reference. |
54
64
  | `template` | the generic Mustache `template` primitive. |
55
65
 
56
66
  **Projections + entity read-views.** An `object.projection` (read-only `source.rdb`
57
67
  `@kind: view` child) gets a read-only Pydantic model from the `entity` generator; its
58
68
  `CREATE VIEW` DDL is emitted by the Node `meta migrate` from the projection's `origin.*`
59
- children (`passthrough` / `aggregate` / `collection` / `computed` / `first`) — never
69
+ children (`passthrough` / `aggregate` / `computed` / `first`) — never
60
70
  hand-write the view SQL for a shape origins can express. An `object.entity` that adds a
61
71
  `@role: replica` `@kind: view` source alongside its writable `table` is a write-through
62
72
  **entity read-view** (#214): the generated read model carries the derived `origin.*`
@@ -67,6 +67,17 @@ block, and the inferred types.
67
67
  A second file, `.metaobjects/config.json`, holds static project state parseable by
68
68
  non-TS tooling; `meta init` scaffolds both plus the `metaobjects/` source dir.
69
69
 
70
+ `sources` in that file is where the metadata lives — `metaobjects/` is only its
71
+ DEFAULT value, so a project can point it anywhere. **Every entry is an OBJECT, never
72
+ a bare string**, and it names a DIRECTORY or a file:
73
+
74
+ ```jsonc
75
+ { "schema_version": 1, "sources": [{ "path": "model" }, { "path": "../shared/metadata" }] }
76
+ ```
77
+
78
+ Every command's directory argument (`meta docs <project-root>`, `--cwd`) is the
79
+ PROJECT ROOT that CONTAINS the metadata — never the metadata directory itself.
80
+
70
81
  ## The generators
71
82
 
72
83
  From `@metaobjectsdev/codegen-ts/generators` (server-side, framework-neutral):
@@ -78,7 +89,7 @@ From `@metaobjectsdev/codegen-ts/generators` (server-side, framework-neutral):
78
89
  | `routesFile()` | `<Entity>.routes.ts` — Fastify CRUD routes on the cross-port REST contract. `routesFileHono()` is the Hono/Workers variant. A TPH `@discriminator` base mounts polymorphic `GET /<base>(+/:id)` plus a per-subtype CRUD set at `<basePath>/<discriminatorValue lowercased>` — create omits the discriminator (the URL names the subtype; the runtime injects it); get/update/delete scoped to the subtype (cross-subtype → 404); discriminator immutable via the runtime `discriminator` option. |
79
90
  | `barrel()` | `index.ts` re-exporting each `<Entity>.ts` (one-shot, not per-entity) |
80
91
  | `promptRender()` | `render<Name>()` per `template.prompt` |
81
- | `outputParser()` | `<Name>.output.ts` (`parse*` / `safeParse*`) per `template.output` |
92
+ | `outputParser()` | `<Name>.response.ts` (`parse*` / `safeParse*`) per **responding `template.prompt`** — one carrying `@responseRef` (ADR-0052: this tier is INBOUND; `template.output` is outbound only and emits nothing here). Siblings: `outputPrompt()` → `<Name>.responseFormat.ts` (the FR-010 output-format fragment, presentation via `@promptStyle`), `extractor()` → `<Name>.extractor.ts` (the tolerant `extract` mapper). |
82
93
  | `callableFile()` | `<Entity>.callable.ts` — an FR-015 `call<Entity>` wrapper for a `source.rdb` `@kind: storedProc`/`tableFunction` (args from the `@parameterRef` value object, in declaration order) |
83
94
 
84
95
  **Projections (read-only views).** For an `object.projection` (a read-only `source.rdb`
@@ -87,8 +98,8 @@ finder (no create/update/delete). The `CREATE VIEW` DDL is generated by `meta mi
87
98
  from the projection's `origin.*` children — `origin.passthrough` (a forwarded column),
88
99
  `origin.aggregate` (`@agg` `count`/`sum`/`avg`/`min`/`max`, plus the #195 `any`/`all`
89
100
  predicate quantifiers over a `@filter` and `collect` array-rollup with optional
90
- `@distinct`/`@orderBy`; any aggregate row-scoped with `@filter`), `origin.collection` (a
91
- nested array), `origin.computed` (a row-level `@expr`), `origin.first` (one related row's
101
+ `@distinct`/`@orderBy`; any aggregate row-scoped with `@filter`),
102
+ `origin.computed` (a row-level `@expr`), `origin.first` (one related row's
92
103
  column along `@via`/`@of`/`@orderBy`). An object-level `@filter` on the projection scopes
93
104
  the whole view's rows (#207 — lowers to the outer `WHERE`, the metadata-managed
94
105
  soft-delete/status view). **Never hand-write the view SQL** for a shape origins can
@@ -28,7 +28,7 @@ cite. (3) Whether anyone fills the ledger in is itself untested; advertising it
28
28
  is the brochure failure this skill exists to avoid.
29
29
 
30
30
  TRIGGER to revisit — the release carrying `requirement.*` has shipped, AND either:
31
- Arm A a dogfooded project's `abandoned`/`superseded` entries are shown, retrospectively,
31
+ Arm A a dogfooded project's retired capabilities are shown, retrospectively,
32
32
  to have been discoverable PRE-adoption from repo evidence at file:line standard
33
33
  (removal commit, dead flag, do-not-reintroduce comment);
34
34
  Arm B a team that adopted via this assessment hits a resurrection the ledger would have
@@ -267,8 +267,8 @@ signature class. Hunt all ten classes:
267
267
  @agg/@of/@via`, row-scoped with `@filter`), a predicate quantifier (`origin.aggregate
268
268
  @agg: any|all`), an array rollup (`origin.aggregate @agg: collect`), a non-aggregate
269
269
  derived scalar (`origin.computed @expr`), an argmax-style "one related row's column"
270
- pick (`origin.first @via` — covers the common `DISTINCT ON` / lateral-join shape), a
271
- child collection (`origin.collection`), a soft-delete/status/type row-scope (an
270
+ pick (`origin.first @via` — covers the common `DISTINCT ON` / lateral-join shape),
271
+ a soft-delete/status/type row-scope (an
272
272
  object-level `@filter` on `object.projection`), or `extends`-borrowed — and joins
273
273
  follow declared relationships/`identity.reference` FKs. Expressible → projection
274
274
  candidate (note: an unmodeled hand view is *unmanaged* — invisible to `verify --db`;
@@ -407,7 +407,7 @@ promising it or counting it in benefits:
407
407
  - hand `COUNT/SUM` subqueries, read-model SQL → `object.projection` + `origin.passthrough`
408
408
  / `origin.aggregate` (`count`/`sum`/`avg`/`min`/`max`, +`@filter` for scoped
409
409
  aggregates, +`any`/`all` predicate quantifiers, +`collect` array rollups) /
410
- `origin.collection` / `origin.computed` (a non-aggregate derived scalar) /
410
+ `origin.computed` (a non-aggregate derived scalar) /
411
411
  `origin.first` (an argmax-style "one related row's column" pick — the usual
412
412
  `DISTINCT ON`/lateral-join case) — plus a soft-delete/status/type view via the
413
413
  object-level `@filter` on `object.projection` instead of a hand `WHERE`.
@@ -19,10 +19,13 @@ language lives in a reference fragment (pointed to at the bottom).
19
19
  A **template** is a typed pair: a logical reference to external text + a payload
20
20
  value-object declaring exactly what data the text expects.
21
21
 
22
- | Subtype | Use | Extra attrs |
23
- |---|---|---|
24
- | `template.prompt` | LLM-targeted | `@maxTokens`, `@requiredSlots`, `@requiredTags`, `@model`, `@responseRef` |
25
- | `template.output` | email / docs / config / export | `@kind: document \| email` (default `document`), `@promptStyle`, `@requiredTags`; `@kind: email` adds `@subjectRef` / `@htmlBodyRef` / `@textBodyRef` |
22
+ A template subtype's axis is **DIRECTION** which way the text travels, not what it
23
+ is about (ADR-0052).
24
+
25
+ | Subtype | Direction | Use | Extra attrs |
26
+ |---|---|---|---|
27
+ | `template.prompt` | outbound, and optionally inbound | LLM-targeted | `@maxTokens`, `@requiredSlots`, `@requiredTags`, `@model`, `@responseRef`, `@responseFormat`, `@promptStyle` |
28
+ | `template.output` | outbound ONLY | email / docs / config / export | `@kind: document \| email` (default `document`), `@requiredTags`; `@kind: email` adds `@subjectRef` / `@htmlBodyRef` / `@textBodyRef` |
26
29
 
27
30
  Both carry the generic attrs:
28
31
 
@@ -36,13 +39,22 @@ Both carry the generic attrs:
36
39
  `template.output @kind: email` renders a structured `EmailDocument` (subject + HTML
37
40
  body + optional plain-text body) instead of one string — the TS render helper emits
38
41
  an `EmailDocument`-returning function for it (see the `render-example-email`
39
- conformance fixture). `@promptStyle` (`guide` / `inline` / `exampleOnly`, FR-010)
40
- selects how the output-format prompt fragment presents the payload shape to an LLM
41
- (see "the output-format prompt fragment" below); `@requiredTags` names output tags
42
- the rendered text must contain (`verify` checks it) on both subtypes.
43
- `template.prompt` additionally carries `@responseRef` naming the response
44
- shape (an `object.value` or sourceless `object.projection`, #210) the prompt
45
- expects, for typed LLM-call trace derivation.
42
+ conformance fixture). `@requiredTags` names output tags the rendered text must contain
43
+ (`verify` checks it) on both subtypes.
44
+
45
+ **The INBOUND half belongs to `template.prompt` alone.** `@responseRef` names the
46
+ response shape (an `object.value` or sourceless `object.projection`, #210) a model's
47
+ reply is parsed into, and its PRESENCE is what asks for the whole inbound tier: the
48
+ response record, the response-format fragment, the parser-on-receipt and the tolerant
49
+ extractor. `@responseFormat` (`json` default / `xml`, ADR-0053) is the syntax of that
50
+ REPLY; `@promptStyle` (`guide` / `inline` / `exampleOnly`, FR-010) selects how the
51
+ fragment presents the shape.
52
+
53
+ > **`@format` and `@responseFormat` are different facts.** `@format` is the syntax of
54
+ > the BODY you render; `@responseFormat` is the syntax of the answer you expect. A
55
+ > plain-text prompt asking for a JSON object is the common case. Putting `@promptStyle`
56
+ > or `@responseFormat` on a `template.output` is a LOAD ERROR — an output renders a
57
+ > document and nothing reads a reply to it.
46
58
 
47
59
  A third, structurally different subtype is also registered core vocabulary:
48
60
  **`template.toolcall`** (`@toolName` + `@payloadRef`, ADR-0011) — a vendor-agnostic
@@ -63,7 +75,7 @@ declared `field.<subType>` + `isArray` + `@objectRef`, and a nested payload is a
63
75
  declared `field.object @objectRef` to another `object.value` (`isArray: true` for a
64
76
  list — nested targets stay value-only, loader-enforced). The caller supplies the
65
77
  field values at render time. An `origin.*` child on a payload field is IGNORED for
66
- typing — and the assembly origins (`aggregate` / `collection` / `computed` /
78
+ typing — and the assembly origins (`aggregate` / `computed` /
67
79
  `first`) are ILLEGAL on an `object.value` host (`ERR_SUBTYPE_RULE_VIOLATION`, #210):
68
80
  an origin-derived payload lives on the sourceless projection, which `@payloadRef`
69
81
  accepts. Projections generally are covered by the `metaobjects-authoring` skill and
@@ -192,33 +204,42 @@ For every template, the verify step resolves the text, parses each `{{...}}`
192
204
  reference, and checks it exists on the payload VO. If the text references
193
205
  `{{authorName}}` but the payload only has `displayName`, **the build fails.** This
194
206
  is the prompt-vs-payload drift gate — run it in CI. It walks both `template.prompt`
195
- and `template.output` nodes the same way.
207
+ and `template.output` nodes the same way (both RENDER; only the direction of what comes
208
+ back differs).
209
+
210
+ ## A RESPONDING `template.prompt` generates a parser-on-receipt
211
+
212
+ For every `template.prompt` declaring `@responseRef`, codegen emits a **typed parser**
213
+ that turns a model's reply back into that shape. It binds `@responseRef`, never
214
+ `@payloadRef` — `@payloadRef` types the request the prompt renders outbound, and the
215
+ question and the answer are usually different shapes. Each port emits the parser
216
+ idiomatically: a throw-on-invalid parse plus, where the language has the precedent, a
217
+ Result-style "safe" variant that doesn't throw.
196
218
 
197
- ## `template.output` also generates a parser-on-receipt
219
+ **A `template.output` gets no parser, ever.** Nothing reads a reply to a document. (Before
220
+ ADR-0052 it did, with no format filter at all — so an `@format: markdown` document got a
221
+ generated `JSON.parse` over rendered prose.)
198
222
 
199
- For every `template.output`, codegen emits a **typed parser** that turns an LLM/raw
200
- response back into the `@payloadRef` value-object the reverse direction, reusing
201
- the same payload VO (no new authoring). Each port emits it idiomatically: a
202
- throw-on-invalid parse plus, where the language has the precedent, a Result-style
203
- "safe" variant that doesn't throw. The parser file is a companion to the payload-VO
204
- file; `verify` catches payload-VO ↔ parser drift at build time too.
223
+ The strict tier is JSON-only: an `@responseFormat: xml` reply gets the tolerant extract
224
+ and nothing strict, because strict all-or-nothing semantics layered over a REPAIRING XML
225
+ reader would raise or accept based on how much repair happened.
205
226
 
206
227
  The three-step consumer pattern is identical everywhere: render the prompt → call
207
- your LLM client → parse the response with the generated parser.
228
+ your LLM client → parse the reply with the generated parser.
208
229
 
209
- ## `template.output` also generates the output-format prompt fragment (FR-010)
230
+ ## A RESPONDING `template.prompt` generates the response-format fragment (FR-010)
210
231
 
211
- For every **json/xml-format** `template.output` whose `@payloadRef` resolves to a
212
- value-object, codegen additionally emits an `output-prompt` artifact: a
213
- `render<Name>Format(...)`-shaped function backed by the render engine's
214
- output-format renderer the "produce your answer like this" instruction fragment
215
- you splice into the prompt text so the model returns exactly the shape the parser
216
- above expects. It's generated only for `json`/`xml` outputs (`text`/`html`/`csv`/
217
- `markdown`/`spreadsheet` don't get a fragment) and skipped under the same
218
- unresolved-`@payloadRef` rule as the parser; the fragment and the parser's
219
- `extract()` codegen agree on the same root name.
232
+ For every `template.prompt` whose `@responseRef` resolves, codegen additionally emits a
233
+ `render<Name>Format(...)`-shaped function backed by the render engine's output-format
234
+ renderer the "produce your answer like this" instruction fragment you splice into the
235
+ prompt text so the model returns exactly the shape the parser above expects. The gate is
236
+ `@responseRef` PRESENCE, not a format value: the old `@format {json,xml}` gate read the
237
+ syntax of the OUTBOUND body to decide whether to instruct the model about the syntax of
238
+ its REPLY, so a text-bodied prompt asking for a JSON answer got no fragment at all.
239
+ `@responseFormat` selects which syntax the fragment teaches; the fragment and the
240
+ extractor agree on the same root name.
220
241
 
221
- `@promptStyle` on the `template.output` controls the fragment's presentation
242
+ `@promptStyle` on the `template.prompt` controls the fragment's presentation
222
243
  (default `guide`):
223
244
 
224
245
  | `@promptStyle` | Presentation |
@@ -1,15 +1,18 @@
1
1
  # C# parser-on-receipt
2
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.
3
+ For every RESPONDING `template.prompt` — one declaring `@responseRef`
4
+ `MetaObjects.Codegen`'s `OutputParserGenerator` emits a **typed parser** that validates
5
+ a model's reply against that shape. ADR-0052: the tier binds `@responseRef`, never
6
+ `@payloadRef` (which types the request the prompt renders outbound), and a
7
+ `template.output` gets no parser at all. This is the receive side only codegen emits
8
+ **no** provider/LLM-call layer; you compose the call yourself. C# names records after the
9
+ resolved VALUE OBJECT, so the response record simply IS that VO's record — no second
10
+ naming convention, and the parser and the record can't silently drift.
8
11
 
9
12
  ## Contents
10
13
  - Wire the generator
11
14
  - What it emits
12
- - The output-format prompt fragment (FR-010)
15
+ - The response-format prompt fragment (FR-010)
13
16
  - The three-step consumer pattern
14
17
  - Recommended LLM caller (bring-your-own)
15
18
  - Consumer dependency
@@ -27,12 +30,14 @@ dotnet meta gen ./metadata --out ./Generated --namespace Acme.Blog
27
30
 
28
31
  ## What it emits
29
32
 
30
- Per `template.output`, `dotnet meta gen` writes one `<TemplateName>.output.cs` with a
31
- static `<TemplateName>Parser` following the .NET BCL `Parse`/`TryParse` dual API —
33
+ Per responding `template.prompt`, `dotnet meta gen` writes one
34
+ `<PromptName>.response.cs` with a static `<PromptName>Parser` following the .NET BCL
35
+ `Parse`/`TryParse` dual API. The strict tier is JSON-only — an `@responseFormat: xml`
36
+ reply gets the tolerant extract and neither `Parse` nor `TryParse` —
32
37
  `Parse` throws, `TryParse` returns a bool plus an out-error:
33
38
 
34
39
  ```csharp
35
- // generated <TemplateName>.output.cs (shape)
40
+ // generated <PromptName>.response.cs (shape)
36
41
  public static class NpcResponseParser
37
42
  {
38
43
  /// <exception cref="JsonException">malformed JSON or schema mismatch.</exception>
@@ -53,25 +58,25 @@ generator also emits a tolerant `Extract(string[, ExtractOptions])` (self-contai
53
58
  components) returning an `ExtractionResult` with a nullable `<Payload>Extracted` mirror
54
59
  — a classified per-field report rather than a throw.
55
60
 
56
- ## The output-format prompt fragment (FR-010)
61
+ ## The response-format prompt fragment (FR-010)
57
62
 
58
- For every json/xml-format `template.output`, `MetaObjects.Codegen`'s
59
- `OutputPromptGenerator` (stable name `output-prompt-generator`) emits a
60
- `<TemplateName>.prompt.cs` declaring a static `<TemplateName>Prompt` class with a
61
- `RenderFormat()` / `RenderFormat(PromptOverrides)` pair, backed by the render
62
- engine's `OutputFormatRenderer` — the "produce your answer like this" fragment for
63
- the model. It runs as part of the same `dotnet meta gen` invocation as the payload
63
+ For every responding `template.prompt`, `MetaObjects.Codegen`'s `OutputPromptGenerator`
64
+ (stable name `output-prompt-generator`) emits a `<PromptName>.responseFormat.cs`
65
+ declaring a static `<PromptName>ResponseFormat` class with a `RenderFormat()` /
66
+ `RenderFormat(PromptOverrides)` pair, backed by the render engine's
67
+ `OutputFormatRenderer` — the "produce your answer like this" fragment for the model. It runs as part of the same `dotnet meta gen` invocation as the payload
64
68
  and parser generators:
65
69
 
66
70
  ```bash
67
71
  dotnet meta gen ./metadata --out ./Generated --namespace Acme.Blog
68
72
  ```
69
73
 
70
- `@promptStyle` on the `template.output` (`guide` default / `inline` / `exampleOnly`)
71
- controls the fragment's presentation; guidance is never emitted as comments. Skipped
72
- for `template.prompt` nodes, non-json/xml `@format`, and an unresolved
73
- `@payloadRef` the same skip contract as the parser generator. The baked spec's
74
- root name is the payload class name, agreeing with the parser's root.
74
+ `@promptStyle` on the `template.prompt` (`guide` default / `inline` / `exampleOnly`)
75
+ controls the fragment's presentation; guidance is never emitted as comments. Skipped for
76
+ `template.output` nodes and an unresolved `@responseRef` the same skip contract as the
77
+ parser generator. There is NO format gate: the old `@format {json,xml}` test read the
78
+ syntax of the outbound body to decide whether to describe the reply. The baked spec's
79
+ root name is the response record's, agreeing with the parser's root.
75
80
 
76
81
  ## The three-step consumer pattern
77
82
 
@@ -1,14 +1,16 @@
1
1
  # Java parser-on-receipt
2
2
 
3
- For every `template.output`, `codegen-spring`'s `SpringOutputParserGenerator` emits
4
- a **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.
3
+ For every RESPONDING `template.prompt` — one declaring `@responseRef`
4
+ `codegen-spring`'s `SpringOutputParserGenerator` emits a **typed parser** that validates
5
+ a model's reply against that shape. ADR-0052: the tier binds `@responseRef`, never
6
+ `@payloadRef` (which types the request the prompt renders outbound), and a
7
+ `template.output` gets no parser at all. This is the receive side only — codegen emits
8
+ **no** provider/LLM-call layer; you compose the call yourself.
7
9
 
8
10
  ## Contents
9
11
  - Wire the generator
10
12
  - What it emits
11
- - The output-format prompt fragment (FR-010)
13
+ - The response-format prompt fragment (FR-010)
12
14
  - The three-step consumer pattern
13
15
  - Recommended LLM caller (bring-your-own)
14
16
  - Drift gate
@@ -31,17 +33,20 @@ the payload record it parses into) to the Maven plugin's `<generators>` list:
31
33
 
32
34
  ## What it emits
33
35
 
34
- Per `template.output`, `mvn metaobjects:generate` writes a `<Name>Parser` class with a static
35
- `parse` method returning the `@payloadRef` payload record. The strict path throws
36
- `com.fasterxml.jackson.core.JsonProcessingException` on malformed input:
36
+ Per responding `template.prompt`, `mvn metaobjects:generate` writes a `<Name>Parser`
37
+ class with a static `parse` method returning the `<Name>Response` record this port's
38
+ records are TEMPLATE-named, so a responding prompt gets a SECOND record beside
39
+ `<Name>Payload`. The strict path throws
40
+ `com.fasterxml.jackson.core.JsonProcessingException` on malformed input, and is
41
+ JSON-only: an `@responseFormat: xml` reply gets the tolerant extract and no `parse`:
37
42
 
38
43
  ```java
39
44
  // generated <Name>Parser.java (shape)
40
45
  public final class NpcResponseParser {
41
46
  private NpcResponseParser() { } // no instances
42
47
 
43
- public static NpcResponsePayload parse(String text) throws JsonProcessingException {
44
- // Jackson-backed: validates the text against the payload record
48
+ public static NpcResponseResponse parse(String text) throws JsonProcessingException {
49
+ // Jackson-backed: validates the text against the @responseRef record
45
50
  }
46
51
  }
47
52
  ```
@@ -61,10 +66,10 @@ reference) return `MetaObjectAware` instances instead, and those need
61
66
  `JsonObjectWriter`/`MetaObjectSerializer` — not a bare mapper — to serialize
62
67
  correctly (see the codegen reference's "Serializing generated objects" section).
63
68
 
64
- ## The output-format prompt fragment (FR-010)
69
+ ## The response-format prompt fragment (FR-010)
65
70
 
66
- For every json/xml-format `template.output`, `codegen-spring`'s
67
- `SpringOutputPromptGenerator` emits a `<TemplateShortName>OutputPrompt` class with a
71
+ For every responding `template.prompt`, `codegen-spring`'s
72
+ `SpringOutputPromptGenerator` emits a `<PromptShortName>ResponseFormat` class with a
68
73
  static `renderFormat()` / `renderFormat(PromptOverrides)` pair, backed by
69
74
  `OutputFormatRenderer` from the `metaobjects-render` module — the "produce your
70
75
  answer like this" fragment for the model. Wire it alongside
@@ -77,11 +82,12 @@ answer like this" fragment for the model. Wire it alongside
77
82
  </generator>
78
83
  ```
79
84
 
80
- `@promptStyle` on the `template.output` (`guide` default / `inline` / `exampleOnly`)
81
- controls the fragment's presentation; guidance is never emitted as comments. Skipped
82
- for `template.prompt` nodes, non-json/xml `@format`, and unresolved `@payloadRef` —
83
- the same skip contract as the parser generator. The `SPEC`'s root name is the
84
- capitalized payload class name, agreeing with the parser's extract-codegen root.
85
+ `@promptStyle` on the `template.prompt` (`guide` default / `inline` / `exampleOnly`)
86
+ controls the fragment's presentation; guidance is never emitted as comments. Skipped for
87
+ `template.output` nodes and an unresolved `@responseRef` — the same skip contract as the
88
+ parser generator. There is NO format gate: the old `@format {json,xml}` test read the
89
+ syntax of the outbound body to decide whether to describe the reply. The `SPEC`'s root
90
+ name is the response record's, agreeing with the parser's extract-codegen root.
85
91
 
86
92
  ## The three-step consumer pattern
87
93
 
@@ -1,16 +1,19 @@
1
1
  # Kotlin parser-on-receipt
2
2
 
3
- For every `template.output`, `codegen-kotlin`'s `KotlinOutputParserGenerator` emits
4
- a **typed parser** that validates an LLM/raw response against the template's
5
- `@payloadRef` payload data class. This is the receive side only — codegen emits
6
- **no** provider/LLM-call layer; you compose the call yourself. The payload data
7
- class itself comes from `KotlinPayloadGenerator` (a `@Serializable data class`), so
8
- the parser and the payload VO can't silently drift.
3
+ For every RESPONDING `template.prompt` — one declaring `@responseRef`
4
+ `codegen-kotlin`'s `KotlinOutputParserGenerator` emits a **typed parser** that validates
5
+ a model's reply against that shape. ADR-0052: the tier binds `@responseRef`, never
6
+ `@payloadRef` (which types the request the prompt renders outbound), and a
7
+ `template.output` gets no parser at all. This is the receive side only — codegen emits
8
+ **no** provider/LLM-call layer; you compose the call yourself. The data class itself comes
9
+ from `KotlinPayloadGenerator` (a `@Serializable data class`) — this port's classes are
10
+ TEMPLATE-named, so a responding prompt gets a SECOND one, `<Name>Response`, beside
11
+ `<Name>Payload`, and the parser and the record can't silently drift.
9
12
 
10
13
  ## Contents
11
14
  - Wire the generators
12
15
  - What it emits
13
- - The output-format prompt fragment (FR-010)
16
+ - The response-format prompt fragment (FR-010)
14
17
  - The three-step consumer pattern
15
18
  - Consumer dependency
16
19
  - Recommended LLM caller (bring-your-own)
@@ -34,9 +37,10 @@ the payload it parses into) to the Maven plugin's `<generators>` list:
34
37
 
35
38
  ## What it emits
36
39
 
37
- Per `template.output`, `mvn metaobjects:generate` writes a `<Name>Parser.kt`
38
- `object` with a dual API matching kotlinx.serialization's exception model plus the
39
- Kotlin stdlib `Result<T>` convention:
40
+ Per responding `template.prompt`, `mvn metaobjects:generate` writes a
41
+ `<Name>Parser.kt` `object` with a dual API matching kotlinx.serialization's exception
42
+ model plus the Kotlin stdlib `Result<T>` convention. The strict tier is JSON-only — an
43
+ `@responseFormat: xml` reply gets the tolerant extract and neither strict function:
40
44
 
41
45
  ```kotlin
42
46
  // generated <Name>Parser.kt (shape)
@@ -44,8 +48,8 @@ object NpcResponseParser {
44
48
  private val json: Json = Json { ignoreUnknownKeys = false }
45
49
 
46
50
  /** Throws kotlinx.serialization.SerializationException on bad input. */
47
- fun parseNpcResponse(text: String): NpcResponsePayload =
48
- json.decodeFromString<NpcResponsePayload>(text)
51
+ fun parseNpcResponse(text: String): NpcResponseResponse =
52
+ json.decodeFromString<NpcResponseResponse>(text)
49
53
 
50
54
  /** Result-style — does not throw. */
51
55
  fun safeParseNpcResponse(text: String): Result<NpcResponsePayload> =
@@ -63,10 +67,10 @@ null) and a `extractLenient(loader, text)` overload that delegates to the runtim
63
67
  components. The lenient mirror type (`<Name>Extracted`) uses nullable fields per
64
68
  the Kotlin null-safety port — a missing/malformed component is `null`, not a throw.
65
69
 
66
- ## The output-format prompt fragment (FR-010)
70
+ ## The response-format prompt fragment (FR-010)
67
71
 
68
- For every json/xml-format `template.output`, `codegen-kotlin`'s
69
- `KotlinOutputPromptGenerator` emits a `<TemplateShortName>OutputPrompt.kt` `object`
72
+ For every responding `template.prompt`, `codegen-kotlin`'s
73
+ `KotlinOutputPromptGenerator` emits a `<PromptShortName>ResponseFormat.kt` `object`
70
74
  with `renderFormat()` / `renderFormat(overrides: PromptOverrides)`, backed by
71
75
  `OutputFormatRenderer` from the `metaobjects-render` module — the "produce your
72
76
  answer like this" fragment for the model. Wire it alongside
@@ -79,11 +83,12 @@ answer like this" fragment for the model. Wire it alongside
79
83
  </generator>
80
84
  ```
81
85
 
82
- `@promptStyle` on the `template.output` (`guide` default / `inline` / `exampleOnly`)
83
- controls the fragment's presentation; guidance is never emitted as comments. Skipped
84
- for `template.prompt` nodes, non-json/xml `@format`, and unresolved `@payloadRef` —
85
- the same skip contract as the parser generator. The `SPEC`'s root name is the
86
- capitalized payload class name, agreeing with the parser's extract-codegen root.
86
+ `@promptStyle` on the `template.prompt` (`guide` default / `inline` / `exampleOnly`)
87
+ controls the fragment's presentation; guidance is never emitted as comments. Skipped for
88
+ `template.output` nodes and an unresolved `@responseRef` — the same skip contract as the
89
+ parser generator. There is NO format gate: the old `@format {json,xml}` test read the
90
+ syntax of the outbound body to decide whether to describe the reply. The `SPEC`'s root
91
+ name is the response class's, agreeing with the parser's extract-codegen root.
87
92
 
88
93
  ## The three-step consumer pattern
89
94