3033: [factory] Successful Slack receipt is re-ingested as a write and rejected on read-only ts

AG
2mo ago
+933-84

Summary

After Relayfile provider-confirmed a Slack reply_in_thread, a follow-on operation reprocessed the materialized result and attempted to write Slack's read-only ts field. The user-visible delivery succeeded, but the lifecycle then emitted a contradictory failure.

A provider receipt must be terminal evidence for that operation, not new writable provider input. This is distinct from the missing-writeback-scope failure tracked in #2029.

Production evidence

Observed on 2026-08-15 while replying to the engineering-triage Slack thread.

Successful operation:

  • Relayfile op: op_232967
  • provider: Slack
  • action: reply_in_thread
  • endpoint: /chat.postMessage
  • HTTP status: 200
  • channel: C0B9Z4CLG1J
  • provider message ts: 1786747030.233189
  • acknowledged at: 2026-08-14T22:37:11.171Z

Follow-on failure:

  • Relayfile op: op_232968
  • error: Field "ts" is read-only and cannot be written

The second failure did not undo delivery, but it makes success ambiguous and can provoke retries or duplicate notifications.

Required behavior

  • Separate requested write payloads from materialized provider receipts.
  • Never feed provider-owned/read-only fields such as Slack ts back into create/update inputs.
  • Treat the provider-confirmed receipt for op_232967 as terminal and idempotent.
  • Reconciliation may enrich local state from the receipt, but must not synthesize a second provider write.
  • Surface any post-success bookkeeping failure separately from delivery outcome.

Definition of done

  • Regression fixture reproduces a successful reply_in_thread receipt containing ts and proves no follow-on provider write is generated.
  • Replaying/reconciling the same mounted receipt is a no-op and cannot duplicate the Slack message.
  • Delivery status remains succeeded if later local bookkeeping fails, with the bookkeeping error reported separately.
  • Add contract coverage for provider-owned fields across create/reply/update paths.
  • Verify with a provider-style receipt through the built runtime/writeback path.

Related: #2029

Fixes #3033