feat(write-skill): chronology guard + explicit production opt-in
Two guards, both from real incidents in the same session. 1. invoice-create.sh — chronology (CGI art. 289). Dolibarr assigns the number at validation, in creation order, so issuing a document dated BEFORE the last one already issued gives a higher number to an earlier date. The July plan walked straight into it: the M3 deferred part is due 2026-10-23 and must be issued at D-60 (24/08) to stay under the L.441-10 I ceiling, while the M4 fixed part is dated 23/08 — issue them in the wrong order and the numbering breaks. The guard reads the last issued document of the same kind and refuses an earlier date, with ARCO_ALLOW_BACKDATE as a loud, documented override. Verified: refuses a 01/07 invoice against FAC008 (23/07), accepts 23/08. 2. test/scripts/guard.ts — production opt-in. The sandbox-only guard had no way to express a deliberate production run, so any prod work meant bypassing it entirely (which is how guards die). Production now requires BOTH ARCO_ALLOW_PRODUCTION=<exact host> and ARCO_PROD_CONFIRM=I-UNDERSTAND-THIS-WRITES-PROD, and prints a banner. Nothing reaches prod by inheriting an ambient variable. Also fixes a misleading "(sandbox verified)" log that printed even on prod. grantAgentRight.ts joins the repo (it was never committed) and gains --revoke, so a temporarily elevated right can be handed back — used today to attach a payment in production and revoked immediately after. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]> Claude-Session: https://claude.ai/code/session_01VRShc4QhLLU73FLHx9vskh
This commit is contained in:
@@ -188,6 +188,37 @@ if [[ -n "${MATCH}" ]]; then
|
||||
fi
|
||||
|
||||
# --- Create (no match) ----------------------------------------------------------
|
||||
# --- Chronology guard (CGI art. 289: numbering must be chronological) --------
|
||||
# Dolibarr assigns the next number at validation, in creation order — so issuing
|
||||
# a document dated BEFORE the last one already issued yields a higher number on
|
||||
# an earlier date, which is a numbering break. This bites whenever two documents
|
||||
# of the same cycle are issued on different days (a deferred part issued after
|
||||
# the next fixed part, for instance). Refuse rather than create the break.
|
||||
NEW_DATE="$(python3 -c "import json,sys; print(json.loads(sys.argv[1])['date'])" "${BODY}")"
|
||||
LAST="$("${W}" GET "${ENDPOINT}?sortfield=t.rowid&sortorder=DESC&limit=1" 2>/dev/null \
|
||||
| python3 -c "
|
||||
import json,sys
|
||||
try:
|
||||
d=json.load(sys.stdin)
|
||||
if isinstance(d,list) and d: print(f\"{d[0].get('date','0')}|{d[0].get('ref','?')}\")
|
||||
else: print('0|-')
|
||||
except Exception: print('0|-')" 2>/dev/null || echo "0|-")"
|
||||
LAST_DATE="${LAST%%|*}"; LAST_REF="${LAST##*|}"
|
||||
if [[ "${LAST_DATE}" =~ ^[0-9]+$ ]] && (( LAST_DATE > 0 )) && (( NEW_DATE < LAST_DATE )); then
|
||||
if [[ "${ARCO_ALLOW_BACKDATE:-}" != "I-UNDERSTAND-THIS-BREAKS-CHRONOLOGY" ]]; then
|
||||
printf 'invoice-create.sh: REFUSED — chronology break.\n' >&2
|
||||
printf ' new document dated %s, but %s is already issued at %s.\n' \
|
||||
"$(date -r "${NEW_DATE}" +%d/%m/%Y 2>/dev/null || echo "${NEW_DATE}")" \
|
||||
"${LAST_REF}" "$(date -r "${LAST_DATE}" +%d/%m/%Y 2>/dev/null || echo "${LAST_DATE}")" >&2
|
||||
printf ' Numbering follows creation order, so this would give a higher number to an\n' >&2
|
||||
printf ' earlier date (CGI art. 289). Issue in chronological order, or set\n' >&2
|
||||
printf ' ARCO_ALLOW_BACKDATE=I-UNDERSTAND-THIS-BREAKS-CHRONOLOGY to override.\n' >&2
|
||||
exit 1
|
||||
fi
|
||||
printf 'invoice-create.sh: WARNING — backdating past %s (%s), override accepted.\n' \
|
||||
"${LAST_REF}" "$(date -r "${LAST_DATE}" +%d/%m/%Y 2>/dev/null)" >&2
|
||||
fi
|
||||
|
||||
ID="$("${W}" POST "${ENDPOINT}" "${BODY}")"
|
||||
if [[ ! "${ID}" =~ ^[0-9]+$ ]]; then
|
||||
echo "invoice-create.sh: create did not return an id: ${ID}" >&2
|
||||
|
||||
Reference in New Issue
Block a user