Commit c684f50
* docs(design): index.* type + identity.secondary key-purity
Splits the overloaded identity.secondary (which had accreted the full DB-index vocab
fields/unique/expr/using/orders/where) by behavior per ADR-0037: identity.secondary =
unique alternate key (drop @unique); new index.lookup = non-unique retrieval structure
(cross-target — an index is a universal IR concept). Physical RDB tuning
(@using/@expr/@where/@orders) stays an RDB-scoped escape (like @dbColumnType), never a
subtype axis. fulltext/vector/spatial reserved on the axis, not built (YAGNI + 1.0
freeze). Grounded in two production adopters' real idx_* usage. Supersedes PR #142.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GF9xLEQZaPus5Y6opk398n
* docs(design): finalize index.* decisions — index.lookup, @unique removed (no back-compat), keep @orders, docs in scope
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GF9xLEQZaPus5Y6opk398n
* docs(plan): index.* type + identity.secondary key-purity implementation plan
13 tasks: ADR → TS reference (vocab/validation/registry/codegen/fixtures) →
C#/Java/Kotlin/Python fan-out → migration → docs → integrate/gate/merge/close-#142.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GF9xLEQZaPus5Y6opk398n
* docs(adr): ADR-0040 — index.* type + identity.secondary key-purity
* feat(metadata): index.lookup type + drop @unique from identity.secondary (TS)
Registers the new index.lookup type as the TS reference vocabulary:
- TYPE_INDEX / INDEX constants in base-types.ts and index-constants.ts
- MetaIndex class with fields() accessor (ADR-0039 resolving)
- index.lookup registered with core @fields attr; physical attrs
(@using/@expr/@where/@orders) contributed by the db provider
- identity.secondary @unique removed — it now always means "unique
constraint"; use index.lookup for non-unique query-performance indexes
- object.base child rules updated to include index (6 structural types)
- All conformance fixtures, drift gates, registry manifest, metamodel
docs, and coverage snapshot regenerated/updated
- 5 pre-existing tests migrated to the new form (removed @unique usage)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GF9xLEQZaPus5Y6opk398n
* fix(index): resolve two test regressions from Task 2 review
Critical 1: remove @unique from error-identity-name-required fixture input.
The attr was incidental; strict load now rejects it with ERR_UNKNOWN_ATTR
alongside ERR_IDENTITY_NAME_REQUIRED, breaking the conformance assertion.
Critical 2: bump BASE_TYPES toHaveLength(12 → 13) in index.test.ts and
add TYPE_INDEX to the toContain assertions (TYPE_INDEX was added to
BASE_TYPES when index.lookup was registered but the test was not updated).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GF9xLEQZaPus5Y6opk398n
* feat(metadata): index.lookup field-resolution validation (TS)
Adds `validateIndexLookupFields` pass to the loader:
- Empty @fields on index.lookup → ERR_INVALID_INDEX
- @fields naming a non-existent field → ERR_INVALID_INDEX
- Fields inherited via extends resolve correctly (ADR-0039 resolving accessor)
Also registers ERR_INVALID_INDEX in errors.ts and the cross-port
conformance fixture (fixtures/conformance/ERROR-CODES.json).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* docs(conformance): note index.lookup + identity.secondary key-purity in registry README
Manifest itself (index.lookup registered, @unique dropped from identity.secondary)
landed in Task 2. Task 4 README note.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GF9xLEQZaPus5Y6opk398n
* feat(codegen-ts): emit index.lookup as non-unique index (Drizzle + DDL)
- identity.secondary always emits uniqueIndex() in Drizzle schema; unique:
true hardcoded in expected-schema (uniqueness is in the type, no bool)
- index.lookup emits non-unique index() in Drizzle schema via
obj.lookupIndexes() + MetaIndex.fields() (ADR-0039); emits CREATE INDEX
(no UNIQUE) in expected-schema, honoring @orders/@using/@where/@expr
- MetaObject.lookupIndexes(): MetaIndex[] accessor added (TYPE_INDEX filter
on children(), mirrors secondaryIdentities() pattern)
- Cleanup: remove MetaIdentity.unique getter + IDENTITY_ATTR_UNIQUE constant
(now genuinely unused after Task 2 dropped @unique from the embedded
definition); remove 4 dead tests that authored @unique:false via setAttr
- Migrate expression-index.test.ts + index-partial-ordered.test.ts from
identity.secondary @unique:false to index.lookup (correct type now)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GF9xLEQZaPus5Y6opk398n
* feat(conformance): index-lookup metadata + persistence fixtures
Add new fixtures/conformance/index-lookup/ with an object.entity
carrying a unique identity.secondary, a single-field index.lookup,
and a composite index.lookup with @orders (asc/asc/desc).
expected.json generated from the TS loader (canonicalSerialize).
Migrate fixtures/persistence-conformance/canonical/meta.fitness.json:
- Drop @unique:true from identity.secondary byTitle (now implicit;
@unique is ERR_UNKNOWN_ATTR on identity.secondary post-ADR-0040)
- Add composite index.lookup idx_programs_title_status on [title, status]
Regenerate schema.postgres.sql via gen:schema; the DDL now emits
both CREATE UNIQUE INDEX (identity.secondary) and CREATE INDEX
(index.lookup, non-unique). schema-artifact drift test passes;
Testcontainers query tests pass (27/27).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GF9xLEQZaPus5Y6opk398n
* test(conformance): normalize index-lookup expected.json trailing newline (cross-port byte-compare)
* feat(python): index.lookup + drop identity.secondary @unique
- New `index` type + `index.lookup` subtype: TYPE_INDEX in base_types,
MetaIndex class (ADR-0039 resolving .fields()), index_constants.py
- spec/metamodel/index.json (canonical spec) + embedded Python copy
- identity.secondary: drop @unique attr (secondary is always unique;
use index.lookup for non-unique indexes) — spec + Python embedded copies
- db_provider: extend index.lookup with @orders/@where/@expr/@using
(same physical attrs as identity.secondary)
- validation_passes: _validate_index_lookup_fields (ERR_INVALID_INDEX)
+ fix allowed_values check for array-valued attrs (checks each element)
- errors.py: ERR_INVALID_INDEX
- SPEC_FILES updated with index.json; object.json embedded copy synced
(adds index child rule to object.base)
- test_index_lookup.py: 6 tests (load, @orders, @unique rejected ×2,
missing-fields, unknown-field)
- Registry-conformance + all metadata-conformance fixtures green
(index-lookup, index-lookup-basic, flattened-kitchen-sink)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GF9xLEQZaPus5Y6opk398n
* feat(csharp): index.lookup + drop identity.secondary @unique
- New type=index, subtype=lookup: IndexConstants, IndexSchema, MetaIndex
(Fields() uses resolving Attr() per ADR-0039), registered in CoreTypes
with canonical description/whenToUse, Wildcard(TYPE_INDEX) in objectRules
- DbProvider extends index.lookup with @orders/@expr/@where/@using
- identity.secondary: removed @unique from schema/constants/MetaIdentity.Unique
(secondary is always unique; non-unique indexes use index.lookup)
- ERR_INVALID_INDEX added; ValidateIndexLookupFields wired into loader
- Fixed array-valued allowedValues Check 3 (each element vs. whole array)
- SpecMetamodel sync: object.json (index.* child), identity.json, db.json
- IndexLookupTests: 6 tests (happy-path + @unique rejected + ERR_INVALID_INDEX)
- registry-conformance: 680/680 pass; codegen: 265/265 pass
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GF9xLEQZaPus5Y6opk398n
* feat(jvm): index.lookup + remove identity.secondary @unique (metadata)
Add the index.* type family to the JVM metadata module:
- New com.metaobjects.index.{Index, LookupIndex, IndexTypesMetaDataProvider}
- index.lookup: non-unique query-performance index; @fields (required array)
contributed by IndexTypesMetaDataProvider; @orders/@expr/@where/@using
contributed by CoreDBMetaDataProvider.extendType (mirroring the db provider
extends blocks in spec/metamodel/db.json)
- Remove @unique from identity.secondary (uniqueness is intrinsic to secondary
identity; no @unique ever on index.* types — ADR brief constraint)
- ERR_INVALID_INDEX: empty @fields or unknown field → thrown by new
validateIndexLookupFields pass in ValidationPhase
- Allow index.* as optional child of object.base (MetaObject.registerTypes)
- Register IndexTypesMetaDataProvider in ServiceLoader SPI and RegistryManifest
- Fix extendType() to wire the registry into the TypeDefinitionBuilder so
auto-generated array constraints (isArray marker) survive the rebuild
- Add spec/metamodel/index.json (type-level docs + fields attr doc); register
it in SpecMetamodelReader.SPEC_FILES (16 files); registry-conformance green
- 6 new IndexLookupTest cases; 1077/1077 metadata tests green
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GF9xLEQZaPus5Y6opk398n
* feat(codegen-kotlin): emit index.lookup as Exposed non-unique index; identity.secondary always unique
* fix(csharp): embed spec/metamodel/index.json + SPEC_FILES 15->16 (FR-033 embed gate)
Closes the C# spec-embed gap the parallel fan-out surfaced: C#'s byte-identity
embed gate enumerated 15 files (no index.json), a false-green. Canonical
spec/metamodel/index.json is single-line (matches sibling identity.json).
* docs(metamodel): index.lookup + drop identity.secondary @unique (spec, docs, skills)
Add index.lookup authoring guidance to metaobjects-authoring SKILL.md:
- Identities table: clarify identity.secondary always enforces uniqueness (uniqueness
is the type, not a @unique attr)
- New 'Indexes (non-unique)' section: index.lookup for query-performance indexes,
choose-by-what-the-constraint-is table, @fields/@orders/@expr/@using/@where attrs,
example snippets, placement guidance
- Regenerate agent-context conformance fixtures (4 stacks, 5 files total)
Metamodel-docs fixtures (fixtures/metamodel-docs/expected/) were already correct
from earlier port work: identity.md has no @unique attr row, index.md is complete,
INDEX.md and providers.md include index.lookup. Conformance gate: 1 pass.
All gates green: metamodel-docs conformance (1), agent-context conformance (4),
vocabulary-drift (4), capability-grounding (5) — 14 total.
* docs(migrate): identity.secondary → index.lookup migration guide + no-op diff test
- docs/features/migrations/identity-secondary-to-index-lookup.md: mechanical
rewrite rule (non-unique → index.lookup; unique → keep identity.secondary,
drop @unique), Python rewrite script, no-DDL-churn guarantee, ERR_UNKNOWN_ATTR
diagnosis
- server/typescript/packages/migrate-ts/test/index-migration-noop.test.ts: four
tests proving the vocabulary move produces zero migration SQL; documents that
@unique:false on identity.secondary is ERR_UNKNOWN_ATTR under strict load
- fixtures/conformance/index-lookup-extends/: shared cross-port fixture gating
index.lookup whose @fields references an extends-inherited field (ADR-0039
resolving); expected.json loader-generated
2595 pass, 11 skip, 0 fail across 2606 tests
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GF9xLEQZaPus5Y6opk398n
* fix(migrate-ts): index-migration-noop test — views: [] not 'as const' (SchemaSnapshot type)
* fix(index): @fields required on index.lookup — align registry, docs, and all 5 ports
The registry and authoring skill claimed @fields "may be omitted when @expr
provided" (an expr-only functional index), but the loader in all 5 ports has
always required ≥1 field (ERR_INVALID_INDEX on empty). The expression-index test
confirms @fields always accompanies @expr — @expr is the key expression *derived
from* those fields, not a replacement.
Changes (all byte-gated artefacts updated consistently):
- spec/metamodel/index.json: min 0 → 1, description corrected
- Byte-identical embed copies updated (C# SpecMetamodel, Python spec_metamodel)
- expected-registry.json: index.lookup @fields required: false → true + description
- TS index-definition.embedded.ts: min 0 → 1
- C# IndexSchema.cs: Required: false → true + doc comment
- Python core_types.py: required=False → True
- Java LookupIndex.java: optionalAttributeWithConstraints → requiredAttributeWithConstraints
- Authoring skill: remove expr-only example; show @fields with @expr (real pattern)
- Conformance fixtures regenerated: metamodel-docs/expected + agent-context-conformance/*
Gates: TS 2234/0, C# 15/0, Python 94/0, Java 5/0.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GF9xLEQZaPus5Y6opk398n
---------
Co-authored-by: Doug Mealing <noreply@anthropic.com>
1 parent 4686f16 commit c684f50
120 files changed
Lines changed: 4106 additions & 231 deletions
File tree
- .superpowers/sdd
- agent-context/skills/metaobjects-authoring
- docs
- features/migrations
- superpowers
- plans
- specs
- fixtures
- agent-context-conformance
- java-kotlin-react-tanstack/expected/.claude/skills/metaobjects-authoring
- java-react/expected/.claude/skills/metaobjects-authoring
- python/expected/.claude/skills/metaobjects-authoring
- ts-react-tanstack/expected/.claude/skills/metaobjects-authoring
- conformance
- error-identity-name-required/input
- index-lookup-basic
- input
- index-lookup-extends
- input
- index-lookup
- input
- metamodel-docs/expected
- types
- persistence-conformance/canonical
- registry-conformance
- server
- csharp
- MetaObjects.Codegen.Tests
- MetaObjects.Conformance.Tests
- MetaObjects
- Core
- Identity
- Index
- Loader
- Meta
- Persistence/Db
- Registry/Spec
- SpecMetamodel
- java
- codegen-kotlin/src
- main/kotlin/com/metaobjects/generator/kotlin
- test/kotlin/com/metaobjects/generator/kotlin
- metadata
- src
- main
- java/com/metaobjects
- database
- identity
- index
- loader
- object
- registry
- spec
- util
- resources/META-INF/services
- test/java/com/metaobjects
- index
- registry/spec
- python
- src/metaobjects
- loader
- meta
- core/index
- persistence/db
- spec_metamodel
- tests/unit
- typescript/packages
- codegen-ts
- src
- generators
- templates
- test
- metadata
- src
- core
- identity
- index
- object
- loader
- persistence/db
- test
- meta
- migrate-ts
- src
- test
- spec
- decisions
- metamodel
Some content is hidden
Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
430 | 430 | | |
431 | 431 | | |
432 | 432 | | |
| 433 | + | |
433 | 434 | | |
434 | 435 | | |
435 | 436 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
384 | 384 | | |
385 | 385 | | |
386 | 386 | | |
387 | | - | |
| 387 | + | |
388 | 388 | | |
389 | 389 | | |
390 | 390 | | |
| |||
407 | 407 | | |
408 | 408 | | |
409 | 409 | | |
410 | | - | |
| 410 | + | |
411 | 411 | | |
412 | 412 | | |
413 | 413 | | |
| |||
419 | 419 | | |
420 | 420 | | |
421 | 421 | | |
| 422 | + | |
| 423 | + | |
| 424 | + | |
| 425 | + | |
| 426 | + | |
| 427 | + | |
| 428 | + | |
| 429 | + | |
| 430 | + | |
| 431 | + | |
| 432 | + | |
| 433 | + | |
| 434 | + | |
| 435 | + | |
| 436 | + | |
| 437 | + | |
| 438 | + | |
| 439 | + | |
| 440 | + | |
| 441 | + | |
| 442 | + | |
| 443 | + | |
| 444 | + | |
| 445 | + | |
422 | 446 | | |
423 | 447 | | |
424 | 448 | | |
| |||
Lines changed: 187 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
| 181 | + | |
| 182 | + | |
| 183 | + | |
| 184 | + | |
| 185 | + | |
| 186 | + | |
| 187 | + | |
0 commit comments