Every write that touches NetSuite goes through this path: a proposed_action claimed by Mike, an ns_pending_pushes insert, a Cloudflare Queue handoff, a NetSuite RESTlet write, and finally a status flip to applied. R560 closed a CRITICAL race condition where two reviewers could claim the same row; the atomic UPDATE ... RETURNING claim guarantees one winner. Failures retry 3x then land in the DLQ.
status='pending'.proposed_actionsaction_type · target_record · proposed_change_json · risk_tier · statusupdate_vendor_cost)POST /api/proposed-actions/:id/decideUPDATE proposed_actions SET status='approved', claim=? WHERE id=? AND status='pending' RETURNING *X-Edit-Token header (ADR-031)ns_pending_pushespayload_json built from proposed_change_json + canonical NS field mapstatus='queued'env.NS_PUSH_QUEUEenv.NS_PUSH_QUEUE.send({ id, payload })queuedqueue() entrypoint in src/index.tscustomscript_gfs_platform_query (R55)src/lib/ns.ts (~100 LOC)ns_internal_id for the affected recordns_pending_pushes flips status and stamps timestamp.status='applied', applied_at=CURRENT_TIMESTAMP, ns_internal_id=?proposed_actions_applied_mirrorgfs-ns-push-dlq queue.env.NS_PUSH_DLQns_push_dlq_log (id, payload_json, error_json, attempts, last_at)POST /api/audit-dlq/replay — admin-only; reinserts into ns_pending_pushes| kind | name | purpose |
|---|---|---|
| table | proposed_actions | HITL queue — every NS write proposal lives here first |
| table | ns_pending_pushes | queued-for-push payloads after approval |
| table | ns_push_dlq_log | 3rd-failure audit + replay surface |
| table | proposed_actions_applied_mirror | audit mirror of applied decisions (R563) |
| endpoint | POST /api/proposed-actions/:id/decide | HITL approve/reject — atomic claim |
| endpoint | POST /api/ns-push/drain | manual drain trigger |
| endpoint | POST /api/audit-dlq/replay | replay DLQ entry back to ns_pending_pushes |
| binding | env.NS_PUSH_QUEUE | CF Queue producer/consumer |
| binding | env.NS_PUSH_DLQ | dead-letter queue |
| RESTlet | customscript_gfs_platform_query | NetSuite endpoint for writes (R55 TBA) |
Pre-R560 the decide flow was a SELECT-then-UPDATE. Two reviewers could each see status='pending' and both submit approve — resulting in two duplicate NS writes. Codex audit CRITICAL #1.
-- Pre-R560 (BROKEN): SELECT status FROM proposed_actions WHERE id=?; -- both see 'pending' UPDATE proposed_actions SET status='approved' WHERE id=?; -- both succeed -- R560 (FIXED): UPDATE proposed_actions SET status='approved', claim=?, decided_at=CURRENT_TIMESTAMP WHERE id=? AND status='pending' RETURNING id, proposed_change_json; -- second concurrent call returns 0 rows