feat(write-skill): GED attach op — upload the source document onto its invoice #72

Merged
arcodange merged 1 commits from arcodange/ged-attach into main 2026-07-19 00:13:29 +02:00
Owner

Closes #43.

Fills the gap confirmed during the 2026-07-11 regularization: the write skill had no document-attach op, so rehearsed invoices carried no PDF (PAF: originals hash-addressed in the GED).

  • document-attach.shPOST /documents/upload (base64, overwriteifexists always 0), addressed by object_id or ref (cross-checked; ambiguous ref aborts). Idempotent by (object, filename, sha256) — own check, not Dolibarr's overwrite flag: same bytes → deduped:true no-op; different bytes under the same name → abort (refuse-never-repair). Post-upload read-back: re-list + download + sha256 verify; download paths derived from the listing's fullname (supplier invoices carry an id-derived get_exdir prefix — live-verified 9/2/, 2/1/, 4/1/, 5/1/).
  • Promote op attach in promote-plan/promote-apply with @ref/#lookup object resolution; file resolves relative to the manifest; plan prints each file's sha256 at review time.
  • bin/arcodange sandbox attach subcommand + SKILL.md workflow 7 + offline test case 12 + live attach op in tests/replay-idempotency.sh.
  • Replay pack manifest-C-ged-attach.json (portable #supplierinvoice:ref_supplier= lookups); PDFs re-fetched from Zoho (arcodange-email-ingest), sha256s match the golden-set sidecars 4/4.

Acceptance (live on the sandbox, in the #43 evidence comment): run 1 4 created, run 2 4 deduped (no-op), API listing shows exactly one file per invoice (FAF2026010–013), each downloaded back with stored sha256 == source sha256. Conflict abort live-proven.

Remaining human-gated step (out of scope here): prod replay of manifest-C via the promote gate.

🤖 Generated with Claude Code

https://claude.ai/code/session_01VRShc4QhLLU73FLHx9vskh

Closes #43. Fills the gap confirmed during the 2026-07-11 regularization: the write skill had no document-attach op, so rehearsed invoices carried no PDF (PAF: originals hash-addressed in the GED). - `document-attach.sh` — `POST /documents/upload` (base64, `overwriteifexists` always 0), addressed by `object_id` or `ref` (cross-checked; ambiguous ref aborts). **Idempotent by (object, filename, sha256) — own check**, not Dolibarr's overwrite flag: same bytes → `deduped:true` no-op; different bytes under the same name → abort (refuse-never-repair). Post-upload read-back: re-list + download + sha256 verify; download paths derived from the listing's `fullname` (supplier invoices carry an id-derived `get_exdir` prefix — live-verified `9/2/`, `2/1/`, `4/1/`, `5/1/`). - Promote op `attach` in `promote-plan`/`promote-apply` with `@ref`/`#lookup` object resolution; `file` resolves relative to the manifest; plan prints each file's sha256 at review time. - `bin/arcodange sandbox attach` subcommand + SKILL.md workflow 7 + offline test case 12 + live attach op in `tests/replay-idempotency.sh`. - Replay pack `manifest-C-ged-attach.json` (portable `#supplierinvoice:ref_supplier=` lookups); PDFs re-fetched from Zoho (`arcodange-email-ingest`), sha256s match the golden-set sidecars 4/4. **Acceptance (live on the sandbox, in the [#43 evidence comment](https://gitea.arcodange.lab/arcodange-org/erp/issues/43)):** run 1 `4 created`, run 2 `4 deduped` (no-op), API listing shows exactly one file per invoice (FAF2026010–013), each downloaded back with stored sha256 == source sha256. Conflict abort live-proven. Remaining human-gated step (out of scope here): prod replay of manifest-C via the promote gate. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01VRShc4QhLLU73FLHx9vskh
arcodange added 1 commit 2026-07-19 00:13:20 +02:00
document-attach.sh uploads a source piece (the supplier's own PDF) onto an
invoice's GED via POST /documents/upload — idempotent by (object, filename,
sha256): before any POST the object's GED is listed and a same-named entry is
downloaded back and sha256-compared. Identical → deduped no-op; different
content → ABORT (refuse-never-repair, overwriteifexists always 0, never
Dolibarr's overwrite flag). Read-back after upload: re-list + download +
sha256-verify. Module-relative download paths are derived from the listing's
fullname (supplier invoices carry an id-derived get_exdir prefix like
9/2/FAF2026013/…, so reconstruction would be wrong).

Promote integration: new `attach` op in promote-plan/promote-apply (OP_SCRIPT),
object_id resolvable via @ref and #supplierinvoice lookups; a relative `file`
resolves against the manifest's directory (replay packs carry pdfs/ beside the
manifest, gitignored — README documents the books@ re-fetch message ids).
promote-plan prints each file's sha256 (or a loud MISSING) at review time.
CLI: `arcodange sandbox attach`.

Proof: offline case 12 in tests/run-tests.sh (upload body, dedupe, conflict
abort, field refusal, manifest-relative resolution via stubbed /documents);
live: manifest-C-ged-attach.json applied twice on the sandbox — run 1 four
created, run 2 four deduped, one GED file per FAF2026010-013, stored sha256s
equal to the re-fetched sources; tests/replay-idempotency.sh extended with an
attach op (4 created → 4 deduped, ged_files count unchanged) and a live
same-name/different-bytes abort verified.

Closes erp#43

Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01VRShc4QhLLU73FLHx9vskh
arcodange merged commit f027ff3c34 into main 2026-07-19 00:13:29 +02:00
arcodange deleted branch arcodange/ged-attach 2026-07-19 00:13:29 +02:00
Sign in to join this conversation.
No Reviewers
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: arcodange-org/erp#72