vike
ConceptsReconciliation

Reconciliation policies

The four reconcile policies, what each one actually folds without an operator, and the rollout rule for a newly wired venue.

A policy answers one question per divergence: fold it now, or hold it for an operator? crates/vike-exec/src/recon/types.rs gives that answer three shapes. ReconMode::Synthesize emits the synthesized events and folds immediately; ReconMode::Quarantine emits nothing and surfaces a ReconAlert for operator confirm; ReconMode::Hybrid is per kind — local-origin kinds synthesize, no-local-origin kinds quarantine. A ReconPolicy is a default mode plus per-kind overrides.

VIKE_RECONCILE_POLICY selects one of four, case-insensitively, one spelling each and no aliases (crates/vike-ops/src/reconcile_config.rs's parse_policy):

ValueWhat it builds
hybrid (default)ReconPolicy::hybrid() — the per-kind preset
synthesizeReconPolicy::default() — mode Synthesize, no overrides: fold everything
quarantinemode Quarantine, no overrides: hold everything
external-quarantineReconPolicy::external_quarantine() — hybrid, with foreign venue activity held

A misspelling gives you MORE auto-folding, not less

parse_policy falls back to hybrid for an unrecognized value as well as an absent one. For a mis-spelled hold-first policy — external_quarantine, external — the process therefore folds more than the operator intended, silently. The startup line below is the check that a spelling took.

Under hybrid, exactly two kinds fold anything

This is the fact everyone gets wrong first, and the code is arranged so nobody has to take prose's word for it. vike_exec::recon::mode_applies is pub because it was private once: its own doc records that while it was, "what does hybrid actually fold?" could only be answered by reading resolve, and two independent reviews of the same code reached opposite answers about one kind — one of which had already propagated into six documents and a rollout policy.

vike_ops::reconcile_config::auto_applied_kinds therefore computes the answer, filtering a five-kind candidate list through mode_applies. Under hybrid the result is MissingFill and PositionDrift, and nothing else. That PositionDrift — a no-local-origin kind — folds at all is argued on the variant itself: unlike every other no-local-origin kind its evidence is a positive venue position row rather than an absence, and it is the only path that re-converges quantity after a fill older than the reconcile lookback.

Auto-apply is not the same as doing something

Four kinds never appear in the auto-applied list even when their mode is auto-apply, because naming a kind that folds nothing misinforms in exactly the direction mode_applies exists to stop:

  • MissingTerminal auto-applies under hybrid and resolves to an empty event list — resolve's events_for has no arm for it, so it lands in the catch-all.
  • OrphanLocalOrder and OrphanLocalPosition fold nothing under any policy. A synthesized cancel would terminalize an order that may still be resting at the venue; a synthesized flattening leg would have to invent a price and book fabricated realized PnL. Held policies surface a dedup-keyed alert instead, and confirming one is a pure acknowledgement.
  • JournalDivergence short-circuits into an investigative alert before the policy is consulted, so its mode is decorative: no auto-apply of a persistence bug.

Every kind is named, and one hand-built policy disagrees

ReconPolicy::hybrid's doc carries a rule with a scar behind it: every kind gets a named row; none may reach its mode through default. A fall-through is indistinguishable from a kind nobody classified — which is what happened to PositionDrift, auto-applying on ten venues through default with no row and no assertion, while the only test of the preset named six kinds and omitted it. crates/vike-exec/tests/recon/recon_policy_pin.rs now gates the rule and pins every kind's mode with an exhaustive match.

A bare Hybrid is not the hybrid preset

resolve's is_local_origin lists only MissingFill and MissingTerminal, so a hand-built ReconPolicy { default: Hybrid, .. } quarantines what the preset folds for PositionDrift. Nothing in the workspace builds one, so it is a latent trap rather than a live divergence — pinned as such by bare_hybrid_and_the_preset_disagree_about_position_drift. VIKE_RECONCILE_POLICY cannot reach it.

external-quarantine

ReconPolicy::external_quarantine is hybrid with every kind whose DivergenceOrigin is External — foreign venue activity nothing local explains — forced to Quarantine. It is computed from DivergenceKind::origin over hybrid()'s rows rather than enumerated, so a future External kind is held by construction. At the kind level exactly one thing changes: PositionDrift stops folding. The other External kinds were already quarantined, and every non-External kind keeps its hybrid row verbatim.

One sub-case changes at the instance level. This is the only constructor that sets hold_external_instances, which makes mode_applies_divergence additionally hold a coid-less MissingFill — a foreign order's fill inside the lookback, whose instance origin reads External while its kind's does not — while coid-linked ones, our own orders' fills, keep folding. The MissingFill mode row is deliberately unchanged: the refinement is per-divergence, not a mode edit, which keeps the journaled per-kind map stable, and the startup line renders it as MissingFill (coid-linked only). For every policy that does not opt in, that function is definitionally mode_applies(policy, d.kind()), so the other three are byte-identical by construction rather than by test.

Claiming a held divergence needs no new machinery: resolve computes a divergence's events before consulting the mode, so the held alert proposes exactly what hybrid would have folded.

The startup line

log_policy_effect emits one line from build_recon_config, the single place every composition root resolves the policy. An empty fold set logs at INFO — nothing auto-folds; a non-empty one logs at WARN naming the kinds. It exists because "is my configuration safe?" used to be answerable only by reading resolve, and the one venue warning that tried printed identical text for a quarantined and an auto-applying policy, while also naming the wrong kind.

Rolling out a newly wired venue

Start a newly wired venue under quarantine until that venue's position-report completeness is proven live, then graduate to hybrid. The reason is PositionDrift: it is the one no-local-origin kind hybrid folds with no operator in front of it, and the fold is destructive — it rewrites local position size onto the venue's number and books realized PnL at the venue's average price.

What an unproven fetch costs you is a row that arrives and is wrong, not one that never arrives. crates/vike-exec/src/recon/diff.rs's diff raises PositionDrift only from a venue row that is present — an explicitly flat qty == 0 row included. A row the venue simply omits raises OrphanLocalPosition instead, which folds nothing under any policy, precisely because most wired position fetches are symbol-scoped and "no row" routinely means "not asked about" rather than "flat" (resolve's module doc).

The corrected reason

This rule used to be justified by an order-side hazard that does not exist: that hybrid auto-cancels orphaned local orders. It does not. resolve synthesizes no OrderCanceled for any kind under any policy — no_policy_ever_synthesizes_a_cancel_for_an_orphan_local_order pins that — and the reconcile lane never calls the venue: everything it emits is a local fold. The advice survives unchanged; only its justification moves, from an order-side hazard to a position-side one that is real.

Each kind has its own page, with the four verdicts computed rather than restated — start from position drift, or from the index for the pass that raises them.

On this page