Screens

Every screen below was generated from this object by OODS Foundry 0.10.1, in React and Vue, and shows the calls that made it and its content hash. Each runs as its generated app runs, in this site's brand, Aquex, in the theme you choose, with the sample records this object's own file carries. A value no record gives shows as a neutral one, such as an empty value or "Not recorded"; OODS Foundry invents none.

React app · sample recordsMaturity: not declaredsha256:86b2472ca161…Open full size: Article detail screen generated by OODS Foundry in React
The calls that made the Article detail apps, in React and Vue
3 calls, as the pipeline sent them
design_compose {
  "object": "Article",
  "context": "detail",
  "preferences": {
    "brand": "Aquex",
    "theme": "light"
  }
}

code_generate {
  "schemaRef": "compose-ca30742e",
  "framework": "react",
  "profile": "build",
  "options": {
    "output": "application",
    "brand": "Aquex",
    "theme": "light",
    "payloadMode": "file"
  }
}

code_generate {
  "schemaRef": "compose-ca30742e",
  "framework": "vue",
  "profile": "build",
  "options": {
    "output": "application",
    "brand": "Aquex",
    "theme": "light",
    "payloadMode": "file"
  }
}

The React app's content hash: sha256:86b2472ca161d55e67a2ef937848c365246168bace148dcbed15489db6e6019a. Its src/ folder: App.tsx, GeneratedUI.tsx, app.css, main.tsx, oods-brand-aquex.css

The Vue app's content hash: sha256:772e8b4298d0d85d64a7d4f545a2bc79c59d984f8d779f41b4b69ff9dd1a8556. Its src/ folder: App.vue, GeneratedUI.vue, app.css, main.ts, oods-brand-aquex.css

Fields

FieldTypeDescription
labelrequired string Human-readable display name rendered in primary surfaces.
description string Supporting description used in detail and card contexts.
placeholder string Hint copy surfaced in form fields when the label is empty.
statusrequired string Canonical lifecycle state derived from the states parameter.
More

This is the single source of truth for the entity's current lifecycle position. Consumed by Colorized to resolve visual tokens, and by view extensions to render StatusBadge and StatusTimeline.

state_history StateTransition[] Chronological log of state transitions.
More

Each entry records the before/after states, timestamp, and (when governance is enabled) the actor, reason, and transition metadata. Rendered by StatusTimeline in the detail and timeline views. Entry structure: - from: string (previous state) - to: string (new state) - timestamp: ISO 8601 datetime - actor_id: string (user/system who triggered the transition, optional) - reason: string (human-readable justification, required when requireTransitionReason is true) - transition_metadata: Record<string, unknown> (arbitrary context, optional)

allowed_transitions string[] Materialized list of valid next states from the current status, computed from the transitionRules parameter.
More

When transitionRules is null (open model), this contains all states except the current one. Used by StatusSelector to disable invalid options and by StatusBadge to indicate available paths.

created_atrequired datetime Timestamp recording when the entity was first created.
updated_at datetime Timestamp for the most recent modification, when available.
last_eventrequired string Lifecycle event associated with the most recent timestamp mutation.
last_event_at datetime Timestamp for the lifecycle event captured in last_event.
classification_metadatarequired ClassificationMetadata Operational metadata describing mode, storage model, governance rules, and audit timestamps.
categories CategoryNode[] Ordered taxonomy nodes scoped to the object.
primary_category_id string Identifier of the canonical taxonomy node.
primary_category_path string Human-readable breadcrumb path (Electronics > Mobile > Android).
tags Tag[] Canonical tag collection after synonym collapse.
tag_count number Number of canonical tags assigned to the object.
tag_preview string Denormalized comma-delimited preview for list renders.
article_idrequired uuid Primary identifier for the article document.
slugrequired string Canonical slug rendered in URLs and breadcrumb links.
author_idrequired uuid Reference to the authoring user.
author_name string Display name of the authoring user.
content_typerequired string (knowledge_base, announcement, release_notes, how_to) Editorial template driving layout and governance workflows.
excerpt string Summary text used in list + SEO contexts.
body_markdownrequired string Markdown content rendered in CMS detail views.
hero_media_id string Optional Media object identifier referenced in hero slots.
reading_time_minutes number Estimated reading time derived from body length.
locale string Locale tag determining headline, copy, and SEO metadata.
published_at datetime Timestamp when the article was published live.

