ovstorage-user-write-safely

Use when writing a new object without clobbering or updating an existing object with optimistic concurrency.

SKILL.md

GitHub

Write Safely (No Clobbering)

Goal: Upload an object's bytes to a path. By default, fail loudly if the destination already exists rather than silently overwriting.

When to use this: You're writing something new and the destination should not exist yet, OR you're updating an existing object and want to verify it hasn't changed since you last looked.

Recipe — new file, refuse to clobber

Pass if_dest: { "kind": "fail" } to refuse to overwrite. The call fails with AlreadyExists if the address is taken.

{
  "tool": "ovstorage_write",
  "arguments": {
    "address": "s3://my-bucket/output/report.json",
    "data_base64": "ewogICJyZXN1bHQiOiAib2sifQo=",
    "if_dest": { "kind": "fail" }
  }
}

Recipe — update with optimistic concurrency

Pass if_dest: { "kind": "match_etag", "etag": "<s>" } with the etag you read earlier. If another writer has changed the object since then, the call fails with PreconditionFailed and you can decide whether to merge or restart. Nothing was written — the destination is checked before anything is committed.

{
  "tool": "ovstorage_write",
  "arguments": {
    "address": "s3://my-bucket/state/counter.json",
    "data_base64": "eyJjIjogN30=",
    "if_dest": { "kind": "match_etag", "etag": "\"abc123\"" }
  }
}

Recipe — intentional overwrite

The default behavior when if_dest is omitted is to overwrite. Pass if_dest: { "kind": "overwrite" } explicitly to document intent.

{
  "tool": "ovstorage_write",
  "arguments": {
    "address": "s3://my-bucket/cache/latest.json",
    "data_base64": "...",
    "if_dest": { "kind": "overwrite" }
  }
}

What success looks like

{
  "v": "0.1",
  "ok": true,
  "operation": "ovstorage_write",
  "result": {
    "info": {
      "address": "s3://my-bucket/output/report.json",
      "size": 4231,
      "etag": "\"def456\"",
      "version": null
    }
  }
}

Save info.etag (and info.version if your backend has it) — you need them for any later optimistic-concurrency write or if_match read.

Optional parameters

  • user_metadata — map of {key: value} strings attached to the object. Treated as opaque by ovstorage; the backend persists what it can.
  • message — an annotation associated with the write (used as a commit message by backends that version objects, e.g. Nucleus checkpoints).

When things go wrong

error.code Likely cause What to do
AlreadyExists if_dest: {"kind": "fail"} and the destination exists Pick a different address, or drop if_dest (or set {"kind": "overwrite"}) after confirming you really want to clobber
PreconditionFailed if_dest: {"kind": "match_etag", ...} didn't match current identity; nothing was written Re-read with ovstorage_stat, decide whether to merge, retry with the new etag
ObjectModified The object moved during a call that had already started — a read whose bytes shifted, or a copy source replaced after staging Retry the whole call; the new etag rides Error.context as Identity { new_etag }
PermissionDenied Credentials can't write here Check ovstorage_doctor connection state
IntegrityFailure Bytes received didn't match a checksum (rare) Retry the write
Transient Backend hiccup retryable: true — try again
Unsupported Backend doesn't support etag-conditional writes Drop if_dest (or use {"kind": "overwrite"}); you lose the concurrency guarantee. Check ovstorage_capabilities to verify in advance

See also