Batch signing: multi-file queue in UI (AutoFirma multi-file/folder mode) #42

Closed
opened 2026-10-15 01:19:22 +02:00 by EduarD3V · 34 comments
Owner

Goal

AutoFirma signs many files in one pass. Add a batch queue to the web app.

Scope (OWNERSHIP: this issue owns the queue store + App-level list UI)

  • Queue: add/remove multiple files (PDF/XML/binary), per-file status (pending/signing/signed/error), download all as ZIP (verify:zip path exists).
  • Progress UI; sequential signing (WebCrypto is fine sequential); cancel.
  • Same cert applies to whole batch.

Ownership boundaries

Do NOT touch SignPanel options (visible-signature issue owns those) or certstore (its own issue). Interface: queue calls existing sign functions per file.

Gates

pnpm test green (queue store + ZIP integration tests), build clean.

## Goal AutoFirma signs many files in one pass. Add a batch queue to the web app. ## Scope (OWNERSHIP: this issue owns the queue store + App-level list UI) - Queue: add/remove multiple files (PDF/XML/binary), per-file status (pending/signing/signed/error), download all as ZIP (verify:zip path exists). - Progress UI; sequential signing (WebCrypto is fine sequential); cancel. - Same cert applies to whole batch. ## Ownership boundaries Do NOT touch SignPanel options (visible-signature issue owns those) or certstore (its own issue). Interface: queue calls existing sign functions per file. ## Gates pnpm test green (queue store + ZIP integration tests), build clean.
Author
Owner

bullet: coder launched on branch issue/42-batch-signing-multi-file-queue (model nan/deepseek-v4-flash).

bullet: coder launched on branch `issue/42-batch-signing-multi-file-queue` (model `nan/deepseek-v4-flash`).
Author
Owner

Claimed — branch issue/42-batch-signing-multi-file-queue. Doing: the queue store, UI, ZIP path and progress already exist on bullet-develop from #9; the one requirement of this issue with no implementation anywhere is cancel (0 grep hits). Implementing cancel in src/lib/batch.ts + BatchQueue.svelte + i18n + tests, and re-verifying the rest of the scope.

Claimed — branch `issue/42-batch-signing-multi-file-queue`. Doing: the queue store, UI, ZIP path and progress already exist on `bullet-develop` from #9; the one requirement of this issue with no implementation anywhere is **cancel** (0 grep hits). Implementing cancel in `src/lib/batch.ts` + `BatchQueue.svelte` + i18n + tests, and re-verifying the rest of the scope.
Author
Owner

Plan — #42 Batch signing: multi-file queue in UI (owner: queue store + App-level list UI)

Restated goal

The issue asks for the AutoFirma "multi-file/folder" flow in the web app: drop N documents
(PDF/XML/binary), per-file status (pending/signing/signed/error), one certificate for the
whole batch, sequential signing, progress UI, cancel, and a "download all" ZIP.

Finding before planning (verified, not assumed): most of that already exists on this branch's
base (bullet-develop @ 4c9f6c31), landed by #9:

Issue requirement State on the base Evidence
Queue store, per-file status present src/lib/batch.ts (createBatchQueue, DocumentStatus, run)
add/remove multiple files present App.svelte:70,371,387 (files list, FileDrop appends, FileCard onremove)
sequential signing, one cert per batch present run() awaits one document at a time; App.svelte makeSigner(cert, format)
progress UI present BatchQueue.svelte:62 (Firmados: n/N)
download all as ZIP, verify:zip path present buildZip + package.json scripts["verify:zip"] + scripts/verify-batch-zip.mjs
cancel absent grep -rn "cancel|abort" src/lib/{batch.ts,i18n.ts,components/BatchQueue.svelte,App.svelte} → 0 hits

So "done" for #42 = cancel works end to end (store + UI + i18n + tests), with the rest of the
scope re-verified by the existing suite rather than re-implemented. Re-implementing a working queue
would be churn against a reviewed design.

Scope

Files I will change (5 + plan/report outside the repo):

  • src/lib/batch.ts — phase union gains 'cancelled'; BatchQueue.cancel(); the run loop stops
    before starting the next document once cancelled and publishes phase: 'cancelled'.
  • src/lib/components/BatchQueue.svelte — a Cancel button, shown only while phase === 'running'.
  • src/lib/i18n.ts — queueCancel + queueCancelled (ES + EN; Record<Lang, Copy> forces both).
  • src/lib/batch.test.ts — store-level cancel test (gated engine) + UI test (button appears while
    running, click cancels, remaining documents stay pending and the ZIP still holds what signed).
  • README.md — one bullet in the existing "Batch signing queue — #9" section.

