Part of the AI back-office PRD — challenge C2 + atom-contract idempotency_key.
Context. Learning #4 of the 2026-07-11 rehearsal: manifest B failed mid-run (varchar(50), fixed by erp#37) and could NOT be safely re-applied — op 1 (invoice create) had already run; a replay would have duplicated the DARNIS invoice. A partial failure currently forces manual manifest surgery.
Deliverables
invoice-create.sh: pre-POST dedupe lookup — supplier kind by (socid, ref_supplier); customer kind by (socid, date, total_ttc, line fingerprint). Existing match → return the existing id with "deduped": true instead of creating.
payment-record.sh: dedupe by (invoice, amount, normalized transaction_id) against the invoice's payment list.
thirdparty-create.sh: dedupe by exact name (abort on ambiguous, per the promote lookup semantics).
Replay test: apply a manifest twice on a sandbox checkpoint → second run reports all-deduped, zero new rows.
Acceptance: the replay test passes; promote-apply output distinguishes created vs deduped ops.
Execution — Blocked by: none · Blocks: #47, #52 · Human gates: none. Start:claude --worktree idempotency-keys from the erp repo root; read the three write scripts + the erp#37 tx-id normalization (merged) they must compose with; replay fixtures available in the preserved replay pack. Done means: evidence comment here (double-apply output: run 1 created / run 2 all-deduped) → PR merged with Closes #44.
Part of the **AI back-office PRD** — [challenge C2](https://gitea.arcodange.lab/arcodange-org/factory/src/branch/main/vibe/PRD/ai-back-office/challenges.md) + atom-contract `idempotency_key`.
**Context.** Learning #4 of the 2026-07-11 rehearsal: manifest B failed mid-run (varchar(50), fixed by erp#37) and could NOT be safely re-applied — op 1 (`invoice` create) had already run; a replay would have duplicated the DARNIS invoice. A partial failure currently forces manual manifest surgery.
**Deliverables**
- `invoice-create.sh`: pre-POST dedupe lookup — supplier kind by (socid, `ref_supplier`); customer kind by (socid, date, total_ttc, line fingerprint). Existing match → return the existing id with `"deduped": true` instead of creating.
- `payment-record.sh`: dedupe by (invoice, amount, normalized transaction_id) against the invoice's payment list.
- `thirdparty-create.sh`: dedupe by exact name (abort on ambiguous, per the promote lookup semantics).
- Replay test: apply a manifest twice on a sandbox checkpoint → second run reports all-deduped, zero new rows.
**Acceptance:** the replay test passes; `promote-apply` output distinguishes created vs deduped ops.
---
**Execution** — Blocked by: none · Blocks: #47, #52 · Human gates: none.
**Start:** `claude --worktree idempotency-keys` from the erp repo root; read the three write scripts + the erp#37 tx-id normalization (merged) they must compose with; replay fixtures available in the preserved replay pack.
**Done means:** evidence comment here (double-apply output: run 1 created / run 2 all-deduped) → PR merged with `Closes #44`.
arcodange
added this to the P1 — Flagship pipeline milestone 2026-07-11 18:17:29 +02:00
Evidence — live double-apply on the sandbox (2026-07-18), acceptance PASSED.
Checkpoint status checked first (ARMED, ai_agent_sandbox id=4); no checkpoint refresh run. tests/replay-idempotency.sh (committed) builds a self-contained manifest (thirdparty + validated supplier invoice + Qonto-style-tx payment) and applies it twice:
-- RUN 1: expect 3 created --
[1/3] thirdparty @tp -> id=13 created
[2/3] invoice @inv -> id=30 created
[3/3] payment -> id=17 created
OK — promote complete (3 created). ref -> id: {"tp": 13, "inv": 30}
row counts after run 1: thirdparties=1 invoices=1 payments=1
-- RUN 2 (same manifest): expect 3 deduped, zero new rows --
[1/3] thirdparty @tp -> id=13 deduped=true (already on target — no write)
[2/3] invoice @inv -> id=30 deduped=true (already on target — no write)
[3/3] payment -> id=- deduped=true (already on target — no write)
OK — promote complete (3 deduped). ref -> id: {"tp": 13, "inv": 30}
row counts after run 2: thirdparties=1 invoices=1 payments=1
PASS — stored payment num verified in erp#37 short form
Closing proof: the actual 2026-07-11 manifest-B-suppliers.json — the manifest Learning #4 declared unreplayable — now replays on the sandbox as 5/5 deduped, zero new rows (DARNIS invoice → id 29, all four payments matched by normalized tx). The runbook's Learning #4 carries a dated resolution addendum.
Offline suite: 11/11 green (dedupe hits POST nothing; conflicts/ambiguity/role-mismatch abort pre-POST; long-form historical tx nums dedupe; draft convergence validates).
Dedupe keys shipped: thirdparty = exact name (ambiguous aborts) · supplier invoice = (socid, ref_supplier), conflict on total aborts · customer invoice = (socid, date, total_ttc ±0.02, line fingerprint) · payment = (invoice, amount, normalized transaction_id). Known holes documented in SKILL.md: payments without tx id have no dedupe key (warned, plan flags tx=MISSING); creditnote-create.sh out of scope — follow-up issue candidate.
Sandbox fixtures left behind (tp 13 / inv 30 / pay 17 + the manifest-B replay rows) — disposable, reclaimed by the next human-run checkpoint refresh.
Shipped in PR #70. Built by a Claude subagent lane; reviewed + merged by the session orchestrator under the operator's in-session delegation.
**Evidence — live double-apply on the sandbox (2026-07-18), acceptance PASSED.**
Checkpoint status checked first (ARMED, `ai_agent_sandbox` id=4); no `checkpoint refresh` run. `tests/replay-idempotency.sh` (committed) builds a self-contained manifest (thirdparty + validated supplier invoice + Qonto-style-tx payment) and applies it twice:
```
-- RUN 1: expect 3 created --
[1/3] thirdparty @tp -> id=13 created
[2/3] invoice @inv -> id=30 created
[3/3] payment -> id=17 created
OK — promote complete (3 created). ref -> id: {"tp": 13, "inv": 30}
row counts after run 1: thirdparties=1 invoices=1 payments=1
-- RUN 2 (same manifest): expect 3 deduped, zero new rows --
[1/3] thirdparty @tp -> id=13 deduped=true (already on target — no write)
[2/3] invoice @inv -> id=30 deduped=true (already on target — no write)
[3/3] payment -> id=- deduped=true (already on target — no write)
OK — promote complete (3 deduped). ref -> id: {"tp": 13, "inv": 30}
row counts after run 2: thirdparties=1 invoices=1 payments=1
PASS — stored payment num verified in erp#37 short form
```
**Closing proof:** the actual 2026-07-11 `manifest-B-suppliers.json` — the manifest Learning #4 declared unreplayable — now replays on the sandbox as **5/5 deduped, zero new rows** (DARNIS invoice → id 29, all four payments matched by normalized tx). The runbook's Learning #4 carries a dated resolution addendum.
Offline suite: 11/11 green (dedupe hits POST nothing; conflicts/ambiguity/role-mismatch abort pre-POST; long-form historical tx nums dedupe; draft convergence validates).
Dedupe keys shipped: thirdparty = exact name (ambiguous aborts) · supplier invoice = (socid, ref_supplier), conflict on total aborts · customer invoice = (socid, date, total_ttc ±0.02, line fingerprint) · payment = (invoice, amount, normalized transaction_id). Known holes documented in SKILL.md: payments without tx id have no dedupe key (warned, plan flags `tx=MISSING`); `creditnote-create.sh` out of scope — follow-up issue candidate.
Sandbox fixtures left behind (tp 13 / inv 30 / pay 17 + the manifest-B replay rows) — disposable, reclaimed by the next human-run checkpoint refresh.
Shipped in PR #70. Built by a Claude subagent lane; reviewed + merged by the session orchestrator under the operator's in-session delegation.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Part of the AI back-office PRD — challenge C2 + atom-contract
idempotency_key.Context. Learning #4 of the 2026-07-11 rehearsal: manifest B failed mid-run (varchar(50), fixed by erp#37) and could NOT be safely re-applied — op 1 (
invoicecreate) had already run; a replay would have duplicated the DARNIS invoice. A partial failure currently forces manual manifest surgery.Deliverables
invoice-create.sh: pre-POST dedupe lookup — supplier kind by (socid,ref_supplier); customer kind by (socid, date, total_ttc, line fingerprint). Existing match → return the existing id with"deduped": trueinstead of creating.payment-record.sh: dedupe by (invoice, amount, normalized transaction_id) against the invoice's payment list.thirdparty-create.sh: dedupe by exact name (abort on ambiguous, per the promote lookup semantics).Acceptance: the replay test passes;
promote-applyoutput distinguishes created vs deduped ops.Execution — Blocked by: none · Blocks: #47, #52 · Human gates: none.
Start:
claude --worktree idempotency-keysfrom the erp repo root; read the three write scripts + the erp#37 tx-id normalization (merged) they must compose with; replay fixtures available in the preserved replay pack.Done means: evidence comment here (double-apply output: run 1 created / run 2 all-deduped) → PR merged with
Closes #44.Evidence — live double-apply on the sandbox (2026-07-18), acceptance PASSED.
Checkpoint status checked first (ARMED,
ai_agent_sandboxid=4); nocheckpoint refreshrun.tests/replay-idempotency.sh(committed) builds a self-contained manifest (thirdparty + validated supplier invoice + Qonto-style-tx payment) and applies it twice:Closing proof: the actual 2026-07-11
manifest-B-suppliers.json— the manifest Learning #4 declared unreplayable — now replays on the sandbox as 5/5 deduped, zero new rows (DARNIS invoice → id 29, all four payments matched by normalized tx). The runbook's Learning #4 carries a dated resolution addendum.Offline suite: 11/11 green (dedupe hits POST nothing; conflicts/ambiguity/role-mismatch abort pre-POST; long-form historical tx nums dedupe; draft convergence validates).
Dedupe keys shipped: thirdparty = exact name (ambiguous aborts) · supplier invoice = (socid, ref_supplier), conflict on total aborts · customer invoice = (socid, date, total_ttc ±0.02, line fingerprint) · payment = (invoice, amount, normalized transaction_id). Known holes documented in SKILL.md: payments without tx id have no dedupe key (warned, plan flags
tx=MISSING);creditnote-create.shout of scope — follow-up issue candidate.Sandbox fixtures left behind (tp 13 / inv 30 / pay 17 + the manifest-B replay rows) — disposable, reclaimed by the next human-run checkpoint refresh.
Shipped in PR #70. Built by a Claude subagent lane; reviewed + merged by the session orchestrator under the operator's in-session delegation.