Traits

Each trait adds fields and behaviour. Every object with the trait gets the same.

Relationships

These arrows show declared relationships between object types. They don't join live records.

  • User, many-to-one, via author_id
  • Media, many-to-one, via hero_media_id
Relationships: Article3 object types and 2 declared associations. Arrows point from source to target. Labels state source-relative cardinality and the via field; they do not assert live referential integrity. Article to Media: Hero media, many-to-one, via hero_media_id. Article to User: Author, many-to-one, via author_id.Hero mediamany-to-onevia hero_media_idAuthormany-to-onevia author_idCanonical editorial article with hybrid taxonomy + tag classification for CMS surfaces.ArticleTraits: Labelled, Stateful,Timestampable, ClassifiableKey fields (6 of 28)article_id: uuid *label: string *author_id: uuid *hero_media_id: stringbody_markdown: string *content_type: string *Canonical digital asset metadata powering media library, DAM, and CMS integrations.MediaTraits: Labelled, Stateful,Timestampable, ClassifiableKey fields (6 of 29)media_id: uuid *label: string *asset_type: string *file_size_bytes: number *mime_type: string *object_key: string *Canonical identity record representing a platform user across lifecycle contexts.UserTraits: Stateful, Timestampable,Taggable, Addressable, Preferenceable,Authable, CommunicableKey fields (6 of 37)user_id: uuid *name: string *primary_email: email *role: string *description: stringpreferred_name: string

The object file

The Article object definition in YAML: objects/content/Article.object.yaml
object:
  name: Article
  version: 1.0.0
  domain: content.publication
  description: Canonical editorial article with hybrid taxonomy + tag classification for CMS surfaces.
  tags:
    - content
    - article
    - hybrid
    - classifiable

traits:
  - name: content/Labelled
    alias: ArticleHeadline
  - name: lifecycle/Stateful
    alias: ArticleWorkflow
    parameters:
      states:
        - draft
        - in_review
        - scheduled
        - published
        - archived
      initialState: draft
  - name: lifecycle/Timestampable
    alias: ArticleTimestamps
    parameters:
      recordedEvents:
        - created
        - scheduled
        - published
        - updated
        - archived
      timezone: UTC
  - name: core/Classifiable
    alias: ArticleClassification
    parameters:
      classification_mode: hybrid
      hierarchy_storage_model: materialized_path
      tag_policy: moderated
      max_tags: 24
      require_primary_category: true

schema:
  article_id:
    type: uuid
    required: true
    description: Primary identifier for the article document.
  slug:
    type: string
    required: true
    description: Canonical slug rendered in URLs and breadcrumb links.
    validation:
      pattern: '^[a-z0-9]+(?:-[a-z0-9]+)*$'
  author_id:
    type: uuid
    required: true
    description: Reference to the authoring user.
  author_name:
    type: string
    required: false
    description: Display name of the authoring user.
  content_type:
    type: string
    required: true
    default: knowledge_base
    description: Editorial template driving layout and governance workflows.
    validation:
      enum:
        - knowledge_base
        - announcement
        - release_notes
        - how_to
  excerpt:
    type: string
    required: false
    description: Summary text used in list + SEO contexts.
    validation:
      maxLength: 240
  body_markdown:
    type: string
    required: true
    description: Markdown content rendered in CMS detail views.
  hero_media_id:
    type: string
    required: false
    description: Optional Media object identifier referenced in hero slots.
  reading_time_minutes:
    type: number
    required: false
    description: Estimated reading time derived from body length.
    validation:
      minimum: 1
      maximum: 60
  locale:
    type: string
    required: false
    default: en-US
    description: Locale tag determining headline, copy, and SEO metadata.
  published_at:
    type: datetime
    required: false
    description: Timestamp when the article was published live.

semantics:
  # The one-line summary under the headline in lists (content/Labelled's description, marked as the summary).
  description:
    semantic_type: text.summary
    token_mapping: tokenMap(text.body.*)
    ui_hints:
      component: TextBody
      maxLengthParameter: maxDescriptionLength
  article_id:
    semantic_type: content.article.id
    token_mapping: tokenMap(content.article.id)
  slug:
    semantic_type: content.article.slug
    token_mapping: tokenMap(content.article.slug)
  author_id:
    semantic_type: content.article.author
    token_mapping: tokenMap(identity.user.id)
    ui_hints:
      displayLabelField: author_name
  content_type:
    semantic_type: content.article.type
    token_mapping: tokenMap(content.article.type)
  reading_time_minutes:
    semantic_type: content.article.read_time
    token_mapping: tokenMap(metric.duration.short)
  locale:
    semantic_type: content.article.locale
    token_mapping: tokenMap(content.locale.badge)
  published_at:
    semantic_type: content.article.published_at
    token_mapping: tokenMap(timestamp.published_at)

