{
  "version": "v1",
  "format": "flax-model-patch-recipe",
  "index": "https://flaxsites.com/.well-known/flax-agent-recipes.v1.json",
  "siteDataModelSchema": "https://flaxsites.com/schemas/flax/v1/site-data-model.schema.json",
  "articleSchema": "https://flaxsites.com/schemas/flax/v1/article.schema.json",
  "modelUpdatePlanSchema": "https://flaxsites.com/schemas/flax/v1/model-update-plan.schema.json",
  "feature": "articles",
  "id": "upsert-article",
  "summary": "Create or update an article in an existing enabled Articles section as an owner-review draft.",
  "useWhen": [
    "The user has supplied or confirmed article content.",
    "The live model has an enabled ui.sections entry with type 'articles'."
  ],
  "modelPaths": {
    "section": "ui.sections[]",
    "articleCollection": "articles.<sectionId>.articles",
    "article": "articles.<sectionId>.articles.<index>"
  },
  "workflow": [
    "Call flax_get_site_model and use the current model and hash.",
    "Find the enabled Articles UI section and use its exact id as sectionId.",
    "Read model.articles[sectionId]. The canonical collection is its articles array; [] is the normal empty state. Do not search for items.",
    "For a new article, call flax_upsert_article with the article object and omit articleId. New articles are published by default; set published: false only when the user explicitly asks for an unpublished draft.",
    "If the Articles record exists but its articles field is missing, repair it with a generic set operation to articles.<sectionId>.articles = [] or let flax_upsert_article include that repair in the draft. Do not delete or recreate the UI section.",
    "If generic repair is needed, use flax_validate_model_update first, then flax_propose_model_update with the same current baseModelHash. Use set, append, replace, or remove operations against the current SiteDataModel.",
    "Return the exact previewUrl from the draft result. The owner approves and publishes in Flax; the agent never publishes."
  ],
  "articleInput": {
    "required": ["title"],
    "recommended": [
      "type: 'blog'",
      "shortDescription",
      "contentBlocks: [{ id, title?, description?, highlights? }]",
      "author",
      "date",
      "showOnHomepage: true",
      "enabled: true",
      "published: true",
      "cta: { title, sectionIntro, buttons } (optional)"
    ],
    "doNotInvent": [
      "Do not invent a section id when an enabled Articles section already exists.",
      "Do not replace the whole article collection when appending one article.",
      "Do not set published: false unless the user explicitly asks to keep the article out of the generated site.",
      "Omit cta, or leave its title, intro, and media empty with both buttons disabled, when the article should not have an article-specific CTA. A configured cta renders directly after the article detail as a normal CTA section."
    ],
    "validation": [
      "contentBlocks are optional; use them for inline article body copy when the article needs text sections. Articles may also contain only a title, short description, images, or other supported article fields.",
      "When no final image is available, omit images or leave the image array empty; the standard append follows the collection's existing media convention, adding Flax's built-in landscape placeholder only when sibling articles already use images and leaving the new item without images when the collection has none. If reusing an existing image, copy its complete image object by value in the new article and never use image_move, which removes the source image.",
      "Do not send contentBlocksPath. It is editor-managed browser storage and agents cannot populate it.",
      "Article contentBlocks descriptions support HTML rich text, including <strong>, <em>, <ul>, <ol>, and <li>.",
      "Use <ul> or <ol> with <li> items for checklists, grouped requirements, and ordered steps when useful. Use <strong> and <em> selectively rather than decorating every sentence or list item."
    ]
  },
  "genericJsonOperations": {
    "allowed": ["set", "append", "replace", "remove"],
    "examples": [
      {
        "op": "set",
        "path": "articles.<sectionId>.articles",
        "value": []
      },
      {
        "op": "append",
        "path": "articles.<sectionId>.articles",
        "values": ["new ArticleItem"]
      },
      {
        "op": "replace",
        "path": "articles.<sectionId>.articles.<index>.contentBlocks.<blockIndex>.description",
        "value": "Corrected article copy."
      }
    ],
    "limits": "The agent API validates the current model hash, known SiteDataModel roots, schema, operation limits, and owner-review draft before accepting operations."
  },
  "failureHandling": [
    "An enabled Articles section with zero records is not an error.",
    "A missing articles array is a repairable canonical-data issue, not a reason to ask the owner to remove and re-add the section.",
    "Only report a blocking error when the enabled section or its canonical articles record is genuinely absent, or when the agent lacks permission to propose changes for the site. State the exact missing condition."
  ]
}
