@metaobjectsdev/sdk 0.20.16 → 0.21.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.
- package/agent-context/skills/metaobjects-audit/SKILL.md +6 -2
- package/agent-context/skills/metaobjects-codegen/references/java.md +70 -2
- package/agent-context/skills/metaobjects-prompts/SKILL.md +17 -11
- package/agent-context/skills/metaobjects-prompts/references/java.md +8 -0
- package/agent-context/skills/metaobjects-runtime-ui/references/java.md +16 -0
- package/agent-context/templates/always-on.md.mustache +1 -0
- package/package.json +2 -2
|
@@ -330,8 +330,12 @@ Per finding: `file:line` → what → generated-equivalent exists? → recommend
|
|
|
330
330
|
## Prompt anti-patterns (hunt per site; classify: fully-modeled / partial / fully-inline)
|
|
331
331
|
|
|
332
332
|
- Inline prompt strings (triple-quoted / template-literal constants in service code).
|
|
333
|
-
- Untyped payloads (`str.format(**dict)` / f-strings / ad-hoc dicts) — payload should be
|
|
334
|
-
`object.value`
|
|
333
|
+
- Untyped payloads (`str.format(**dict)` / f-strings / ad-hoc dicts) — payload should be a declared
|
|
334
|
+
shape: an `object.value` (caller-supplied fields; `origin.passthrough` only — FR-015 parameter
|
|
335
|
+
lineage) or, when fields derive by `aggregate` / `collection` / `computed` / `first`, a
|
|
336
|
+
**sourceless `object.projection`** carrying those origins (#210 — assembly origins on an
|
|
337
|
+
`object.value` fail load with `ERR_SUBTYPE_RULE_VIOLATION`; `@payloadRef` accepts the sourceless
|
|
338
|
+
projection).
|
|
335
339
|
- Silent-degradation hack (`try/except KeyError` or `?? ''` around formatting) — flag every instance.
|
|
336
340
|
- Hand-rolled output parsing (regex / XML / ad-hoc JSON) vs declared `template.output` +
|
|
337
341
|
generated `parse*` / `safeParse*` / `extract*` parser — **generated in all five ports**
|
|
@@ -89,9 +89,10 @@ concrete imports and signatures so you don't have to guess them.
|
|
|
89
89
|
|
|
90
90
|
## `codegen-spring` generators
|
|
91
91
|
|
|
92
|
-
|
|
92
|
+
Most live in `metaobjects-codegen-spring` under
|
|
93
93
|
`com.metaobjects.generator.spring.*`; wire any subset, typically all three of the
|
|
94
|
-
first group together
|
|
94
|
+
first group together. (`JavaObjectCodeGenerator`, last row below, lives in the
|
|
95
|
+
separate `metaobjects-codegen-base` module instead.)
|
|
95
96
|
|
|
96
97
|
| Generator | Output |
|
|
97
98
|
|---|---|
|
|
@@ -105,6 +106,7 @@ first group together:
|
|
|
105
106
|
| `SpringRenderHelperGenerator` | the typed render helper for a `template.prompt` payload |
|
|
106
107
|
| `LlmTraceHelperGenerator` | `<Entity>TraceHelper.java` per concrete entity — the LLM-trace helper |
|
|
107
108
|
| `SpringFilterAllowlistGenerator` | per-entity filter allowlist |
|
|
109
|
+
| `JavaObjectCodeGenerator` | module `metaobjects-codegen-base` (`com.metaobjects.generator.direct.object.javacode`), a separate module from the Spring generators above. Flavor-selected via the `flavor` generator arg. `flavor=pojoAware` → `class <Name> extends PojoObject` (a concrete `MetaObjectAware` class with a `(MetaObject)` constructor) — its inherited `getMetaData()` back-reference is what breaks a default Jackson/Gson mapper, see "Serializing generated objects" below. `flavor=valueObject` → `class <Name> extends ValueObject` (map-backed; less hostile to a default mapper, but still not the sanctioned serialization path). Either concrete flavor also emits a `<Name>Extractor` plus a self-registering `ObjectClassBindingProvider`. For a plain default-Jackson-friendly type, use the `codegen-spring` record surface instead — never `pojoAware`. |
|
|
108
110
|
|
|
109
111
|
**Projections (read-only views).** An `object.projection` (read-only `source.rdb`
|
|
110
112
|
`@kind: view` child) is served read-only through OMDB at the ObjectManager layer
|
|
@@ -153,3 +155,69 @@ polymorphic + per-subtype-scoped repository seam the consumer implements against
|
|
|
153
155
|
Spring Data JPA / JDBC. Conformance-gated by `fixtures/api-contract-conformance/tph`
|
|
154
156
|
(HTTP wire shape) and `fixtures/persistence-conformance/tph-*` (single-table
|
|
155
157
|
runtime semantics).
|
|
158
|
+
|
|
159
|
+
## Serializing generated objects
|
|
160
|
+
|
|
161
|
+
Two paths hand you a `MetaObjectAware` instance: (a) `JavaObjectCodeGenerator`'s
|
|
162
|
+
flavored codegen above (a `pojoAware` or `valueObject` class), and (b) the om/omdb
|
|
163
|
+
runtime (`ObjectManager.getObjects(...)` / `MetaObject.newInstance()` — see the
|
|
164
|
+
runtime-ui reference). **A default Jackson/Gson mapper over a `PojoObject` subtype
|
|
165
|
+
fails on the `MetaObject` back-reference** — the inherited `getMetaData()` getter
|
|
166
|
+
leads a bean-style mapper into the metadata graph, and on a modular JVM into
|
|
167
|
+
`InaccessibleObjectException`. This is expected, not a bug to work around. If you
|
|
168
|
+
want a type that serializes cleanly with a bare default mapper, use the
|
|
169
|
+
`codegen-spring` record surface (`SpringDtoGenerator` / `SpringPayloadGenerator` /
|
|
170
|
+
`SpringValueObjectGenerator`) instead — never `pojoAware`.
|
|
171
|
+
|
|
172
|
+
Serialize any `MetaObjectAware` instance through the MetaObjects JSON layer's
|
|
173
|
+
`JsonObjectWriter`/`JsonObjectReader`, not a bare mapper — it applies the temporal
|
|
174
|
+
wire form below, and read/write round-trip through the same pair of calls:
|
|
175
|
+
|
|
176
|
+
```java
|
|
177
|
+
import com.metaobjects.io.object.json.JsonObjectWriter;
|
|
178
|
+
import com.metaobjects.io.object.json.JsonObjectReader;
|
|
179
|
+
import com.metaobjects.loader.MetaDataLoader;
|
|
180
|
+
import com.metaobjects.object.MetaObject;
|
|
181
|
+
|
|
182
|
+
import java.io.StringReader;
|
|
183
|
+
import java.io.StringWriter;
|
|
184
|
+
import java.nio.file.Path;
|
|
185
|
+
|
|
186
|
+
MetaDataLoader loader = MetaDataLoader.fromDirectory("app", Path.of("src/main/metaobjects"));
|
|
187
|
+
MetaObject mo = loader.getMetaObjectByName("acme::blog::Author");
|
|
188
|
+
|
|
189
|
+
// pojoAware-flavor generated class: public Author(MetaObject mo) { super(mo); }
|
|
190
|
+
Author author = new Author(mo);
|
|
191
|
+
author.setName("Ada");
|
|
192
|
+
author.setBirthDate(new java.util.Date()); // field.date
|
|
193
|
+
|
|
194
|
+
// Write
|
|
195
|
+
StringWriter out = new StringWriter();
|
|
196
|
+
JsonObjectWriter.writeObject(author, out);
|
|
197
|
+
String json = out.toString();
|
|
198
|
+
// {"@type":"acme::blog::Author","name":"Ada","birthDate":"2026-06-03"}
|
|
199
|
+
|
|
200
|
+
// Read
|
|
201
|
+
Author roundTripped = JsonObjectReader.readObject(Author.class, mo, new StringReader(json));
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
**Wire form** (`field.date` / `field.timestamp`):
|
|
205
|
+
|
|
206
|
+
| Field | Wire form | Example |
|
|
207
|
+
|---|---|---|
|
|
208
|
+
| `field.date` | calendar date of the instant at UTC — `YYYY-MM-DD` | `"2026-06-03"` |
|
|
209
|
+
| `field.timestamp` + `@localTime: true` | wall clock of the instant at UTC, no `Z` | `"2026-06-03T14:30:00.123"` |
|
|
210
|
+
| `field.timestamp` (default, tz-aware) | UTC instant, with `Z` | `"2026-06-03T14:30:00.123Z"` |
|
|
211
|
+
|
|
212
|
+
Fraction is millisecond resolution, trailing zeros stripped, and the `.` plus
|
|
213
|
+
fraction omitted entirely when zero (`.123`→`.123`, `.120`→`.12`, `.100`→`.1`,
|
|
214
|
+
`.000`→omitted). A `null` value writes JSON `null`. Readers are tolerant and
|
|
215
|
+
backward-compatible: a JSON **number** is still read as **legacy epoch
|
|
216
|
+
milliseconds**; a JSON **string** is tried in order as an ISO instant (the `Z`
|
|
217
|
+
form) → a local date-time (no `Z`) → a date-only form, failing with a message
|
|
218
|
+
naming all three accepted forms.
|
|
219
|
+
|
|
220
|
+
**Known bounded caveat:** a hand-constructed `field.date` value carrying a
|
|
221
|
+
sub-day time component writes as the calendar date only (truncated on first
|
|
222
|
+
write, stable thereafter) — this matches the shipped OMDB DATE codec, which
|
|
223
|
+
anchors DATE columns at midnight UTC.
|
|
@@ -28,7 +28,7 @@ Both carry the generic attrs:
|
|
|
28
28
|
|
|
29
29
|
| Attr | Required | Purpose |
|
|
30
30
|
|---|---|---|
|
|
31
|
-
| `@payloadRef` | yes | the `object.value` declaring the payload shape |
|
|
31
|
+
| `@payloadRef` | yes | the `object.value` — or sourceless `object.projection` (#210) — declaring the payload shape |
|
|
32
32
|
| `@textRef` | yes for `template.prompt` and a `template.output @kind: document` (the default) — a `template.output @kind: email` carries **no** `@textRef`; it uses `@subjectRef` + `@htmlBodyRef` (+ optional `@textBodyRef`) instead | the 2-layer logical text reference `group/source`, resolved by a provider |
|
|
33
33
|
| `@format` | no | `text` (default) / `html` / `xml` / `csv` / `json` / `markdown` / `spreadsheet` — drives the escaper |
|
|
34
34
|
| `@maxChars` | no | build-time size budget |
|
|
@@ -41,7 +41,8 @@ selects how the output-format prompt fragment presents the payload shape to an L
|
|
|
41
41
|
(see "the output-format prompt fragment" below); `@requiredTags` names output tags
|
|
42
42
|
the rendered text must contain (`verify` checks it) on both subtypes.
|
|
43
43
|
`template.prompt` additionally carries `@responseRef` — naming the response
|
|
44
|
-
|
|
44
|
+
shape (an `object.value` or sourceless `object.projection`, #210) the prompt
|
|
45
|
+
expects, for typed LLM-call trace derivation.
|
|
45
46
|
|
|
46
47
|
A third, structurally different subtype is also registered core vocabulary:
|
|
47
48
|
**`template.toolcall`** (`@toolName` + `@payloadRef`, ADR-0011) — a vendor-agnostic
|
|
@@ -50,21 +51,26 @@ LLM tool-call envelope with no renderable text body (the body IS the
|
|
|
50
51
|
`@format` attrs above). The vocabulary exists today; MCP exposure of declared
|
|
51
52
|
prompts/tools is roadmap, not shipped — don't promise it.
|
|
52
53
|
|
|
53
|
-
## The payload is an `object.value
|
|
54
|
+
## The payload is a shape you declare — an `object.value`, or a sourceless `object.projection`
|
|
54
55
|
|
|
55
|
-
The payload is **not** an entity — it's
|
|
56
|
-
|
|
56
|
+
The payload is **not** an entity — it's a declared shape whose fields ARE the
|
|
57
|
+
prompt's typed surface: an `object.value` (caller-supplied fields;
|
|
58
|
+
`origin.passthrough` only — FR-015 parameter lineage), or a **sourceless
|
|
59
|
+
`object.projection`** (#210 — no `source.*` child, own or inherited) when fields
|
|
60
|
+
derive by assembly. Every port's payload codegen is
|
|
57
61
|
**declared-type-authoritative (#270)**: a field's generated type comes only from its
|
|
58
62
|
declared `field.<subType>` + `isArray` + `@objectRef`, and a nested payload is a
|
|
59
63
|
declared `field.object @objectRef` to another `object.value` (`isArray: true` for a
|
|
60
|
-
list
|
|
61
|
-
|
|
62
|
-
`
|
|
63
|
-
|
|
64
|
-
|
|
64
|
+
list — nested targets stay value-only, loader-enforced). The caller supplies the
|
|
65
|
+
field values at render time. An `origin.*` child on a payload field is IGNORED for
|
|
66
|
+
typing — and the assembly origins (`aggregate` / `collection` / `computed` /
|
|
67
|
+
`first`) are ILLEGAL on an `object.value` host (`ERR_SUBTYPE_RULE_VIOLATION`, #210):
|
|
68
|
+
an origin-derived payload lives on the sourceless projection, which `@payloadRef`
|
|
69
|
+
accepts. Projections generally are covered by the `metaobjects-authoring` skill and
|
|
70
|
+
`docs/features/source-kinds.md`, not here.
|
|
65
71
|
|
|
66
72
|
Declaring the payload shape is what makes payload bloat visible: adding a field to
|
|
67
|
-
the prompt is a diff on the
|
|
73
|
+
the prompt is a diff on the declared shape, and `verify` catches template/payload
|
|
68
74
|
drift at build time instead of letting a prompt silently degrade.
|
|
69
75
|
|
|
70
76
|
```json
|
|
@@ -53,6 +53,14 @@ rather than a throw. The payload record itself comes from `SpringPayloadGenerato
|
|
|
53
53
|
— the parser is a companion to it, so the parser and payload VO can't silently
|
|
54
54
|
drift.
|
|
55
55
|
|
|
56
|
+
Both `parse()` and `extractLenient(...)` here return **plain Java 21 records** —
|
|
57
|
+
safe with any mapper, nothing special needed. That's specific to this
|
|
58
|
+
`codegen-spring` extract tier: the codegen-base flavored `<Name>Extractor` and the
|
|
59
|
+
raw `MetaObjectExtractor` (the alternative extraction path, see the codegen
|
|
60
|
+
reference) return `MetaObjectAware` instances instead, and those need
|
|
61
|
+
`JsonObjectWriter`/`MetaObjectSerializer` — not a bare mapper — to serialize
|
|
62
|
+
correctly (see the codegen reference's "Serializing generated objects" section).
|
|
63
|
+
|
|
56
64
|
## The output-format prompt fragment (FR-010)
|
|
57
65
|
|
|
58
66
|
For every json/xml-format `template.output`, `codegen-spring`'s
|
|
@@ -65,6 +65,22 @@ try {
|
|
|
65
65
|
taking a `QueryOptions` (built from an `Expression`). `ValueObject` is the
|
|
66
66
|
map-backed runtime carrier.
|
|
67
67
|
|
|
68
|
+
## Serializing a row
|
|
69
|
+
|
|
70
|
+
A `ValueObject` **is** a `Map<String, Object>`, so a default Jackson
|
|
71
|
+
`ObjectMapper` map-serializes it without special configuration — you may not
|
|
72
|
+
hit a hard failure at all. The hard failure other shapes hit is the
|
|
73
|
+
**`pojoAware`** codegen flavor's bean shape (a public `getMetaData()`
|
|
74
|
+
back-reference a bean-style mapper walks into) and any direct Gson field walk
|
|
75
|
+
over a `MetaObjectAware` instance — an OMDB `ValueObject` row sidesteps both.
|
|
76
|
+
|
|
77
|
+
Even so, the MetaObjects JSON layer (`JsonObjectWriter`/`JsonObjectReader`,
|
|
78
|
+
`com.metaobjects.io.object.json`) is the sanctioned path for an OMDB row
|
|
79
|
+
regardless of mapper friendliness — it's what applies the temporal wire form
|
|
80
|
+
(`field.date`/`field.timestamp` render per the cross-port contract; a default
|
|
81
|
+
mapper has no idea what shape those should take). See the codegen reference's
|
|
82
|
+
"Serializing generated objects" section for the write+read snippet.
|
|
83
|
+
|
|
68
84
|
## Spring wiring
|
|
69
85
|
|
|
70
86
|
`metaobjects-core-spring` (or the Spring Boot starter) declares an
|
|
@@ -15,6 +15,7 @@ spine; generated code is the disposable artifact. Regenerate with `{{codegenComm
|
|
|
15
15
|
- Never hand-edit generated files — change the metadata and regenerate (three-way merge preserves hand-written regions).
|
|
16
16
|
- Use the generated constants for any string that names metadata.
|
|
17
17
|
- The loaded metadata model is READ-ONLY — never inject nodes or mutate the tree at load time (no "enrich the model on load" hooks). Need an extra field/column? Author it in the metadata, or derive it during codegen (read the metadata, emit output). Mutating the loaded model makes it diverge from what's declared — a bad practice reserved for very rare cases.
|
|
18
|
+
- **JVM:** serialize a MetaObject-backed instance (a `pojoAware`-flavor generated class, a runtime `ValueObject`, or any `MetaObjectAware` type) through the MetaObjects JSON layer — never hand-configure a Jackson/Gson mapper around the framework fields to make a default mapper cope.
|
|
18
19
|
|
|
19
20
|
## Authoring rules you must not violate
|
|
20
21
|
- Nodes are fused-key maps: `{"<type>.<subType>": { ... }}` (e.g. `{"field.string": {"name": "email"}}`) — never split the type and subtype into separate keys.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@metaobjectsdev/sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.21.1",
|
|
4
4
|
"description": "Workspace helpers and agent-docs utilities for MetaObjects projects.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
"access": "public"
|
|
57
57
|
},
|
|
58
58
|
"dependencies": {
|
|
59
|
-
"@metaobjectsdev/metadata": "0.
|
|
59
|
+
"@metaobjectsdev/metadata": "0.21.1",
|
|
60
60
|
"zod": "^3.23.0"
|
|
61
61
|
},
|
|
62
62
|
"devDependencies": {
|