Skip to content

Add entity types and references to entity fields - #2465

Merged
pournasserian merged 3 commits into
v2from
2461-entity-types
Oct 18, 2026
Merged

pournasserian merged 3 commits into
v2from
2461-entity-types

Conversation

@pournasserian

Copy link
Copy Markdown
Member

Closes #2461

What

The third slice of F-SCH (#2415): entity types (SCH-03) and references to an entity or one of its fields (SCH-04), on top of the stored content types from #2451. There is no migration.

  • Entity flag: the definition document gains "entity": true|false, written first and read back as ContentTypeDefinition.IsEntity. A stored document without it (everything written by Store content types with their versions and the site record #2455) reads as a plain content type.
  • Fact metadata: FactMetadata sets the names every field of an entity type carries next to its value: source (url, file, person or system), owner, validFrom, validUntil and lastVerified. A field cannot opt out, because the definition has no syntax for it and an unknown field property such as "fact": false is already rejected.
  • An entity type stays one: ContentType.Revise rejects a definition that drops the flag (entity_flag_removed). A content type may still become an entity type.
  • Field references: reference takes an optional field, as in {"target": "product", "field": "price"}. IFieldType.ReferencedTypes becomes ReferenceTargets and returns ReferenceTarget(Type, Field?). list passes on its item type's targets.
  • Checks against stored data (ContentTypeDirectory):
    • A field target must be a field of the target's current definition (reference_field_unknown).
    • The target must be an entity type (reference_target_not_entity).
    • A revision cannot remove a field another type references (field_referenced, naming the referencing type and field).
    • References to the type's own fields are checked against the definition being written.
  • CONTRIBUTING.md: the renamed member.

Why

SCH-03 and SCH-04, and the F-SCH acceptance: every field on an entity type carries source, owner and validity, a definition that drops them is rejected, and ProductPage can reference Product.price.

Choices worth a look:

  • The flag lives in the versioned definition, not a column. Flagging a type is a schema change like any other, and SCH-05 will version it as one. Nothing queries the flag yet. A column can come with the first query that needs it.

  • "A definition that drops them" is read as two rules.

    • A field of an entity type has no way to opt out of the metadata.
    • An entity type cannot be revised into a plain content type, because that would strip provenance from every fact stored under it.

    If the maintainers want the flag to be reversible, for example through a reviewed change set once SCH-05 exists, only the check in ContentType.Revise changes.

  • Field references only target entity types. SCH-04 names entity fields, and only facts are worth pointing at field by field. Plain content types are referenced as whole entries, as before.

  • FactMetadata is only names. Checking a fact's value, whether it is unsourced or stale, and its owner comes with F-ENT (F-ENT: Entity registry with sources, validity and blast radius #2417). Pinning the names here keeps the schema document (SCH-06) and entity values in agreement.

  • Removing a referenced field is checked against every type's current definition in the site, and only when a revision removes fields. Tens of types per site keep that cheap (D26). Two concurrent schema writers could still slip past each other. Schema changes going through change sets (SCH-05) is where that gets serialised.

  • ReferencedTypes is renamed to ReferenceTargets on the field type extension point. It landed in Store content types with their versions and the site record #2455 and nothing outside this repository implements it. There is no versioning policy for extension interfaces yet (EXT-N2).

Tests

Unit:

  • The flag is written first and reads back. A document without it is a content type. A non-boolean flag and a field-level "fact" property are rejected.
  • FactMetadata pins its five properties and four source kinds.
  • An entity type rejects a revision that drops the flag, and a content type can take the flag.
  • reference accepts field and rejects a field that is malformed, blank or not a string, or one given without a target. It declares ReferenceTarget("product", "price").
  • list passes a field target on. A definition reports field references.
  • The conformance base now checks that every declared target is a slug with an optional field name. All built-ins and both sample plugin types pass.

Integration, each on SQLite and PostgreSQL:

  • An entity type stores "entity": true and reads back as one. A definition stored without the flag reads as a content type.
  • Dropping the flag fails and writes nothing, while flagging a content type works.
  • product-page references product.price directly and through a list.
  • Each of these fails with an error naming the field: a field the target lacks, a field of a non-entity type, and a revision removing price while two types reference it. Removing an unreferenced field works.
  • A type can reference its own field only as an entity type.
  • The reference scenario now defines product, category, spec and region as entity types and the four page types as content types, with product-page and comparison-page referencing product.price.

All ran locally against a postgres:17 container as well as SQLite. There is no migration and no pending model change, no new package and no new project reference.

Checklist

  • Linked to an issue a maintainer has confirmed
  • Targets v2
  • Tests or docs updated where behaviour changed
  • No tool-generated co-author trailers or footers

@pournasserian pournasserian added this to the M1 Governance core milestone Oct 18, 2026
@pournasserian pournasserian added type:feature New capability v2 FluentCMS v2 (v2 branch) area:schema Content types and entity types (SCH) labels Oct 18, 2026
@pournasserian
pournasserian merged commit 6d460fe into v2 Oct 18, 2026
2 checks passed
@pournasserian
pournasserian deleted the 2461-entity-types branch October 18, 2026 18:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:schema Content types and entity types (SCH) type:feature New capability v2 FluentCMS v2 (v2 branch)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Entity types and entity field references: entity flag, fact metadata and references to an entity or one of its fields

1 participant