canonical_id: "kbdb_upsert_block"
display_name: "KBDB Upsert Block"
category: "data"
version: "v1"
wasi_target: "preview1"
stability: "floating"
runtime_compat:
  - "cf-workers"
  - "workerd"
  - "wazero"
constraints:
  max_size_kb: 2048
  max_cold_start_ms: 50
  no_network_syscall: false
  no_filesystem_syscall: true
  io_model: "stdin_stdout_json"
input_schema:
  type: object
  required: [api_key, page_name, content]
  properties:
    api_key:
      type: string
      description: KBDB partner key（ak_xxx）
    page_name:
      type: string
      description: 當 idempotency key。內部用 GET /blocks?page_name= 查找。
    content:
      type: string
      description: block 內容（PATCH 時覆寫，CREATE 時新建）
    type:
      type: string
      description: block type（建立時用，PATCH 時忽略）
    parent_id:
      type: string
      description: 父 block id（建立時用，PATCH 時忽略）
    user_id:
      type: string
      description: 建立時帶入 + lookup 時用來 filter（同 page_name 多 user 共存場景）
    source:
      type: string
      description: 來源標記
    tags_json:
      type: string
      description: tags JSON 字串（PATCH 時轉 array、CREATE 時直傳）
    kbdb_url:
      type: string
      description: KBDB API base（預設 https://kbdb.finally.click）
output_schema:
  type: object
  properties:
    success:
      type: boolean
    action:
      type: string
      enum: [created, patched]
      description: 實際做了哪個動作
    data:
      type: object
      description: KBDB 回傳（含 block id 等）
    error:
      type: string
    phase:
      type: string
      enum: [lookup, patch, create]
      description: 出錯在哪個階段
gherkin_tests:
  - scenario: "缺 page_name"
    given: '{"api_key":"ak_x","content":"hi"}'
    then_contains: '"success":false'
  - scenario: "建立新 block"
    given: '{"api_key":"ak_x","page_name":"new-page-uniq","content":"hello"}'
    then_contains: '"action":"created"'
  - scenario: "PATCH 既有 block"
    given: '{"api_key":"ak_x","page_name":"existing-page","content":"updated"}'
    then_contains: '"action":"patched"'
tags: [data, storage, kbdb, upsert, primitive, idempotent]
description: |
  Upsert：用 page_name 當 idempotency key。內部 GET 找有沒有同 page_name 的 block，
  找到就 PATCH 不到就 POST 新建。解 arcrun workflow 缺 IF/branch 能力的缺口
  （arcrun.md P1 #1）。mira 7B.3f index-entry per-entity 維護是第一個使用者。
config_example: |
  upsert_index_entry:
    api_key: "{{api_key}}"
    page_name: "index-{{entity}}"
    parent_id: "{{mira_wiki_index_entities_id}}"
    type: "index-entry"
    user_id: "inkstone_mira_tools"
    source: "ai-canon-wiki"
    content: "{{compose_index_entry.data.text}}"
    tags_json: '["mira-wiki", "ai-generated", "index"]'
