Context
Surfaced while recording observations from an agent session in a private repository, so the originating work is not linkable here.
Problem
The store is append-only, with no edit and no delete. Every write is final. But nothing on either side of that write lets a caller confirm what actually landed:
- Before the write there is no way to see the record that would be created.
- After the write the response is
{id, recorded_at} — facts about the write having occurred, not about its content.
So a caller that submits malformed content receives no signal at all. The tool description says "only malformed input is rejected," and that holds for structural requirements — a missing required property is refused with a clear message. It does not hold for content that is structurally valid and semantically wrong, which is accepted and made permanent.
Evidence
A record was written whose observation text ends with a literal fragment of the caller's own tool-call markup:
…suggesting the class is mechanically detectable where the description
and the classification sit adjacent.</observation>
<parameter name="scope_kind">project
The caller malformed the scope_kind argument. Because that property has a server-side default, the request still validated, the default applied, and the stray markup landed inside the previous property's value. The write returned success. The damage was found on a later read, by which point it was permanent.
The asymmetry that produced it is the part worth noting. The identical caller error against a required property (class) was rejected outright, with a clear message, and cost nothing but a retry. Against a property with a default, the same error was accepted silently and cannot be undone. Properties with defaults are therefore the more dangerous surface — and they are the ones a caller is likeliest to get wrong, since omitting them is legitimate.
The tool description separately warns against pasting credentials, tokens, or raw error payloads, because "the docket is stored unencrypted and entries cannot be removed in-band." That warning describes this same exposure from the other side: it asks the caller to be careful precisely because nothing downstream can help. A caller who is careful and still wrong has no recourse.
Suggested approaches
Options rather than a prescription, ordered by what each asks of the caller rather than by power. The failure above was an attention lapse, so a mitigation that has to be remembered is guarded by the same faculty that failed.
-
Reject structurally-suspect content at write time. Observation text ending in an angle-bracketed parameter name= fragment is never legitimate prose. A narrow check would convert this silent path into the loud one callers demonstrably recover from. Fires unconditionally and asks nothing of the caller; bounded to shapes the server can recognise.
-
Return the stored content on write. Echo the observation as stored — or its tail — alongside {id, recorded_at}, so a discrepancy surfaces at the moment it happens rather than on a later read. Also unconditional, but it informs after permanence rather than preventing it.
-
Preview-then-commit with a replayed token. A dry_run returns the would-be record plus a short-lived token; the commit accepts only that token. Because the commit re-emits no content, there is no second construction to get wrong — which is what separates this from a plain dry-run, where preview and commit are independent emissions and only the first is inspected. The strongest option, and the only one that also covers the credential-paste case, but it has to be invoked to help.
-
A supersede relation. An additive supersedes: <id> edge, letting a correction point at a damaged record without deleting anything — preserving the append-only ledger rather than weakening it. Repairs rather than prevents, so worth considering only if the class recurs.
Options 1 and 3 compose: the floor catches what the server can recognise with no discipline required, and the preview covers what it cannot.
Context
Surfaced while recording observations from an agent session in a private repository, so the originating work is not linkable here.
Problem
The store is append-only, with no edit and no delete. Every write is final. But nothing on either side of that write lets a caller confirm what actually landed:
{id, recorded_at}— facts about the write having occurred, not about its content.So a caller that submits malformed content receives no signal at all. The tool description says "only malformed input is rejected," and that holds for structural requirements — a missing required property is refused with a clear message. It does not hold for content that is structurally valid and semantically wrong, which is accepted and made permanent.
Evidence
A record was written whose
observationtext ends with a literal fragment of the caller's own tool-call markup:The caller malformed the
scope_kindargument. Because that property has a server-side default, the request still validated, the default applied, and the stray markup landed inside the previous property's value. The write returned success. The damage was found on a later read, by which point it was permanent.The asymmetry that produced it is the part worth noting. The identical caller error against a required property (
class) was rejected outright, with a clear message, and cost nothing but a retry. Against a property with a default, the same error was accepted silently and cannot be undone. Properties with defaults are therefore the more dangerous surface — and they are the ones a caller is likeliest to get wrong, since omitting them is legitimate.The tool description separately warns against pasting credentials, tokens, or raw error payloads, because "the docket is stored unencrypted and entries cannot be removed in-band." That warning describes this same exposure from the other side: it asks the caller to be careful precisely because nothing downstream can help. A caller who is careful and still wrong has no recourse.
Suggested approaches
Options rather than a prescription, ordered by what each asks of the caller rather than by power. The failure above was an attention lapse, so a mitigation that has to be remembered is guarded by the same faculty that failed.
Reject structurally-suspect content at write time. Observation text ending in an angle-bracketed
parameter name=fragment is never legitimate prose. A narrow check would convert this silent path into the loud one callers demonstrably recover from. Fires unconditionally and asks nothing of the caller; bounded to shapes the server can recognise.Return the stored content on write. Echo the observation as stored — or its tail — alongside
{id, recorded_at}, so a discrepancy surfaces at the moment it happens rather than on a later read. Also unconditional, but it informs after permanence rather than preventing it.Preview-then-commit with a replayed token. A
dry_runreturns the would-be record plus a short-lived token; the commit accepts only that token. Because the commit re-emits no content, there is no second construction to get wrong — which is what separates this from a plain dry-run, where preview and commit are independent emissions and only the first is inspected. The strongest option, and the only one that also covers the credential-paste case, but it has to be invoked to help.A supersede relation. An additive
supersedes: <id>edge, letting a correction point at a damaged record without deleting anything — preserving the append-only ledger rather than weakening it. Repairs rather than prevents, so worth considering only if the class recurs.Options 1 and 3 compose: the floor catches what the server can recognise with no discipline required, and the preview covers what it cannot.