@metaobjectsdev/sdk 0.15.18 → 0.15.19-rc.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/LICENSE +82 -69
- package/agent-context/skills/metaobjects-audit/SKILL.md +47 -1
- package/agent-context/skills/metaobjects-audit/references/capability-checklist.md +13 -1
- package/agent-context/skills/metaobjects-authoring/SKILL.md +40 -13
- package/agent-context/skills/metaobjects-codegen/SKILL.md +14 -2
- package/agent-context/skills/metaobjects-codegen/references/java.md +7 -0
- package/agent-context/skills/metaobjects-codegen/references/kotlin.md +7 -0
- package/agent-context/skills/metaobjects-codegen/references/typescript.md +8 -0
- package/agent-context/skills/metaobjects-verify/SKILL.md +6 -1
- package/agent-context/skills/metaobjects-verify/references/migration.md +29 -0
- package/package.json +2 -2
package/LICENSE
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
|
|
1
2
|
Apache License
|
|
2
3
|
Version 2.0, January 2004
|
|
3
4
|
http://www.apache.org/licenses/
|
|
@@ -32,33 +33,36 @@
|
|
|
32
33
|
not limited to compiled object code, generated documentation,
|
|
33
34
|
and conversions to other media types.
|
|
34
35
|
|
|
35
|
-
"Work" shall mean the work of authorship
|
|
36
|
-
the License, as indicated by a
|
|
37
|
-
|
|
36
|
+
"Work" shall mean the work of authorship, whether in Source or
|
|
37
|
+
Object form, made available under the License, as indicated by a
|
|
38
|
+
copyright notice that is included in or attached to the work
|
|
39
|
+
(an example is provided in the Appendix below).
|
|
38
40
|
|
|
39
41
|
"Derivative Works" shall mean any work, whether in Source or Object
|
|
40
42
|
form, that is based on (or derived from) the Work and for which the
|
|
41
|
-
editorial revisions, annotations, elaborations, or other
|
|
43
|
+
editorial revisions, annotations, elaborations, or other modifications
|
|
42
44
|
represent, as a whole, an original work of authorship. For the purposes
|
|
43
45
|
of this License, Derivative Works shall not include works that remain
|
|
44
46
|
separable from, or merely link (or bind by name) to the interfaces of,
|
|
45
47
|
the Work and Derivative Works thereof.
|
|
46
48
|
|
|
47
|
-
"Contribution" shall mean
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
or
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
49
|
+
"Contribution" shall mean any work of authorship, including
|
|
50
|
+
the original version of the Work and any modifications or additions
|
|
51
|
+
to that Work or Derivative Works thereof, that is intentionally
|
|
52
|
+
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
53
|
+
or by an individual or Legal Entity authorized to submit on behalf of
|
|
54
|
+
the copyright owner. For the purposes of this definition, "submitted"
|
|
55
|
+
means any form of electronic, verbal, or written communication sent
|
|
56
|
+
to the Licensor or its representatives, including but not limited to
|
|
57
|
+
communication on electronic mailing lists, source code control systems,
|
|
58
|
+
and issue tracking systems that are managed by, or on behalf of, the
|
|
59
|
+
Licensor for the purpose of discussing and improving the Work, but
|
|
60
|
+
excluding communication that is conspicuously marked or otherwise
|
|
61
|
+
designated in writing by the copyright owner as "Not a Contribution."
|
|
62
|
+
|
|
63
|
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
64
|
+
on behalf of whom a Contribution has been received by Licensor and
|
|
65
|
+
subsequently incorporated within the Work.
|
|
62
66
|
|
|
63
67
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
64
68
|
this License, each Contributor hereby grants to You a perpetual,
|
|
@@ -74,22 +78,22 @@
|
|
|
74
78
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
75
79
|
where such license applies only to those patent claims licensable
|
|
76
80
|
by such Contributor that are necessarily infringed by their
|
|
77
|
-
Contribution(s) alone or by
|
|
81
|
+
Contribution(s) alone or by combination of their Contribution(s)
|
|
78
82
|
with the Work to which such Contribution(s) was submitted. If You
|
|
79
|
-
institute patent litigation against any entity (including a
|
|
80
|
-
or counterclaim in a lawsuit) alleging that the Work
|
|
81
|
-
Contribution
|
|
82
|
-
patent infringement, then any patent licenses
|
|
83
|
-
this License for that Work shall terminate
|
|
84
|
-
litigation is filed.
|
|
83
|
+
institute patent litigation against any entity (including a
|
|
84
|
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
85
|
+
or a Contribution incorporated within the Work constitutes direct
|
|
86
|
+
or contributory patent infringement, then any patent licenses
|
|
87
|
+
granted to You under this License for that Work shall terminate
|
|
88
|
+
as of the date such litigation is filed.
|
|
85
89
|
|
|
86
90
|
4. Redistribution. You may reproduce and distribute copies of the
|
|
87
91
|
Work or Derivative Works thereof in any medium, with or without
|
|
88
92
|
modifications, and in Source or Object form, provided that You
|
|
89
93
|
meet the following conditions:
|
|
90
94
|
|
|
91
|
-
(a) You must give any other recipients of the Work or
|
|
92
|
-
Works a copy of this License; and
|
|
95
|
+
(a) You must give any other recipients of the Work or
|
|
96
|
+
Derivative Works a copy of this License; and
|
|
93
97
|
|
|
94
98
|
(b) You must cause any modified files to carry prominent notices
|
|
95
99
|
stating that You changed the files; and
|
|
@@ -101,24 +105,28 @@
|
|
|
101
105
|
the Derivative Works; and
|
|
102
106
|
|
|
103
107
|
(d) If the Work includes a "NOTICE" text file as part of its
|
|
104
|
-
distribution,
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
the
|
|
109
|
-
Derivative Works;
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
108
|
+
distribution, then any Derivative Works that You distribute must
|
|
109
|
+
include a readable copy of the attribution notices contained
|
|
110
|
+
within such NOTICE file, excluding those notices that do not
|
|
111
|
+
pertain to any part of the Derivative Works, in at least one
|
|
112
|
+
of the following places: within a NOTICE text file distributed
|
|
113
|
+
as part of the Derivative Works; within the Source form or
|
|
114
|
+
documentation, if provided along with the Derivative Works; or,
|
|
115
|
+
within a display generated by the Derivative Works, if and
|
|
116
|
+
wherever such third-party notices normally appear. The contents
|
|
117
|
+
of the NOTICE file are for informational purposes only and
|
|
118
|
+
do not modify the License. You may add Your own attribution
|
|
119
|
+
notices within Derivative Works that You distribute, alongside
|
|
120
|
+
or as an addendum to the NOTICE text from the Work, provided
|
|
121
|
+
that such additional attribution notices cannot be construed
|
|
122
|
+
as modifying the License.
|
|
123
|
+
|
|
124
|
+
You may add Your own copyright statement to Your modifications and
|
|
125
|
+
may provide additional or different license terms and conditions
|
|
126
|
+
for use, reproduction, or distribution of Your modifications, or
|
|
127
|
+
for any such Derivative Works as a whole, provided Your use,
|
|
128
|
+
reproduction, and distribution of the Work otherwise complies with
|
|
129
|
+
the conditions stated in this License.
|
|
122
130
|
|
|
123
131
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
124
132
|
any Contribution intentionally submitted for inclusion in the Work
|
|
@@ -137,10 +145,10 @@
|
|
|
137
145
|
agreed to in writing, Licensor provides the Work (and each
|
|
138
146
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
139
147
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
140
|
-
implied, including, without limitation, any
|
|
141
|
-
NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
142
|
-
PURPOSE. You are solely responsible for determining the
|
|
143
|
-
appropriateness of using or
|
|
148
|
+
implied, including, without limitation, any warranties or conditions
|
|
149
|
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
150
|
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
151
|
+
appropriateness of using or redistributing the Work and assume any
|
|
144
152
|
risks associated with Your exercise of permissions under this License.
|
|
145
153
|
|
|
146
154
|
8. Limitation of Liability. In no event and under no legal theory,
|
|
@@ -148,20 +156,23 @@
|
|
|
148
156
|
unless required by applicable law (such as deliberate and grossly
|
|
149
157
|
negligent acts) or agreed to in writing, shall any Contributor be
|
|
150
158
|
liable to You for damages, including any direct, indirect, special,
|
|
151
|
-
incidental, or
|
|
159
|
+
incidental, or consequential damages of any character arising as a
|
|
152
160
|
result of this License or out of the use or inability to use the
|
|
153
161
|
Work (including but not limited to damages for loss of goodwill,
|
|
154
|
-
work stoppage, computer failure or malfunction, or all
|
|
155
|
-
commercial damages or losses), even if such Contributor
|
|
156
|
-
advised of the possibility of such damages.
|
|
162
|
+
work stoppage, computer failure or malfunction, or any and all
|
|
163
|
+
other commercial damages or losses), even if such Contributor
|
|
164
|
+
has been advised of the possibility of such damages.
|
|
157
165
|
|
|
158
166
|
9. Accepting Warranty or Additional Liability. While redistributing
|
|
159
167
|
the Work or Derivative Works thereof, You may choose to offer,
|
|
160
168
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
161
169
|
or other liability obligations and/or rights consistent with this
|
|
162
|
-
License. However, in accepting such obligations, You may
|
|
163
|
-
|
|
164
|
-
|
|
170
|
+
License. However, in accepting such obligations, You may act only
|
|
171
|
+
on Your own behalf and on Your sole responsibility, not on behalf
|
|
172
|
+
of any other Contributor, and only if You agree to indemnify,
|
|
173
|
+
defend, and hold each Contributor harmless for any liability
|
|
174
|
+
incurred by, or claims asserted against, such Contributor by reason
|
|
175
|
+
of your accepting any such warranty or additional liability.
|
|
165
176
|
|
|
166
177
|
END OF TERMS AND CONDITIONS
|
|
167
178
|
|
|
@@ -171,19 +182,21 @@
|
|
|
171
182
|
boilerplate notice, with the fields enclosed by brackets "[]"
|
|
172
183
|
replaced with your own identifying information. (Don't include
|
|
173
184
|
the brackets!) The text should be enclosed in the appropriate
|
|
174
|
-
comment syntax for the file format
|
|
175
|
-
|
|
185
|
+
comment syntax for the file format. We also recommend that a
|
|
186
|
+
file or class name and description of purpose be included on the
|
|
187
|
+
same "printed page" as the copyright notice for easier
|
|
188
|
+
identification within third-party archives.
|
|
176
189
|
|
|
177
|
-
|
|
190
|
+
Copyright 2026 MetaObjects Contributors
|
|
178
191
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
192
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
193
|
+
you may not use this file except in compliance with the License.
|
|
194
|
+
You may obtain a copy of the License at
|
|
182
195
|
|
|
183
|
-
|
|
196
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
184
197
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
198
|
+
Unless required by applicable law or agreed to in writing, software
|
|
199
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
200
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
201
|
+
See the License for the specific language governing permissions and
|
|
202
|
+
limitations under the License.
|
|
@@ -189,6 +189,43 @@ emits two distinct finders (named by the FK field) automatically — no annotati
|
|
|
189
189
|
reverse finder is a **codegen feature, not an attribute** (there is no reverse-nav `@`-attr
|
|
190
190
|
to author). Advisory severity — a modernization opportunity, not a failing finding.
|
|
191
191
|
|
|
192
|
+
**New-vocabulary OPPORTUNITY (the inverse hunt — advisory).** The checks above audit
|
|
193
|
+
custom vocabulary the adopter *already* registered; this one hunts where the app
|
|
194
|
+
**should** register vocabulary but hand-coded the pattern instead. Smells
|
|
195
|
+
(grep-then-verify; require a *recurring, closed* set — never flag a one-off):
|
|
196
|
+
- **N parallel hand-written integration modules** sharing a payload shape and a config
|
|
197
|
+
pattern (a closed set of channels / providers / export targets, each a near-copy with
|
|
198
|
+
a different transport) → candidate for ONE project-registered subtype whose variants
|
|
199
|
+
sit behind a closed structural-variant discriminator (the `source.rdb` `@kind`
|
|
200
|
+
pattern), plus a small owned generator emitting the per-variant wiring.
|
|
201
|
+
- **An ad-hoc string discriminator steering code switches** — a string column whose
|
|
202
|
+
values select per-variant behavior/config scattered across the codebase. If the values
|
|
203
|
+
are just a closed symbol set, that is `field.enum` (`@values`), NOT new vocabulary. It
|
|
204
|
+
earns a subtype only when the discriminated concept **owns behavior or attributes of
|
|
205
|
+
its own** (ADR-0037 step 2a).
|
|
206
|
+
- **A provider-shaped pattern re-implemented per instance** — repeated registration /
|
|
207
|
+
config blocks restating a shape one declared node per instance could carry, with codegen
|
|
208
|
+
emitting the repetition.
|
|
209
|
+
- **A downstream integration modeled entirely in code** (an outbound webhook / notifier /
|
|
210
|
+
queue publisher / tool wiring beyond `template.toolcall`) where registered vocabulary +
|
|
211
|
+
a small generator would own the payload wiring, the names-only fail-closed config check,
|
|
212
|
+
and drift detection.
|
|
213
|
+
|
|
214
|
+
Run the **ADR-0037 ordered test** before proposing: derivable → derive; differs from an
|
|
215
|
+
existing subtype only by a property → an attribute; a plain validated value → an
|
|
216
|
+
attribute; one-off author-supplied properties → the `attr.properties` bag. Only a concept
|
|
217
|
+
with its own behavior/attributes earns a subtype, and transport/protocol is never the
|
|
218
|
+
subtype axis (keep the node protocol- and address-free; variants behind the discriminator).
|
|
219
|
+
**Converge before inventing** — check shipped vocabulary AND the planned/chartered names
|
|
220
|
+
(see CALIBRATION "planned, not shipped"); never claim a chartered name for a project-local
|
|
221
|
+
type. **Recommendation:** propose a project-registered provider (explicit loader wiring,
|
|
222
|
+
ADR-0023 — never a loosened `strict` free-ride), a `metadata_sketch` of the node, and the
|
|
223
|
+
owned generator that retires the duplication; a second independent consumer needing the
|
|
224
|
+
same concept is a consumer→core promotion candidate (ADR-0011 — file an upstream issue).
|
|
225
|
+
**Verdict: VOCAB CANDIDATE (advisory)** — a modeling opportunity, never a failing finding,
|
|
226
|
+
never a tier gate; bias to under-flagging (the >15% false-positive kill criterion applies
|
|
227
|
+
with full force). Full guidance: `docs/features/downstream-metadata-decisions.md`.
|
|
228
|
+
|
|
192
229
|
---
|
|
193
230
|
|
|
194
231
|
## Classification scheme (every surface; classify on codegen AND runtime)
|
|
@@ -200,7 +237,8 @@ to author). Advisory severity — a modernization opportunity, not a failing fin
|
|
|
200
237
|
| **CODEGEN CANDIDATE (high)** | Standard CRUD/list/form over a modeled or modelable entity. | Author the view + generate; parity-gate. |
|
|
201
238
|
| **CODEGEN CANDIDATE (partial)** | Generatable data layer, bespoke presentation. | Generate data layer; keep viz hand-written. |
|
|
202
239
|
| **DYNAMIC-RUNTIME CANDIDATE** | Behavior that could be metadata-driven at runtime. | Assess runtime-metadata feasibility. |
|
|
203
|
-
| **BESPOKE (keep)** | Genuine custom:
|
|
240
|
+
| **BESPOKE (keep)** | Genuine custom: irreducible SQL (recursive CTEs, window functions, set ops — NOT plain count/sum/avg rollups, which are `origin.aggregate` on a projection), graph, SSE, auth, search, viz. | Leave hand-written — still import generated types. |
|
|
241
|
+
| **VOCAB CANDIDATE (advisory)** | A recurring, closed hand-coded pattern (parallel integration modules / an ad-hoc string discriminator) that project-registered vocabulary — a custom subtype/attr via a provider — plus a small owned generator would own. | Propose the provider + `metadata_sketch`; apply the ADR-0037 ordered test; advisory only. |
|
|
204
242
|
|
|
205
243
|
**Gold-standard exception.** A hand-written component that *derives* from generated metadata
|
|
206
244
|
cannot drift — flag as good. A "bespoke" component hardcoding a shape metadata knows is a
|
|
@@ -220,6 +258,10 @@ Per finding: `file:line` → what → generated-equivalent exists? → recommend
|
|
|
220
258
|
5. **Runtime schema patching** (`ALTER TABLE … ADD COLUMN IF NOT EXISTS`, `_ensure_schema()`) — N schema owners.
|
|
221
259
|
6. **N declarations of one shape** — same entity as Drizzle table + Zod schema + Pydantic model + hand dataclass; target is 1 + N generated.
|
|
222
260
|
7. **`own*()` accessor read of an effective property** (ADR-0039) — `ownAttr` / `ownFields` / `own_children` / bare Python `attr(` / `getMetaAttr(name, false)` / native `IsArray` used to read a value or iterate members outside the sanctioned subclass-emit / own-serializer / `@dbColumnType` cases → silently drops `extends`-inherited values. A **correctness defect** (axis G2), not advisory.
|
|
261
|
+
8. **Hand-written `CREATE VIEW` / read-only SQL standing in for a projection (view-necessity test).** Grep migrations, checked-in `.sql`, and repository/query code for `CREATE [OR REPLACE] [MATERIALIZED] VIEW`, and for hand-rolled read-only queries that mirror a read model — a pure-`SELECT` repository/service method with joins or `GROUP BY` feeding a DTO, or a raw-SQL escape (`db.execute(sql…)`, `FromSqlRaw`, a JPA @Query with hand-written SQL). For each, run the **necessity test** — can `object.projection` + origins express this shape? It can when every output column is (a) a base-entity or relationship-joined column → `origin.passthrough` (`@from` / `@via`), (b) a count/sum/avg/min/max over related rows → `origin.aggregate` (`@agg` / `@of` / `@via`, optionally row-scoped with `@filter`), (c) a child collection → `origin.collection` (`@via`), or (d) a column borrowed via `extends` — and the joins follow declared relationships / `identity.reference` FKs.
|
|
262
|
+
- **Expressible → CODEGEN CANDIDATE (high):** convert to an `object.projection` with a read-only `source.rdb` `@kind: view` child, let `meta migrate` emit the `CREATE VIEW`, and consume the generated read-only query — the hand-written view is a second source of truth for a derivable shape. Flag in the finding that **`meta verify --db` cannot catch this**: an unmodeled DB view is *unmanaged*, so this audit is the only gate that sees it. Parity-gate: the generated view returns row-identical results before the hand-written SQL is deleted.
|
|
263
|
+
- **Not expressible → BESPOKE (keep), with a NAMED justification:** record the construct that makes it irreducible (recursive CTE, window function / `OVER`, `UNION` / `INTERSECT` / `EXCEPT`, `DISTINCT ON`, lateral join, non-aggregate expression column). "It's an aggregation" is NOT a justification — plain count/sum/avg/min/max rollups are `origin.aggregate`.
|
|
264
|
+
9. **A closed variant-set hand-modeled per instance** — N sibling modules / classes / config blocks, one per channel / provider / target, sharing a payload + config shape and diverging only by transport. Grep for sibling-file families and switch-on-a-string dispatch; verify the set is closed and recurring (never a one-off). → axis I "New-vocabulary OPPORTUNITY" (VOCAB CANDIDATE, advisory).
|
|
223
265
|
|
|
224
266
|
---
|
|
225
267
|
|
|
@@ -238,6 +280,10 @@ Per finding: `file:line` → what → generated-equivalent exists? → recommend
|
|
|
238
280
|
own + customize / author a template-spec / fix upstream / stopgap.
|
|
239
281
|
- **Verify the DB artifact, not just the types** — computed view columns may appear in the
|
|
240
282
|
contract but be dropped from the view DDL; the contract may lie.
|
|
283
|
+
- **A hand-authored DB view is invisible to `meta verify --db`.** An unmodeled view is
|
|
284
|
+
*unmanaged* (informational only — never actionable drift, never auto-dropped), so a
|
|
285
|
+
hand-written view standing in for an expressible `object.projection` can never be
|
|
286
|
+
outsourced to the drift gate; hunt it here (drift signature 8, below).
|
|
241
287
|
- **Version skew:** check *actually-resolved* package versions, not declared; consuming a fix
|
|
242
288
|
requires a coordinated lockstep bump, not a source-file copy.
|
|
243
289
|
|
|
@@ -63,7 +63,13 @@ classify it (using the classification scheme in `SKILL.md`) and route the cutove
|
|
|
63
63
|
- **`source.rdb`** (`@table`, `@schema`) — hunt hard-coded physical table/schema names that
|
|
64
64
|
diverge from the default naming the source models.
|
|
65
65
|
- **`@kind` = `view` / `materializedView`** — hunt hand-written SQL views where an authored
|
|
66
|
-
projection source (read-only `@kind`) belongs.
|
|
66
|
+
projection source (read-only `@kind`) belongs. Apply the **view-necessity test** (SKILL.md,
|
|
67
|
+
drift signature 8): a hand-written `CREATE VIEW` (or read-only SQL mirroring a read model) is a
|
|
68
|
+
CODEGEN CANDIDATE when its shape is expressible via `origin.passthrough` / `origin.aggregate` /
|
|
69
|
+
`origin.collection` + `extends` — convert it to an `object.projection` so `meta migrate` emits
|
|
70
|
+
the view DDL. It is BESPOKE-keep only when a NAMED irreducible construct (recursive CTE, window
|
|
71
|
+
function, set op) blocks projection authoring. `meta verify --db` can't see an unmodeled view,
|
|
72
|
+
so this is audit-only.
|
|
67
73
|
- **`@kind` = `storedProc` / `tableFunction`** (`@parameterRef`) — hunt hand-called procs /
|
|
68
74
|
table functions that a modeled callable source with `@parameterRef` already describes.
|
|
69
75
|
- **`@role` = `primary`** (multi-source write-through) — hunt manual CQRS / write-through
|
|
@@ -157,6 +163,12 @@ classify it (using the classification scheme in `SKILL.md`) and route the cutove
|
|
|
157
163
|
- **`attr.class`** — binding facet (`field.class` binding); hunt hand-wired type-binding
|
|
158
164
|
facets. (`attr.base`, `attr.string`, `attr.int`, `attr.long`, `attr.double`, `attr.boolean`
|
|
159
165
|
are the value-type primitives behind typed attrs — not direct audit targets.)
|
|
166
|
+
- **Custom-provider extension point** (`attr.properties` is the one-off escape hatch;
|
|
167
|
+
`template.toolcall` is the historical register→extend precedent) — hunt a *recurring,
|
|
168
|
+
closed* variant set hand-coded as parallel modules (channels / providers / export targets)
|
|
169
|
+
that a project-registered subtype with a closed variant discriminator + a small owned
|
|
170
|
+
generator would own. Apply the ADR-0037 ordered test before proposing; advisory (VOCAB
|
|
171
|
+
CANDIDATE); see SKILL.md axis I "New-vocabulary OPPORTUNITY".
|
|
160
172
|
|
|
161
173
|
## Common documentation attrs (any node)
|
|
162
174
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
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.
|
|
3
|
+
description: Use when authoring or modifying MetaObjects metadata — fields, entities, relationships, sources, enums, abstracts/inheritance — in YAML or canonical JSON; includes deciding whether and how to register custom vocabulary (new subtypes or attributes via a provider).
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Authoring MetaObjects metadata
|
|
@@ -355,17 +355,39 @@ metamodel attribute*: you declare the FK once via `identity.reference`, and the
|
|
|
355
355
|
finders fall out of codegen. So never hand-roll a `findByParentId` / `WHERE fk = ?` helper —
|
|
356
356
|
consume the generated finder.
|
|
357
357
|
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
and
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
358
|
+
### Extending the metamodel — custom providers, and the downstream lifecycle
|
|
359
|
+
|
|
360
|
+
The same ordered procedure above governs new vocabulary you register — apply it
|
|
361
|
+
mechanically before registering anything. A would-be subtype that differs from an
|
|
362
|
+
existing one only by a *property* is an **attribute**, not a subtype (a "short
|
|
363
|
+
string" isn't a new field subtype — that is `@maxLength`); a plain string that merely
|
|
364
|
+
needs validating is a **validation attribute**, not a subtype (its native type is
|
|
365
|
+
still `string`, and there's no behavior to own); a concept with its own native type
|
|
366
|
+
or behavior is a **subtype**, and structural variants *within* such a subtype are
|
|
367
|
+
`@kind`. Every new first-class element also requires a registered provider + a
|
|
368
|
+
`registry-conformance` fixture (ADR-0023 strict provenance), and closed enums
|
|
369
|
+
(including any `@kind` value-set) carry `allowedValues` in the gate (ADR-0036).
|
|
370
|
+
ADR-0037 is the authority.
|
|
371
|
+
|
|
372
|
+
**Converge before inventing.** Search the shipped vocabulary first (`meta types
|
|
373
|
+
<term>`) and check the roadmap — if core already models (or plans) the concept, model
|
|
374
|
+
yours in a fold-in-friendly shape. The type names `api`, `operation`, `surface`, and
|
|
375
|
+
`binding` are chartered for planned core vocabulary — never claim one for a
|
|
376
|
+
project-local type (a later core release would force a breaking rename).
|
|
377
|
+
|
|
378
|
+
**Design rules for downstream vocabulary that ages well.** Keep the node protocol-
|
|
379
|
+
and address-free — the subtype names the *concept*; transport/protocol is a closed
|
|
380
|
+
`@kind` variant *within* the subtype (as `source.rdb` puts table/view behind
|
|
381
|
+
`@kind`), never the subtype axis, and endpoints/addresses never enter metadata.
|
|
382
|
+
Declare config as the *names* of required keys (values stay in env/config) and
|
|
383
|
+
generate a fail-closed check. Reference typed payloads instead of inlining shapes.
|
|
384
|
+
|
|
385
|
+
**Lifecycle.** Register with explicit provider wiring — never loosen `strict` to
|
|
386
|
+
free-ride ad-hoc attrs. When core later ships the concept your provider shrinks from
|
|
387
|
+
`register` to `extend` (the `template.toolcall` precedent). A second independent
|
|
388
|
+
consumer wanting the same concept is a consumer→core promotion candidate (ADR-0011) —
|
|
389
|
+
open an upstream issue rather than adding core vocabulary yourself. Full guidance:
|
|
390
|
+
`docs/features/downstream-metadata-decisions.md`.
|
|
369
391
|
|
|
370
392
|
### Currency
|
|
371
393
|
|
|
@@ -599,7 +621,12 @@ identity passes through via `extends` (`identity.primary: { name: id, extends:
|
|
|
599
621
|
the exposure (fail-closed). Give it a read-only `source.rdb` `@kind: view`
|
|
600
622
|
child (`source.rdb: { kind: view, table: v_author }`) — codegen keys projection
|
|
601
623
|
detection + view DDL off that read-only source, so without it `meta gen` emits
|
|
602
|
-
nothing for the projection.
|
|
624
|
+
nothing for the projection. **The `CREATE VIEW` body is generated from those
|
|
625
|
+
`origin.*` children by the Node `meta migrate` — never hand-author view SQL for a
|
|
626
|
+
shape origins can express** (an unmodeled view is *unmanaged*, so `meta verify --db`
|
|
627
|
+
can't even catch the drift). Hand-write a view only when a construct origins can't
|
|
628
|
+
express (recursive CTE, window function, set operation) blocks projection authoring,
|
|
629
|
+
and keep that DDL in a hand-edited migration file.
|
|
603
630
|
|
|
604
631
|
**A `passthrough` field must match its `@from` source's type.** A passthrough
|
|
605
632
|
forwards the source value unchanged, so the projection field's `field.<subType>`
|
|
@@ -45,8 +45,12 @@ Every emitted file carries a `@generated` header. This is load-bearing:
|
|
|
45
45
|
|
|
46
46
|
Practical rule: **pattern-derivable-from-metadata = regenerate; business logic =
|
|
47
47
|
hand-write in a non-generated file.** FK columns, CRUD, validator chains,
|
|
48
|
-
type-safe finders, `relations()` blocks — all derived, never hand-coded.
|
|
49
|
-
|
|
48
|
+
type-safe finders, `relations()` blocks — all derived, never hand-coded. What you
|
|
49
|
+
hand-write is what metadata genuinely can't express: regex from outside metadata,
|
|
50
|
+
domain logic, and *irreducible* SQL views (recursive CTEs, window functions, set
|
|
51
|
+
ops). Most views are NOT irreducible — model them as an `object.projection` and the
|
|
52
|
+
view DDL is generated (see the projection bullet below); a hand-written view for a
|
|
53
|
+
shape origins can express is drift the drift gate can't even see.
|
|
50
54
|
|
|
51
55
|
## Selecting generators by stable name
|
|
52
56
|
|
|
@@ -86,6 +90,14 @@ the data access too.
|
|
|
86
90
|
`meta migrate` its DB view), and you **call that generated query from your
|
|
87
91
|
route**. Declaring the projection is only half the win — *consuming* its
|
|
88
92
|
generated query is the other half.
|
|
93
|
+
- **Never hand-author the view SQL for a shape origins can express.** The
|
|
94
|
+
`CREATE VIEW` body is emitted by the Node `meta migrate` from the projection's
|
|
95
|
+
`origin.*` children — hand-writing it is a second source of truth that drifts
|
|
96
|
+
silently, because an unmodeled DB view is *unmanaged*: `meta verify --db` never
|
|
97
|
+
flags it. Hand-written view SQL is legitimate only when a named construct
|
|
98
|
+
origins can't express (recursive CTE, window function, set op) blocks
|
|
99
|
+
projection authoring — then keep that DDL in a hand-edited migration file and
|
|
100
|
+
justify it in review.
|
|
89
101
|
|
|
90
102
|
`meta gen --list` prints every generator by stable name; the `generators` array in
|
|
91
103
|
`metaobjects.config.ts` is where you opt each one in or out.
|
|
@@ -89,6 +89,13 @@ first group together:
|
|
|
89
89
|
| `SpringOutputParserGenerator` | the `template.output` parser-on-receipt (see the prompts reference) |
|
|
90
90
|
| `SpringFilterAllowlistGenerator` | per-entity filter allowlist |
|
|
91
91
|
|
|
92
|
+
**Projections (read-only views).** An `object.projection` (read-only `source.rdb`
|
|
93
|
+
`@kind: view` child) is served read-only through OMDB at the ObjectManager layer
|
|
94
|
+
(mutating ops throw); no controller is generated (controllers cover writable entities
|
|
95
|
+
only). Its `CREATE VIEW` DDL is emitted by the Node `meta migrate` from the
|
|
96
|
+
projection's `origin.*` children — never hand-author the view SQL for a shape origins
|
|
97
|
+
can express (an unmodeled view is unmanaged and drifts silently).
|
|
98
|
+
|
|
92
99
|
Metadata lives under `src/main/metaobjects/` in the same canonical JSON the other
|
|
93
100
|
ports read — fused-key form, `source.rdb` + `@table`, `@column` for a renamed
|
|
94
101
|
physical column.
|
|
@@ -106,6 +106,13 @@ All live in `metaobjects-codegen-kotlin` under
|
|
|
106
106
|
| `KotlinStoredProcGenerator` | stored-procedure call wrappers for `source.rdb` `@kind="storedProc"` |
|
|
107
107
|
| `KotlinFilterAllowlistGenerator` | per-entity filter allowlist |
|
|
108
108
|
|
|
109
|
+
**Projections (read-only views).** For an `object.projection` (read-only `source.rdb`
|
|
110
|
+
`@kind: view` child), `KotlinExposedTableGenerator` emits a read-only Exposed `Table`
|
|
111
|
+
wrapper (same column mapping, no write path). The `CREATE VIEW` DDL is emitted by the
|
|
112
|
+
Node `meta migrate` from the projection's `origin.*` children — never hand-author the
|
|
113
|
+
view SQL for a shape origins can express (an unmodeled view is unmanaged and drifts
|
|
114
|
+
silently).
|
|
115
|
+
|
|
109
116
|
Metadata lives under `src/main/metaobjects/` in the same canonical JSON the other
|
|
110
117
|
ports read — fused-key form, `source.rdb` + `@table`, `@column` for a renamed
|
|
111
118
|
physical column.
|
|
@@ -80,6 +80,14 @@ From `@metaobjectsdev/codegen-ts/generators` (server-side, framework-neutral):
|
|
|
80
80
|
| `promptRender()` | `render<Name>()` per `template.prompt` |
|
|
81
81
|
| `outputParser()` | `<Name>.output.ts` (`parse*` / `safeParse*`) per `template.output` |
|
|
82
82
|
|
|
83
|
+
**Projections (read-only views).** For an `object.projection` (a read-only `source.rdb`
|
|
84
|
+
`@kind: view` child), `entityFile()` emits a `pgView(...)` + read-only Zod + a read-only
|
|
85
|
+
finder (no create/update/delete). The `CREATE VIEW` DDL is generated by `meta migrate`
|
|
86
|
+
from the projection's `origin.*` children — `origin.passthrough` (a forwarded column),
|
|
87
|
+
`origin.aggregate` (a `count`/`sum`/`avg`/`min`/`max`, optionally row-scoped with
|
|
88
|
+
`@filter`), `origin.collection` (a nested array). **Never hand-write the view SQL** for a
|
|
89
|
+
shape origins can express; an unmodeled view is unmanaged and drifts silently.
|
|
90
|
+
|
|
83
91
|
## Discriminator inheritance (TPH)
|
|
84
92
|
|
|
85
93
|
The TS reference implementation fully supports **table-per-hierarchy (TPH)
|
|
@@ -17,7 +17,12 @@ Drift is any place where a derived artifact has fallen out of sync with the
|
|
|
17
17
|
metadata that should define it. The ones a developer must actively guard:
|
|
18
18
|
|
|
19
19
|
- **DB-vs-metadata** — the live database schema has diverged from the metadata
|
|
20
|
-
(a column the metadata no longer declares, a missing index, a type mismatch).
|
|
20
|
+
(a column the metadata no longer declares, a missing index, a type mismatch). A
|
|
21
|
+
**modeled projection's** view body is compared too — a changed `CREATE VIEW` emits
|
|
22
|
+
a `replace-view`. But a **hand-authored, unmodeled view is *unmanaged*** (reported
|
|
23
|
+
as informational, never failed, never dropped), so a hand-written view standing in
|
|
24
|
+
for an expressible `object.projection` is the one drift class `verify --db` can't
|
|
25
|
+
catch — the `metaobjects-audit` skill is the only gate that sees it.
|
|
21
26
|
- **Generated-vs-metadata (codegen)** — committed generated code no longer matches
|
|
22
27
|
what the current metadata would emit (someone edited a `@generated` file, or
|
|
23
28
|
forgot to regenerate after changing metadata).
|
|
@@ -127,6 +127,35 @@ A non-unique recency index is `index.lookup`:
|
|
|
127
127
|
"@orders": ["asc", "desc"], "@where": "archived_at IS NULL" } }
|
|
128
128
|
```
|
|
129
129
|
|
|
130
|
+
The `@where` / `@using` / `@expr` / `@orders` attributes are **index** physical
|
|
131
|
+
escapes on `identity.secondary` / `index.lookup` — they are NOT a raw-SQL escape
|
|
132
|
+
hatch for views. There is no attribute that injects hand-written SQL into a
|
|
133
|
+
projection view body (by design — see below).
|
|
134
|
+
|
|
135
|
+
## Projection views (generated view DDL)
|
|
136
|
+
|
|
137
|
+
A read-only projection (`object.projection` with a `source.rdb` `@kind: view` child)
|
|
138
|
+
does **not** get a hand-written `CREATE VIEW`. `meta migrate` synthesizes the view
|
|
139
|
+
DDL from the projection's `origin.*` children — `passthrough` columns, `aggregate`
|
|
140
|
+
rollups (`count`/`sum`/`avg`/`min`/`max`), and `collection` joins — through the one
|
|
141
|
+
canonical view-SQL emitter shared with drift detection.
|
|
142
|
+
|
|
143
|
+
- Change the projection (add a passthrough, change an aggregate) → `meta migrate`
|
|
144
|
+
emits a `create-view` / `replace-view`; a change to a source-table column the view
|
|
145
|
+
selects auto-recreates the dependent view.
|
|
146
|
+
- `meta verify --db` **body-compares** a modeled view — a live `CREATE VIEW` that no
|
|
147
|
+
longer matches the projection is `replace-view` drift.
|
|
148
|
+
- A **hand-authored, unmodeled** DB view is *unmanaged* — never diffed, never
|
|
149
|
+
dropped, never reported as actionable drift. That is the blind spot: a hand-written
|
|
150
|
+
view that could have been a projection drifts silently, so it is caught by the
|
|
151
|
+
`metaobjects-audit` skill, not here.
|
|
152
|
+
|
|
153
|
+
**Do not hand-author view SQL for a shape origins can express** — model it as a
|
|
154
|
+
projection so the view DDL is generated and drift-checked. The only case for
|
|
155
|
+
hand-written view DDL is a genuinely irreducible view (recursive CTE, window
|
|
156
|
+
function, set operation) that origins can't express; carry that in a hand-edited
|
|
157
|
+
migration file.
|
|
158
|
+
|
|
130
159
|
## Adopting an existing database (non-destructive)
|
|
131
160
|
|
|
132
161
|
`meta verify --db` / `meta migrate` can reach **zero drift** against a hand-built
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@metaobjectsdev/sdk",
|
|
3
|
-
"version": "0.15.
|
|
3
|
+
"version": "0.15.19-rc.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.15.
|
|
59
|
+
"@metaobjectsdev/metadata": "0.15.19-rc.1",
|
|
60
60
|
"zod": "^3.23.0"
|
|
61
61
|
},
|
|
62
62
|
"devDependencies": {
|