tokens:
  content.article.id: "var(--sys-text-strong)"
  content.article.slug: "var(--sys-text-muted)"
  content.article.type: "var(--sys-badge-neutral-fg)"
  content.article.read_time: "var(--sys-text-subtle)"
  content.locale.badge: "var(--sys-badge-accent-fg)"
  timestamp.published_at: "var(--sys-text-muted)"

metadata:
  owners:
    - content-platform@oods.systems
    - docs@oods.systems
  steward: publishing@oods.systems
  changelog:
    - version: 1.0.0
      date: "2025-11-18"
      description: Initial article object composing hybrid Classifiable mode for taxonomy + tags.

# Authored type associations; no record join or referential-integrity claim.
relationships:
  - target: User
    via: author_id
    cardinality: many-to-one
    label: Author
  - target: Media
    via: hero_media_id
    cardinality: many-to-one
    label: Hero media

# Eight authored sample help-centre articles about the billing product, written by three editors. Each follows the
# editorial lifecycle from draft (its initial state); only published and archived articles carry a publication date, and
# the heroes are sample Media assets.
samples:
  - label: September 2026 release notes
    description: What changed in billing this month
    slug: september-2026-release-notes
    author_id: 8b2e4d6f-1a3c-4e5b-9d7f-2c4e6a8b0e01
    author_name: Elena Park
    content_type: release_notes
    excerpt: A preview of usage-based pricing, smarter payment retries and faster invoice search.
    body_markdown: |
      ## Payment retries
      Failed card payments now retry on the schedule you choose, and each retry is listed on the invoice.

      ## Invoice search
      Search finds invoices by number, customer or amount in under a second.
    hero_media_id: 5a7c9e1b-3d5f-4a6c-8e0a-2b4d6f8a0c03
    reading_time_minutes: 5
    locale: en-US
    primary_category_id: updates-release-notes
    primary_category_path: Updates > Release notes
    tag_preview: releases
    status: published
    state_history:
      - { title: Drafted, from: null, to: draft, at: '2026-09-22T09:00:00Z', reason: Drafted from the release checklist }
      - { title: In review, from: draft, to: in_review, at: '2026-09-26T15:00:00Z', reason: Sent to product review }
      - { title: Published, from: in_review, to: published, at: '2026-09-29T16:00:00Z', reason: Published with the release }
    published_at: '2026-09-29T16:00:00Z'
    created_at: '2026-09-22T09:00:00Z'
    updated_at: '2026-09-29T16:00:00Z'
    last_event: published
    last_event_at: '2026-09-29T16:00:00Z'
  - label: How prorations work when plans change
    description: Why an upgrade adds a prorated charge
    slug: how-prorations-work
    author_id: 8b2e4d6f-1a3c-4e5b-9d7f-2c4e6a8b0e02
    author_name: Marcus Hale
    content_type: knowledge_base
    excerpt: When a customer upgrades mid-cycle, they pay the difference for the days left in the period.
    body_markdown: |
      An upgrade takes effect straight away. The customer is charged the new price minus the old one, for the days left
      in the current billing period, and the next renewal is at the new price.

      A downgrade takes effect at the end of the period, so no credit is issued.
    hero_media_id: 5a7c9e1b-3d5f-4a6c-8e0a-2b4d6f8a0c01
    reading_time_minutes: 6
    locale: en-US
    primary_category_id: billing-plans
    primary_category_path: Billing > Plans
    tag_preview: prorations, upgrades
    status: published
    state_history:
      - { title: Drafted, from: null, to: draft, at: '2026-06-22T10:00:00Z', reason: Requested by the support team }
      - { title: In review, from: draft, to: in_review, at: '2026-07-01T10:00:00Z', reason: Sent to the billing lead }
      - { title: Published, from: in_review, to: published, at: '2026-07-08T14:00:00Z', reason: Approved and published }
    published_at: '2026-07-08T14:00:00Z'
    created_at: '2026-06-22T10:00:00Z'
    updated_at: '2026-07-08T14:00:00Z'
    last_event: published
    last_event_at: '2026-07-08T14:00:00Z'
  - label: Set up automatic payment retries
    description: Recover failed payments on a schedule
    slug: set-up-automatic-payment-retries
    author_id: 8b2e4d6f-1a3c-4e5b-9d7f-2c4e6a8b0e03
    author_name: Ines Duarte
    content_type: how_to
    excerpt: Choose how often a declined card is retried and when the customer is reminded.
    body_markdown: |
      1. Open Settings, then Payments.
      2. Under Retries, choose up to four attempts over 21 days.
      3. Turn on reminder emails so the customer can update their card before the last retry.
    hero_media_id: 5a7c9e1b-3d5f-4a6c-8e0a-2b4d6f8a0c02
    reading_time_minutes: 4
    locale: en-US
    primary_category_id: billing-payments
    primary_category_path: Billing > Payments
    tag_preview: retries, dunning
    status: published
    state_history:
      - { title: Drafted, from: null, to: draft, at: '2026-08-05T09:00:00Z', reason: Drafted for the retries launch }
      - { title: In review, from: draft, to: in_review, at: '2026-08-12T09:00:00Z', reason: Sent to the support lead }
      - { title: Published, from: in_review, to: published, at: '2026-08-19T13:00:00Z', reason: Approved and published }
    published_at: '2026-08-19T13:00:00Z'
    created_at: '2026-08-05T09:00:00Z'
    updated_at: '2026-08-19T13:00:00Z'
    last_event: published
    last_event_at: '2026-08-19T13:00:00Z'
  - label: Introducing usage-based billing
    description: Charge for what customers use
    slug: introducing-usage-based-billing
    author_id: 8b2e4d6f-1a3c-4e5b-9d7f-2c4e6a8b0e01
    author_name: Elena Park
    content_type: announcement
    excerpt: Meter API calls or seats, include an allowance in each plan and bill the overage automatically.
    body_markdown: |
      Usage-based billing arrives on October 6. Each plan can include an allowance, and anything above it is billed at
      the rate you set, on the same invoice as the subscription.
    hero_media_id: 5a7c9e1b-3d5f-4a6c-8e0a-2b4d6f8a0c04
    reading_time_minutes: 3
    locale: en-US
    primary_category_id: updates-announcements
    primary_category_path: Updates > Announcements
    tag_preview: usage, launch
    status: scheduled
    state_history:
      - { title: Drafted, from: null, to: draft, at: '2026-09-01T09:00:00Z', reason: Drafted for the October launch }
      - { title: In review, from: draft, to: in_review, at: '2026-09-15T09:00:00Z', reason: Sent to product marketing }
      - { title: Scheduled, from: in_review, to: scheduled, at: '2026-09-24T16:00:00Z', reason: Scheduled for October 6 }
    published_at: null
    created_at: '2026-09-01T09:00:00Z'
    updated_at: '2026-09-24T16:00:00Z'
    last_event: scheduled
    last_event_at: '2026-09-24T16:00:00Z'
  - label: Refund a duplicate charge
    description: Find and refund a charge made twice
    slug: refund-a-duplicate-charge
    author_id: 8b2e4d6f-1a3c-4e5b-9d7f-2c4e6a8b0e03
    author_name: Ines Duarte
    content_type: how_to
    excerpt: Spot two charges for the same period and refund one of them in full.
    body_markdown: |
      Open the customer's payments and look for two charges on consecutive days for the same amount. Open the later one
      and choose Refund; the refund appears on the invoice and in the customer's payment history.
    hero_media_id: null
    reading_time_minutes: 3
    locale: en-US
    primary_category_id: billing-payments
    primary_category_path: Billing > Payments
    tag_preview: refunds
    status: in_review
    state_history:
      - { title: Drafted, from: null, to: draft, at: '2026-09-17T11:00:00Z', reason: Written after a support ticket }
      - { title: In review, from: draft, to: in_review, at: '2026-09-25T11:00:00Z', reason: Sent to the support lead }
    published_at: null
    created_at: '2026-09-17T11:00:00Z'
    updated_at: '2026-09-25T11:00:00Z'
    last_event: updated
    last_event_at: '2026-09-25T11:00:00Z'
  - label: Invoice numbers, tax IDs and legal names
    description: What each invoice must show, by region
    slug: invoice-numbers-tax-ids
    author_id: 8b2e4d6f-1a3c-4e5b-9d7f-2c4e6a8b0e02
    author_name: Marcus Hale
    content_type: knowledge_base
    excerpt: The details an invoice needs in the US, the UK and the EU, and where to set them.
    body_markdown: |
      Every invoice shows a unique number, your legal name and address, and the customer's. In the UK and the EU it also
      shows your VAT number and, for business customers, theirs.
    hero_media_id: 5a7c9e1b-3d5f-4a6c-8e0a-2b4d6f8a0c06
    reading_time_minutes: 7
    locale: en-GB
    primary_category_id: billing-invoices
    primary_category_path: Billing > Invoices
    tag_preview: invoices, tax
    status: draft
    state_history:
      - { title: Drafted, from: null, to: draft, at: '2026-09-08T08:00:00Z', reason: Drafted for the tax settings page }
      - { title: In review, from: draft, to: in_review, at: '2026-09-16T08:00:00Z', reason: Sent to the finance team }
      - { title: Changes requested, from: in_review, to: draft, at: '2026-09-18T08:00:00Z', reason: Needs the EU VAT section }
    published_at: null
    created_at: '2026-09-08T08:00:00Z'
    updated_at: '2026-09-18T08:00:00Z'
    last_event: updated
    last_event_at: '2026-09-18T08:00:00Z'
  - label: Moving from the Pro plan
    description: Pro customers moved to Business in 2026
    slug: moving-from-the-pro-plan
    author_id: 8b2e4d6f-1a3c-4e5b-9d7f-2c4e6a8b0e01
    author_name: Elena Park
    content_type: announcement
    excerpt: The Pro plan closed to new customers; existing Pro subscriptions moved to Business at the same price.
    body_markdown: |
      Pro subscriptions moved to the Business plan at their next renewal, at the Pro price for the first year. Nothing
      changed for customers already on Business.
    hero_media_id: 5a7c9e1b-3d5f-4a6c-8e0a-2b4d6f8a0c07
    reading_time_minutes: 2
    locale: en-US
    primary_category_id: updates-announcements
    primary_category_path: Updates > Announcements
    tag_preview: plans
    status: archived
    state_history:
      - { title: Drafted, from: null, to: draft, at: '2025-10-01T09:00:00Z', reason: Drafted with the pricing change }
      - { title: In review, from: draft, to: in_review, at: '2025-10-06T09:00:00Z', reason: Sent to product marketing }
      - { title: Published, from: in_review, to: published, at: '2025-10-09T14:00:00Z', reason: Published with the pricing change }
      - { title: Archived, from: published, to: archived, at: '2026-04-30T09:00:00Z', reason: Migration complete }
    published_at: '2025-10-09T14:00:00Z'
    created_at: '2025-10-01T09:00:00Z'
    updated_at: '2026-04-30T09:00:00Z'
    last_event: archived
    last_event_at: '2026-04-30T09:00:00Z'
  - label: Pause a subscription for the off-season
    description: Stop billing without losing your data
    slug: pause-a-subscription
    author_id: 8b2e4d6f-1a3c-4e5b-9d7f-2c4e6a8b0e03
    author_name: Ines Duarte
    content_type: how_to
    excerpt: Pause billing for up to three months and resume from the same place.
    body_markdown: |
      Open the subscription and choose Pause. Billing stops at the end of the current period, and nothing is deleted.
      Choose Resume at any time to start the next period straight away.
    hero_media_id: null
    reading_time_minutes: 3
    locale: en-US
    primary_category_id: billing-subscriptions
    primary_category_path: Billing > Subscriptions
    tag_preview: subscriptions
    status: published
    state_history:
      - { title: Drafted, from: null, to: draft, at: '2026-05-11T10:00:00Z', reason: Requested by seasonal customers }
      - { title: In review, from: draft, to: in_review, at: '2026-05-19T10:00:00Z', reason: Sent to the support lead }
      - { title: Published, from: in_review, to: published, at: '2026-05-26T12:00:00Z', reason: Approved and published }
    published_at: '2026-05-26T12:00:00Z'
    created_at: '2026-05-11T10:00:00Z'
    updated_at: '2026-05-26T12:00:00Z'
    last_event: published
    last_event_at: '2026-05-26T12:00:00Z'