Files I will not touch (ownership boundaries from the issue + out of scope):

  • src/lib/sign.ts — SignPanel options (visible-signature issue owns them).
  • src/lib/certstore.ts — its own issue.
  • src/lib/cms.ts, xades.ts, pades.ts, zip.ts, verify/** — engines/verification, no change
    needed: the queue calls them through BatchSigner.
  • src/lib/App.svelte — no change needed; it already renders BatchQueue and owns the file list,
    certificate and format. Adding cancel needs nothing from it.
  • component s ui/*, App.test.ts — untouched.

Ordered tasks

Each task: one verifying command, commit when green. Commits carry Refs #42.

  1. Baseline — pnpm test (record pass count) and pnpm check.
  2. Store cancel — edit batch.ts: phase: 'idle' | 'running' | 'done' | 'cancelled',
    cancel(): void on BatchQueue, let cancelled flag cleared at the top of run, checked at the
    top of each loop iteration and before the final phase write.
    Verify: pnpm vitest run src/lib/batch.test.ts -t 'cancel'.
  3. Store test — batch.test.ts: 3 documents, signer gated on a promise; call cancel() while
    a.txt is in flight; assert signed === ['a.txt'], phase === 'cancelled', b.txt/c.txt
    still pending with result: NOT_STARTED; then a further run() resumes at b.txt and ends
    done. Verify: same command.
  4. i18n + UI — add the two copy keys, render the Cancel button next to the run button while
    running, wire queue.cancel().
    Verify: pnpm vitest run src/lib/batch.test.ts (new UI case: button present while running and
    gone after; clicking it leaves the batch cancelled, i.e. the progress line stops at the cancelled
    count and the run button is enabled again).
  5. README — one bullet under ## Batch signing queue — #9.
    Verify: pnpm test && pnpm check.
  6. Gates — pnpm test, pnpm check, pnpm build, pnpm verify:zip, git diff --check.

Open questions (assumptions I am proceeding on)

  1. Does cancel discard the signature already in flight? Assumption: no. WebCrypto cannot
    abort a digest/sign mid-call, and throwing away a finished signature loses work; so the document
    being signed when cancel lands finishes and stays signed, and cancel prevents the next one
    from starting. Documented in the README bullet.
  2. New phase value vs a boolean flag? Assumption: widen the union with 'cancelled'. It is
    what the UI needs to distinguish "stopped by the user" from "never started" (idle) and
    "finished" (done), and run() already skips signed documents, so a re-run after a cancel
    resumes. Only three existing assertions touch phase (batch.test.ts:135,146,256) and none
    breaks.
  3. Is a cancel needed during a single document's WebCrypto call? Assumption: no — the issue says
    "sequential signing (WebCrypto is fine sequential)"; per-document granularity is the contract.

Risk

  • Widening DocumentStatus/phase breaks a consumer. Checked: phase is read in
    BatchQueue.svelte:62,81,85 and the three test assertions above; DocumentStatus is unchanged.
    Fallback if something unexpected breaks: keep phase as-is and add a cancelled: boolean on the
    state instead (same UI effect, no union change).
  • UI test hangs if the click races the run loop (jsdom is single-threaded, the gated engine needs
    a microtask to release). Fallback: keep the deterministic store-level cancel test as the real
    proof and reduce the UI test to "the Cancel button exists while running and triggers
    queue.cancel()", spying on the store rather than racing it.
  • pnpm test runtime — the suite already shells out to openssl and builds real CAdES fixtures
    (~1 min). No new external tool, so no new flake source; verify:zip needs unzip/sha256sum,
    both present (used by #9's gate).
  • Nothing to implement after all (i.e. cancel turns out to exist elsewhere): then the honest
    outcome is a report saying #42 is already satisfied by #9 and a no-op diff — but the greps above
    say cancel is genuinely missing, so I proceed with tasks 2–5.

Refs #42

# Plan — #42 Batch signing: multi-file queue in UI (owner: queue store + App-level list UI) ## Restated goal The issue asks for the AutoFirma "multi-file/folder" flow in the web app: drop N documents (PDF/XML/binary), per-file status (`pending`/`signing`/`signed`/`error`), one certificate for the whole batch, sequential signing, progress UI, **cancel**, and a "download all" ZIP. **Finding before planning (verified, not assumed):** most of that already exists on this branch's base (`bullet-develop` @ `4c9f6c31`), landed by #9: | Issue requirement | State on the base | Evidence | |---|---|---| | Queue store, per-file status | present | `src/lib/batch.ts` (`createBatchQueue`, `DocumentStatus`, `run`) | | add/remove multiple files | present | `App.svelte:70,371,387` (`files` list, `FileDrop` appends, `FileCard onremove`) | | sequential signing, one cert per batch | present | `run()` awaits one document at a time; `App.svelte` `makeSigner(cert, format)` | | progress UI | present | `BatchQueue.svelte:62` (`Firmados: n/N`) | | download all as ZIP, `verify:zip` path | present | `buildZip` + `package.json scripts["verify:zip"]` + `scripts/verify-batch-zip.mjs` | | **cancel** | **absent** | `grep -rn "cancel\|abort" src/lib/{batch.ts,i18n.ts,components/BatchQueue.svelte,App.svelte}` → 0 hits | So "done" for #42 = **cancel works end to end** (store + UI + i18n + tests), with the rest of the scope re-verified by the existing suite rather than re-implemented. Re-implementing a working queue would be churn against a reviewed design. ## Scope Files I will change (5 + plan/report outside the repo): - `src/lib/batch.ts` — `phase` union gains `'cancelled'`; `BatchQueue.cancel()`; the run loop stops before starting the next document once cancelled and publishes `phase: 'cancelled'`. - `src/lib/components/BatchQueue.svelte` — a Cancel button, shown only while `phase === 'running'`. - `src/lib/i18n.ts` — `queueCancel` + `queueCancelled` (ES + EN; `Record<Lang, Copy>` forces both). - `src/lib/batch.test.ts` — store-level cancel test (gated engine) + UI test (button appears while running, click cancels, remaining documents stay `pending` and the ZIP still holds what signed). - `README.md` — one bullet in the existing "Batch signing queue — #9" section. Files I will **not** touch (ownership boundaries from the issue + out of scope): - `src/lib/sign.ts` — SignPanel options (visible-signature issue owns them). - `src/lib/certstore.ts` — its own issue. - `src/lib/cms.ts`, `xades.ts`, `pades.ts`, `zip.ts`, `verify/**` — engines/verification, no change needed: the queue calls them through `BatchSigner`. - `src/lib/App.svelte` — no change needed; it already renders `BatchQueue` and owns the file list, certificate and format. Adding cancel needs nothing from it. - `component s ui/*`, `App.test.ts` — untouched. ## Ordered tasks Each task: one verifying command, commit when green. Commits carry `Refs #42`. 1. **Baseline** — `pnpm test` (record pass count) and `pnpm check`. 2. **Store cancel** — edit `batch.ts`: `phase: 'idle' | 'running' | 'done' | 'cancelled'`, `cancel(): void` on `BatchQueue`, `let cancelled` flag cleared at the top of `run`, checked at the top of each loop iteration and before the final phase write. Verify: `pnpm vitest run src/lib/batch.test.ts -t 'cancel'`. 3. **Store test** — `batch.test.ts`: 3 documents, signer gated on a promise; call `cancel()` while `a.txt` is in flight; assert `signed === ['a.txt']`, `phase === 'cancelled'`, `b.txt`/`c.txt` still `pending` with `result: NOT_STARTED`; then a further `run()` resumes at `b.txt` and ends `done`. Verify: same command. 4. **i18n + UI** — add the two copy keys, render the Cancel button next to the run button while running, wire `queue.cancel()`. Verify: `pnpm vitest run src/lib/batch.test.ts` (new UI case: button present while running and gone after; clicking it leaves the batch cancelled, i.e. the progress line stops at the cancelled count and the run button is enabled again). 5. **README** — one bullet under `## Batch signing queue — #9`. Verify: `pnpm test && pnpm check`. 6. **Gates** — `pnpm test`, `pnpm check`, `pnpm build`, `pnpm verify:zip`, `git diff --check`. ## Open questions (assumptions I am proceeding on) 1. **Does cancel discard the signature already in flight?** Assumption: **no**. WebCrypto cannot abort a digest/sign mid-call, and throwing away a finished signature loses work; so the document being signed when cancel lands finishes and stays `signed`, and cancel prevents the *next* one from starting. Documented in the README bullet. 2. **New `phase` value vs a boolean flag?** Assumption: widen the union with `'cancelled'`. It is what the UI needs to distinguish "stopped by the user" from "never started" (`idle`) and "finished" (`done`), and `run()` already skips `signed` documents, so a re-run after a cancel resumes. Only three existing assertions touch `phase` (`batch.test.ts:135,146,256`) and none breaks. 3. **Is a cancel needed during a single document's WebCrypto call?** Assumption: no — the issue says "sequential signing (WebCrypto is fine sequential)"; per-document granularity is the contract. ## Risk - **Widening `DocumentStatus`/`phase` breaks a consumer.** Checked: `phase` is read in `BatchQueue.svelte:62,81,85` and the three test assertions above; `DocumentStatus` is unchanged. Fallback if something unexpected breaks: keep `phase` as-is and add a `cancelled: boolean` on the state instead (same UI effect, no union change). - **UI test hangs** if the click races the run loop (jsdom is single-threaded, the gated engine needs a microtask to release). Fallback: keep the deterministic store-level cancel test as the real proof and reduce the UI test to "the Cancel button exists while running and triggers `queue.cancel()`", spying on the store rather than racing it. - **`pnpm test` runtime** — the suite already shells out to `openssl` and builds real CAdES fixtures (~1 min). No new external tool, so no new flake source; `verify:zip` needs `unzip`/`sha256sum`, both present (used by #9's gate). - **Nothing to implement after all** (i.e. cancel turns out to exist elsewhere): then the honest outcome is a report saying #42 is already satisfied by #9 and a no-op diff — but the greps above say cancel is genuinely missing, so I proceed with tasks 2–5. Refs #42
Author
Owner

Issue #42 — Batch signing: multi-file queue in UI — coder report

Outcome: DONE. Branch issue/42-batch-signing-multi-file-queue, 4 commits on top of
4c9f6c31 (bullet-develop HEAD), tree clean, nothing pushed.

Headline finding

Almost the whole scope of #42 already existed on the base: #9 (merged before this branch point)
shipped src/lib/batch.ts (queue state machine, per-document status, sequential run, manifest),
BatchQueue.svelte (progress UI + ZIP download), the App.svelte multi-file list with per-file
remove, and scripts/verify-batch-zip.mjs behind pnpm verify:zip. Re-implementing that would
have been churn against a reviewed design.

The one requirement in the issue body with no implementation anywhere was cancel
(grep -rn "cancel\|abort" src/lib/{batch.ts,i18n.ts,components/BatchQueue.svelte,App.svelte} →
0 hits before this change). That is what this branch adds. Every other bullet of the issue is
re-verified by the existing suite, listed under "scope audit" below.

Commits

Commit What
54bed2d9 web: stop a batch run at the next document when cancelled — phase: 'cancelled', BatchQueue.cancel(), loop guard, store test
7ed3271b web: cancel the batch from the queue UI — primary button becomes Cancel while running, phase label, i18n ES/EN, UI test
b2e39737 test: read the xmlsec1 1.3.12 'Verification status:' verdict — see "out-of-scope note"
833ba9eb docs: the queue is cancellable — one README bullet

Gates (all exit 0, run on 833ba9eb)

pnpm test        → 0   16 files, 142/142 tests passed (incl. src/lib/batch.test.ts: 7/7)
pnpm check       → 0   svelte-check: 1324 files, 0 errors, 0 warnings
pnpm build       → 0   adapter-static wrote site to build/
pnpm verify:zip  → 0   Info-ZIP `unzip -t` OK + sha256sum matches the manifest digest
git diff --check → 0   clean

pnpm test was red on the untouched base (5–8 failures in xades.test.ts + one XAdES case in
App.test.ts): this box's xmlsec1 1.3.12 (openssl) prints Verification status: OK /
Verification status: FAILED, a third verdict shape the helper did not know, so
src/test/fixtures.ts:164 threw xmlsec1 printed no verdict (exit 0) before any assertion ran.
One regex line now accepts it; no assertion changed. Proven: the full suite is green after it, and
the tampered-document case now genuinely asserts FAIL instead of erroring.

Behavioural evidence for the new feature (not just unit assertions):

  • store level — 3 documents, engine gated on a promise, cancel() called while a.txt is in
    flight: signed === ['a.txt'], phase === 'cancelled', the two behind it stay pending /
    NOT_STARTED, a.txt's output is verified by openssl cms -verify, and the next run()
    resumes at b.txt and ends done.
  • UI level — BatchQueue.svelte mounted in jsdom with the real CAdES engine: the run button
    becomes Cancelar while running, clicking it leaves Lote cancelado: 1/3, the button reverts to
    Firmar, and the queue state is ['signed','pending','pending'].
  • pnpm verify:zip cross-checks the ZIP path this issue names, unchanged and green.
  • Limit of the evidence: no real-browser smoke this session (jsdom + the production build instead);
    the change is one button and one store flag, and both paths are exercised above.

Scope audit — every issue bullet, against the tree

Issue requirement Where it lives Verified by
add/remove multiple files (PDF/XML/binary) App.svelte files list, FileDrop (appends), FileCard onremove App.test.ts sign-workflow suite
per-file status pending/signing/signed/error batch.ts DocumentStatus, patch() batch.test.ts (all 7)
download all as ZIP, verify:zip path buildZip + scripts/verify-batch-zip.mjs pnpm verify:zip, ZIP tests
progress UI BatchQueue.svelte progressLabel + n/N, aria-live UI tests
sequential signing one await per document in run() test asserts running === 1
same cert for the whole batch App.svelte makeSigner(cert, format) App.test.ts
cancel added here store + UI tests above

Files changed

src/lib/batch.ts (+25/-1), src/lib/components/BatchQueue.svelte (+30/-15),
src/lib/i18n.ts (+6), src/lib/batch.test.ts (+100), README.md (+3),
src/test/fixtures.ts (+4/-4, out-of-scope note below).

Not touched, per the issue's ownership boundaries: src/lib/sign.ts (SignPanel options),
src/lib/certstore.ts, the engines (cms.ts, xades.ts, pades.ts), zip.ts, verify/**, and
App.svelte (it already renders BatchQueue and needed nothing for cancel).

What a human must decide

  1. #42 vs #9 overlap. #9 already delivered the queue, the ZIP and the status UI; this branch adds
    only cancel. Either merge it as the small delta it is, or close #42 as "already satisfied by #9
    plus 54bed2d9/7ed3271b".
  2. phase: 'cancelled' is a new store state — any future consumer of BatchState.phase must
    handle it (BatchQueue.svelte and the tests do). DocumentStatus is unchanged, so the manifest
    and ZIP formats did not move.
  3. Cancel keeps the signature already in flight. WebCrypto cannot be aborted mid-call, and
    discarding a finished signature loses work; the queue stops before starting the next document.
    If the desired semantics are "cancel discards the in-flight document too", that is a two-line
    change plus a new message in the manifest (ERROR_PRE vs NOT_STARTED for an aborted document).
  4. Known pre-existing limitation, deliberately not fixed here: in App.svelte the queue is
    rebuilt from the file list on any change ($effect(() => queue.set(files, format))), so
    dropping an extra file — or removing one — after a run resets every result and the ZIP contents
    to the new list, even though the signatures were kept in the browser store by keepSignatures.
    Fixing it needs a queue add()/remove() API with stable per-document ids (today ids are
    positions) and touches the #9 integration test "abandons a run whose document list was
    replaced". Out of this issue's budget; worth its own issue.
  5. src/test/fixtures.ts is outside the stated scope — say the word if you would rather the
    suite stay red on this box (xmlsec1 version) and have that fix land in the issue that owns the
    XML oracle (#7/#39).

Refs #42

# Issue #42 — Batch signing: multi-file queue in UI — coder report **Outcome: DONE.** Branch `issue/42-batch-signing-multi-file-queue`, 4 commits on top of `4c9f6c31` (bullet-develop HEAD), tree clean, nothing pushed. ## Headline finding Almost the whole scope of #42 already existed on the base: #9 (merged before this branch point) shipped `src/lib/batch.ts` (queue state machine, per-document status, sequential run, manifest), `BatchQueue.svelte` (progress UI + ZIP download), the `App.svelte` multi-file list with per-file remove, and `scripts/verify-batch-zip.mjs` behind `pnpm verify:zip`. Re-implementing that would have been churn against a reviewed design. The one requirement in the issue body with **no implementation anywhere** was **cancel** (`grep -rn "cancel\|abort" src/lib/{batch.ts,i18n.ts,components/BatchQueue.svelte,App.svelte}` → 0 hits before this change). That is what this branch adds. Every other bullet of the issue is re-verified by the existing suite, listed under "scope audit" below. ## Commits | Commit | What | |---|---| | `54bed2d9` | `web: stop a batch run at the next document when cancelled` — `phase: 'cancelled'`, `BatchQueue.cancel()`, loop guard, store test | | `7ed3271b` | `web: cancel the batch from the queue UI` — primary button becomes *Cancel* while running, phase label, i18n ES/EN, UI test | | `b2e39737` | `test: read the xmlsec1 1.3.12 'Verification status:' verdict` — see "out-of-scope note" | | `833ba9eb` | `docs: the queue is cancellable` — one README bullet | ## Gates (all exit 0, run on `833ba9eb`) ``` pnpm test → 0 16 files, 142/142 tests passed (incl. src/lib/batch.test.ts: 7/7) pnpm check → 0 svelte-check: 1324 files, 0 errors, 0 warnings pnpm build → 0 adapter-static wrote site to build/ pnpm verify:zip → 0 Info-ZIP `unzip -t` OK + sha256sum matches the manifest digest git diff --check → 0 clean ``` `pnpm test` was **red on the untouched base** (5–8 failures in `xades.test.ts` + one XAdES case in `App.test.ts`): this box's `xmlsec1 1.3.12 (openssl)` prints `Verification status: OK` / `Verification status: FAILED`, a third verdict shape the helper did not know, so `src/test/fixtures.ts:164` threw `xmlsec1 printed no verdict (exit 0)` before any assertion ran. One regex line now accepts it; no assertion changed. Proven: the full suite is green after it, and the tampered-document case now genuinely asserts `FAIL` instead of erroring. Behavioural evidence for the new feature (not just unit assertions): - store level — 3 documents, engine gated on a promise, `cancel()` called while `a.txt` is in flight: `signed === ['a.txt']`, `phase === 'cancelled'`, the two behind it stay `pending` / `NOT_STARTED`, `a.txt`'s output is verified by `openssl cms -verify`, and the next `run()` resumes at `b.txt` and ends `done`. - UI level — `BatchQueue.svelte` mounted in jsdom with the **real** CAdES engine: the run button becomes `Cancelar` while running, clicking it leaves `Lote cancelado: 1/3`, the button reverts to `Firmar`, and the queue state is `['signed','pending','pending']`. - `pnpm verify:zip` cross-checks the ZIP path this issue names, unchanged and green. - Limit of the evidence: no real-browser smoke this session (jsdom + the production build instead); the change is one button and one store flag, and both paths are exercised above. ## Scope audit — every issue bullet, against the tree | Issue requirement | Where it lives | Verified by | |---|---|---| | add/remove multiple files (PDF/XML/binary) | `App.svelte` `files` list, `FileDrop` (appends), `FileCard onremove` | `App.test.ts` sign-workflow suite | | per-file status pending/signing/signed/error | `batch.ts` `DocumentStatus`, `patch()` | `batch.test.ts` (all 7) | | download all as ZIP, `verify:zip` path | `buildZip` + `scripts/verify-batch-zip.mjs` | `pnpm verify:zip`, ZIP tests | | progress UI | `BatchQueue.svelte` `progressLabel` + `n/N`, `aria-live` | UI tests | | sequential signing | one `await` per document in `run()` | test asserts `running === 1` | | same cert for the whole batch | `App.svelte makeSigner(cert, format)` | `App.test.ts` | | **cancel** | **added here** | store + UI tests above | ## Files changed `src/lib/batch.ts` (+25/-1), `src/lib/components/BatchQueue.svelte` (+30/-15), `src/lib/i18n.ts` (+6), `src/lib/batch.test.ts` (+100), `README.md` (+3), `src/test/fixtures.ts` (+4/-4, out-of-scope note below). Not touched, per the issue's ownership boundaries: `src/lib/sign.ts` (SignPanel options), `src/lib/certstore.ts`, the engines (`cms.ts`, `xades.ts`, `pades.ts`), `zip.ts`, `verify/**`, and `App.svelte` (it already renders `BatchQueue` and needed nothing for cancel). ## What a human must decide 1. **#42 vs #9 overlap.** #9 already delivered the queue, the ZIP and the status UI; this branch adds only cancel. Either merge it as the small delta it is, or close #42 as "already satisfied by #9 plus `54bed2d9`/`7ed3271b`". 2. **`phase: 'cancelled'` is a new store state** — any future consumer of `BatchState.phase` must handle it (`BatchQueue.svelte` and the tests do). `DocumentStatus` is unchanged, so the manifest and ZIP formats did not move. 3. **Cancel keeps the signature already in flight.** WebCrypto cannot be aborted mid-call, and discarding a finished signature loses work; the queue stops before starting the next document. If the desired semantics are "cancel discards the in-flight document too", that is a two-line change plus a new message in the manifest (`ERROR_PRE` vs `NOT_STARTED` for an aborted document). 4. **Known pre-existing limitation, deliberately not fixed here:** in `App.svelte` the queue is rebuilt from the file list on *any* change (`$effect(() => queue.set(files, format))`), so dropping an extra file — or removing one — after a run resets every result and the ZIP contents to the new list, even though the signatures were kept in the browser store by `keepSignatures`. Fixing it needs a queue `add()`/`remove()` API with stable per-document ids (today ids are positions) and touches the #9 integration test "abandons a run whose document list was replaced". Out of this issue's budget; worth its own issue. 5. **`src/test/fixtures.ts` is outside the stated scope** — say the word if you would rather the suite stay red on this box (xmlsec1 version) and have that fix land in the issue that owns the XML oracle (#7/#39). Refs #42
Author
Owner

bullet: coder session died — relaunched from committed state (branch issue/42-batch-signing-multi-file-queue).

bullet: coder session died — relaunched from committed state (branch `issue/42-batch-signing-multi-file-queue`).
Author
Owner

DONE — branch issue/42-batch-signing-multi-file-queue, 5 commits on 4c9f6c31, tree clean, nothing pushed.

Commits: 54bed2d9 (store: stop at the next document when cancelled) · 7ed3271b (UI: cancel button + phase label + i18n) · b2e39737 (xmlsec1 1.3.12 verdict) · 833ba9eb (README) · 5b5e294b (test headroom under load).

What the issue still lacked was cancel: the queue store, per-file status, sequential run, progress UI, ZIP + verify:zip and the App-level multi-file list all shipped with #9 and are re-verified, not re-implemented.

Gates on 5b5e294b, all exit 0:

  • pnpm test → 142/142 (16 files; src/lib/batch.test.ts 7/7)
  • pnpm check → 1324 files, 0 errors, 0 warnings
  • pnpm build → adapter-static wrote build/
  • pnpm verify:zip → unzip -t OK + sha256sum matches the manifest
  • git diff --check → clean

Real-browser proof (production build on :4173, generated PKCS#12, 5 documents, CPU-throttled so the run lasts): queue's Firmar becomes Cancelar while running; clicking it gives Lote cancelado: 1/5 with the first document Firmada and the other four Pendiente; re-running resumes to Firmados: 5/5; the downloaded ZIP holds the five .csig + manifest.json, and all five verify with openssl cms -verify -binary.

Full report: .bullet/meta/issue-42.report.md. Open questions for the human are on it (the phase: 'cancelled' state, cancel keeping the in-flight signature, the pre-existing "list change resets results" limitation, and whether the 5b5e294b timeout headroom is wanted).

Refs #42

**DONE** — branch `issue/42-batch-signing-multi-file-queue`, 5 commits on `4c9f6c31`, tree clean, nothing pushed. Commits: `54bed2d9` (store: stop at the next document when cancelled) · `7ed3271b` (UI: cancel button + phase label + i18n) · `b2e39737` (xmlsec1 1.3.12 verdict) · `833ba9eb` (README) · `5b5e294b` (test headroom under load). What the issue still lacked was **cancel**: the queue store, per-file status, sequential run, progress UI, ZIP + `verify:zip` and the App-level multi-file list all shipped with #9 and are re-verified, not re-implemented. Gates on `5b5e294b`, all exit 0: - `pnpm test` → 142/142 (16 files; `src/lib/batch.test.ts` 7/7) - `pnpm check` → 1324 files, 0 errors, 0 warnings - `pnpm build` → adapter-static wrote `build/` - `pnpm verify:zip` → `unzip -t` OK + sha256sum matches the manifest - `git diff --check` → clean Real-browser proof (production build on `:4173`, generated PKCS#12, 5 documents, CPU-throttled so the run lasts): queue's `Firmar` becomes `Cancelar` while running; clicking it gives `Lote cancelado: 1/5` with the first document `Firmada` and the other four `Pendiente`; re-running resumes to `Firmados: 5/5`; the downloaded ZIP holds the five `.csig` + `manifest.json`, and all five verify with `openssl cms -verify -binary`. Full report: `.bullet/meta/issue-42.report.md`. Open questions for the human are on it (the `phase: 'cancelled'` state, cancel keeping the in-flight signature, the pre-existing "list change resets results" limitation, and whether the `5b5e294b` timeout headroom is wanted). Refs #42
Author
Owner

bullet: coder session died — relaunched from committed state (branch issue/42-batch-signing-multi-file-queue).

bullet: coder session died — relaunched from committed state (branch `issue/42-batch-signing-multi-file-queue`).
Author
Owner

bullet: implementation finished (DONE: — branch issue/42-batch-signing-multi-file-queue, 5 commits on 4c9f6c31, tree clean, nothing pushed.) — moving to review.

bullet: implementation finished (DONE: — branch `issue/42-batch-signing-multi-file-queue`, 5 commits on `4c9f6c31`, tree clean, nothing pushed.) — moving to review.
Author
Owner

bullet: reviewer launched (model 9router/reviewer, independent of coder).

bullet: reviewer launched (model `9router/reviewer`, independent of coder).
Author
Owner

REVIEW: FIXED 1 — commit f7eed73d: findings: plan-fidelity gap — plan promised UI cancel test asserts the ZIP after cancel holds only what signed, original test at 5b5e294b stopped at statuses; fixed in f7eed73d; UNFIXED: none

REVIEW: FIXED 1 — commit f7eed73d: findings: plan-fidelity gap — plan promised UI cancel test asserts the ZIP after cancel holds only what signed, original test at 5b5e294b stopped at statuses; fixed in f7eed73d; UNFIXED: none
Author
Owner

bullet: fixer launched (round 1/2).

bullet: fixer launched (round 1/2).
Author
Owner

FIXED: applied 1 — f7eed73d: UI cancel test now asserts the ZIP after a cancel holds exactly a.txt.csig + manifest.json (plan line 34 fidelity); UNFIXED: none

Re-verified at head f7eed73d (fixer round 1): pnpm vitest run src/lib/batch.test.ts 7/7; pnpm test 16 files 142/142; pnpm check 1324 files 0 errors 0 warnings; pnpm build clean; pnpm verify:zip 3 entries unzip+sha256sum OK; git diff --check clean. Mutation check: disabling the cancel guard (if (cancelled) break;) makes the batch tests fail 2 — the new assertion genuinely pins the behaviour, not a tautology. No code change needed; no new commits.

Refs #42

FIXED: applied 1 — f7eed73d: UI cancel test now asserts the ZIP after a cancel holds exactly a.txt.csig + manifest.json (plan line 34 fidelity); UNFIXED: none Re-verified at head f7eed73d (fixer round 1): pnpm vitest run src/lib/batch.test.ts 7/7; pnpm test 16 files 142/142; pnpm check 1324 files 0 errors 0 warnings; pnpm build clean; pnpm verify:zip 3 entries unzip+sha256sum OK; git diff --check clean. Mutation check: disabling the cancel guard (if (cancelled) break;) makes the batch tests fail 2 — the new assertion genuinely pins the behaviour, not a tautology. No code change needed; no new commits. Refs #42
Author
Owner

bullet: fixer round done — fresh reviewer re-checking.

bullet: fixer round done — fresh reviewer re-checking.
Author
Owner

REVIEW: CLEAN — commit f7eed73d: findings none; UNFIXED: none

REVIEW: CLEAN — commit f7eed73d: findings none; UNFIXED: none
Author
Owner

bullet: review CLEAN — ready to publish.

bullet: review CLEAN — ready to publish.
Author
Owner

bullet: PR #56 open → bullet-develop (auto-merge follows).

bullet: PR #56 open → `bullet-develop` (auto-merge follows).
Author
Owner

bullet: merge blocked (HTTP 405) — conciliation agent merges trunk work into bullet-develop; PR auto-merges next tick.

bullet: merge blocked (HTTP 405) — conciliation agent merges trunk work into `bullet-develop`; PR auto-merges next tick.
Author
Owner

bullet: merge conflicts with bullet-develop — conflict-solver agent owns the worktree until it pushes.

bullet: merge conflicts with `bullet-develop` — conflict-solver agent owns the worktree until it pushes.
Author
Owner

bullet: merge blocked (HTTP 405) — conciliation agent merges trunk work into bullet-develop; PR auto-merges next tick.

bullet: merge blocked (HTTP 405) — conciliation agent merges trunk work into `bullet-develop`; PR auto-merges next tick.
Author
Owner

bullet: merge conflicts with bullet-develop — conflict-solver agent owns the worktree until it pushes.

bullet: merge conflicts with `bullet-develop` — conflict-solver agent owns the worktree until it pushes.
Author
Owner

bullet: merge blocked (HTTP 405) — conciliation agent merges trunk work into bullet-develop; PR auto-merges next tick.

bullet: merge blocked (HTTP 405) — conciliation agent merges trunk work into `bullet-develop`; PR auto-merges next tick.
Author
Owner

bullet: merge conflicts with bullet-develop — conflict-solver agent owns the worktree until it pushes.

bullet: merge conflicts with `bullet-develop` — conflict-solver agent owns the worktree until it pushes.
Author
Owner

bullet: merge blocked (HTTP 405) — conciliation agent merges trunk work into bullet-develop; PR auto-merges next tick.

bullet: merge blocked (HTTP 405) — conciliation agent merges trunk work into `bullet-develop`; PR auto-merges next tick.
Author
Owner

bullet: merge conflicts with bullet-develop — conflict-solver agent owns the worktree until it pushes.

bullet: merge conflicts with `bullet-develop` — conflict-solver agent owns the worktree until it pushes.
Author
Owner

bullet: merge blocked (HTTP 405) — conciliation agent merges trunk work into bullet-develop; PR auto-merges next tick.

bullet: merge blocked (HTTP 405) — conciliation agent merges trunk work into `bullet-develop`; PR auto-merges next tick.
Author
Owner

bullet: merge conflicts with bullet-develop — conflict-solver agent owns the worktree until it pushes.

bullet: merge conflicts with `bullet-develop` — conflict-solver agent owns the worktree until it pushes.
Author
Owner

bullet: merge blocked (HTTP 405) — conciliation agent merges trunk work into bullet-develop; PR auto-merges next tick.

bullet: merge blocked (HTTP 405) — conciliation agent merges trunk work into `bullet-develop`; PR auto-merges next tick.
Author
Owner

bullet: merge conflicts with bullet-develop — conflict-solver agent owns the worktree until it pushes.

bullet: merge conflicts with `bullet-develop` — conflict-solver agent owns the worktree until it pushes.
Author
Owner

bullet: merge blocked (HTTP 405) — conciliation agent merges trunk work into bullet-develop; PR auto-merges next tick.

bullet: merge blocked (HTTP 405) — conciliation agent merges trunk work into `bullet-develop`; PR auto-merges next tick.
Author
Owner

bullet: merge conflicts with bullet-develop — conflict-solver agent owns the worktree until it pushes.

bullet: merge conflicts with `bullet-develop` — conflict-solver agent owns the worktree until it pushes.
Author
Owner

bullet: merge blocked (HTTP 405) — conciliation agent merges trunk work into bullet-develop; PR auto-merges next tick.

bullet: merge blocked (HTTP 405) — conciliation agent merges trunk work into `bullet-develop`; PR auto-merges next tick.
Author
Owner

Claimed — branch bullet-develop. Doing: reconcile trunk (origin/main) into bullet-develop for the rolling PR.

Claimed — branch `bullet-develop`. Doing: reconcile trunk (origin/main) into bullet-develop for the rolling PR.
Author
Owner

bullet: merged into bullet-develop — integration gates green.

bullet: merged into `bullet-develop` — integration gates green.
Author
Owner

bullet: PR #56 closed — work already merged into bullet-develop.

bullet: PR #56 closed — work already merged into `bullet-develop`.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
EduarD3V/AutoFirme#42
No description provided.