Company Rematch
Re-run the matcher over every entry that could still gain a link.
The selection spans two independent state columns on purpose (ZET-179). An entry that matched on the document axis but never reached the settlement plane reads reconciliation_status=matched with its whole amount in Suspense — invisible to a reconciliation-status filter, which is exactly how 131 entries stayed stuck. The settlement arms are the worker claim's own pending_settlement_criteria (ZET-189), so this route and the background sweep can never disagree about which entries are recoverable — the matched + unsettled cell matched neither of this route's previous arms, leaving the operator with no way to reach the exact population the defect strands.
Still link-only — it never posts. Draining the Suspense those links unlock is /reconciliation/resettle, which is privileged accordingly.
Neither this route nor /resettle eager-loads the entry's link graph, and neither may start to: both return counts, and nothing in match_bank_entry / autonomous_settle reads a relationship off the entry — every one of them goes through an explicit aggregate query, which is why the background sweep's own claim (claim_reconciliation_entries) selects bare rows too. The seven selectinload chains that used to sit here (_ENTRY_LOADS, built for the serialising GET routes) pulled each entry's documents and their whole raw analysis JSON for nobody to read (ZCT-546).
limit bounds the batch exactly as /resettle's does (review finding: the widened population made an unbounded scan-and-eager-load possible on a backlogged company — thousands of rows with seven selectinload chains inside one request transaction). Deterministic ORDER BY id; run repeatedly to walk the rest.
ZCT-546 — read this before believing the paragraph above. That review finding was only half the cost, and the half it fixed was never the expensive one: the selection here is bounded (limit=1 issues two statements), but each entry's own matching pass then re-scanned the company's whole backlog — one round-trip per item, one per journal entry and up to three per open item, plus an eager-load of every document's raw analysis JSON — so the per-entry cost scaled with the backlog and limit multiplied it rather than bounding it. Measured on a seeded 230-item / 150-journal / 120-open-item company: 794 statements and 5.9 s at limit=1, and 15,235 statements and 119 s at limit=20 — on localhost, before any network latency. Batched, the same calls cost 53 and 529 statements, ~25 per entry independent of the backlog, so the cost of a call is proportional to limit again.
One caveat kept deliberately: this bounded the cost, it did not measure the reporter's machine. limit=1 at ~6 s locally does not on its own reach any deployed timeout (gunicorn 120 s, nginx and the MCP client 600 s, no statement_timeout), so if a limit=1 call still hangs after this, suspect the blocking with_for_update() in _locked_open_amount rather than the scan volume.
Request
- Base URL: https://app.zeno-works.ch (the document declares no server; this is the origin it was published from)
- URL: https://app.zeno-works.ch/companies/{company_id}/reconciliation/rematch
- Auth: none declared
Path parameters
Query parameters
Headers
Cookies
Response
Successful Response
Changes
No recorded changes to this operation across all 1 revision of this API.