Skip to content

ldh-CreateContainer and ldh-CreateItem only create: If-None-Match: *, and if_absent to accept an existing document - #68

Merged
namedgraph merged 1 commit into
mainfrom
create-if-none-match
Oct 28, 2026
Merged

namedgraph merged 1 commit into
mainfrom
create-if-none-match

Conversation

@namedgraph

Copy link
Copy Markdown
Member

ldh-CreateContainer and ldh-CreateItem wrote through the client's conditional write. That HEADs the target for its entity tag and quotes it in If-Match, so on a URI that already held a document, a create replaced it whole, content blocks included. The system prompt even said so ("A document that already exists at that URL is replaced"). Pipelines that "make sure" a container exists on every run were wiping whatever had been added to it since. The case that surfaced this: a nightly importer replaced /messages/ and the view installed on it.

This brings the operations in line with LinkedDataHub's client (its Container/Item modals) and with the CLI change in AtomGraph/LinkedDataHub#505:

  • Create only creates. client.create() PUTs with If-None-Match: *, no HEAD and no If-Match. On an existing document the server answers 412 under the document's write lock and leaves it as it was. The operation raises WriteRefusedError (412), whose reason says a document already exists.
  • if_absent (xsd:boolean) is the counterpart of --if-absent, and works like mkdir -p. An existing document is the result: the single status/url row, status 412, writing nothing. That URL is not reported to an enclosing ForEach's iteration gate (§3.6), since nothing was written. So a loop can make sure of the same container on every iteration.
  • Kind write, no longer destructive: a create can't remove anything now.
  • PUT is unchanged: it still replaces, quoting the tag.
  • Spec (Appendix A), system prompt (both operations' sections and headings) and CHANGELOG (Unreleased, Changed) are updated.

Behaviour change for callers:

  • A pipeline that relied on a create replacing a document should PUT it instead.
  • ldh-GenerateClassContainers creates through ldh-CreateItem, so on a re-run it now stops at the first class whose item already exists, instead of replacing it. if_absent isn't right there either: it would keep the item and append a second set of blocks.

Tests

  • New: tests/unit/test_ldh_create_if_absent.py, offline against http_stub. The stub tags every resource, so a create that quoted the tag would show. It covers both operations:
    • the create sends If-None-Match: *, no HEAD, no If-Match;
    • a 412 raises "already exists" after one request;
    • if_absent returns the existing URL after one request;
    • if_absent still creates when nothing is there;
    • if_absent doesn't swallow other refusals (403);
    • if_absent must be a boolean;
    • PUT still sends If-Match;
    • a ForEach can make sure of the same container on every iteration;
    • the kind and schema are right.
  • Mutation-checked:
    • with client.create reading and quoting the tag again, 7 of the new tests fail;
    • with the if_absent path reporting a write, the ForEach test fails.
  • Updated: the kind test (creates are WRITE), and test_write_contract's If-Match table, which no longer lists ldh-CreateItem.
  • pytest: 1109 passed, 7 skipped.
  • Not run: the live test_ldh_cli_parity (it needs a LinkedDataHub). Once #505 is in, an if_absent ↔ --if-absent case belongs there.

🤖 Generated with Claude Code

… and if_absent to accept an existing document

They PUT through the client's conditional write, which reads the document's entity tag
and quotes it in If-Match, so a create on an existing URI replaced the document whole,
content blocks and all. They now send If-None-Match: * and no If-Match, as the
LinkedDataHub CLI's create commands and its client's create forms do; the server refuses
that 412 where a document exists and leaves it as it is. That fails the operation, unless
if_absent is true, which takes the existing document as the result, like mkdir -p, and
does not report it as a write to an enclosing ForEach's iteration gate. Their kind is
write. The system prompt said a create replaces; it says it only creates.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
@namedgraph
namedgraph merged commit fc25ddf into main Oct 28, 2026
12 checks passed
@namedgraph
namedgraph deleted the create-if-none-match branch October 28, 2026 23:57
@namedgraph namedgraph mentioned this pull request Oct 28, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant