arcodangeandClaude Fable 5 3840e74dcd feat(write-skill): idempotency keys — manifest replay is a no-op (erp#44)
Learning #4 of the 2026-07-11 rehearsal: manifest B failed mid-run and could
not be re-applied — op 1 (the DARNIS invoice) had already run and a replay
would have duplicated it. Every write op now dedupes BEFORE any POST:

- thirdparty-create.sh: by exact name (promote '#thirdparty:name=' semantics);
  ambiguous (2+) aborts; an existing fiche missing the requested role aborts
  (refuse-never-repair). Emits {"id", "deduped"} instead of a bare id.
- invoice-create.sh: supplier kind by (socid, ref_supplier) — same key with a
  different total aborts as a conflict; customer kind (or supplier without
  ref_supplier) by (socid, date, total_ttc ±0.02, line fingerprint) with descs
  HTML-unescaped. Credit notes are never candidates. A deduped DRAFT with
  validate:true is validated on replay, so an interrupted run converges.
- payment-record.sh: by (invoice, amount, normalized transaction_id), composing
  with the erp#37 varchar(50) normalization on BOTH sides so historical
  long-form nums still match; same tx + different amount aborts; without a tx
  id there is no dedupe key (warned). Dedupe answers id:null (the payments list
  exposes no paiement rowid) + the existing bank line.
- All three refuse to POST blind when the dedupe lookup fails with anything but
  the documented empty-list 404 (the voir_tous trap would otherwise mint dupes).
- promote-apply.sh: marks each op created / deduped=true inline and totals them
  in the summary — an all-deduped second run is visible proof of a no-op.
- promote-plan.sh: advertises each op's dedupe key (and flags tx=MISSING as
  'a replay WILL double-pay').

Proof:
- tests/run-tests.sh: 5 new offline cases (11 total) — dedupe hits POST
  nothing, conflicts/ambiguity abort pre-POST, long-form history dedupes,
  draft convergence validates; stub extended to serve the new lookups with the
  live-observed empty behaviors ([] for invoices/payments, 404 for tiers).
- tests/replay-idempotency.sh (new, live): double-applies a self-contained
  manifest on the sandbox — run 1 '3 created' (rows 1/1/1), run 2 '3 deduped'
  with row counts unchanged and the stored num in erp#37 short form.
- The historic manifest-B now replays on the sandbox as 5/5 deduped, zero new
  rows — the exact replay Learning #4 declared impossible.

SKILL.md updated in the same change (per-op dedupe keys, replay-safety section,
gotchas); the 2026-07-11 runbook's Learning #4 carries a dated resolution
addendum.

Closes erp#44.

Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01VRShc4QhLLU73FLHx9vskh
2026-07-18 23:46:05 +02:00

ERP

CLI — bin/arcodange

Read-only operational CLI for the Arcodange Dolibarr at erp.arcodange.lab. One entry point, subcommands per domain:

bin/arcodange ping                          # Dolibarr version + liveness
bin/arcodange whoami                        # confirm auth as ai_agent
bin/arcodange invoice list                  # KissMetrics invoices with payment state
bin/arcodange invoice audit 12              # JSON facts + PDF mandatory-mention audit
bin/arcodange payments state                # per-invoice TTC vs payments reconciliation
bin/arcodange payments timeline --year 2026 # cash receipts with cumulative balance
bin/arcodange tva summary                   # CA3-ready collectée  déductible per month
bin/arcodange thirdparty audit-all          # completeness audit, country-aware
bin/arcodange templates inspect 1           # recurring template health (frequency, next fire, …)
bin/arcodange snapshot --out /tmp/erp.json  # full state dump with content_hash
bin/arcodange help                          # full command tree

Read-only by design. The underlying API key (ai_agent) has no write permissions; corrections go through the Dolibarr UI.

Credentials. Reads .claude/skills/dolibarr/.env (mode 600, gitignored). Setup instructions: .claude/skills/dolibarr/README.md.

Source of behaviour. Each subcommand delegates to a script under .claude/skills/<skill>/scripts/. The skills' SKILL.md files document the business logic and are also discoverable by Claude Code via skill triggers.

Dolibarr

Premiers démarrages

Si l'application log au démarrage l'erreur suivante:

Importing custom SQL from update_table_ownership.sql ...
sed: couldn't open temporary file /var/www/scripts/before-starting.d/sedwHcRlQ: Read-only file system

Il faudra prendre la main du shell du pod et executer:

kubectl exec -n erp `kubectl get pod -n erp -l app.kubernetes.io/name=erp -o=name` -c erp -- sh -c 'PGPASSWORD=${DOLI_DB_PASSWORD} psql -U ${DOLI_DB_USER} -h ${DOLI_DB_HOST} -p ${DOLI_DB_HOST_PORT} ${DOLI_DB_NAME} \
-f /var/www/scripts/before-starting.d/update_table_ownership.sql'

Sous peine de ne plus avoir les droits de consulter la base de données une fois les crédentials mis à jour par vault. Dans ce cas executer la commande mais avec les credentials d'admin postgres.

S
Description
No description provided
Readme
2.5 MiB
Languages
Shell 44.9%
HTML 25.3%
TypeScript 17.4%
Python 11.8%
HCL 0.4%
Other 0.2%