/** * What a recipe actually does, as opposed to what it says. * * Several fields in this schema are authored in good faith and then have no effect, or a different * effect than the number written down. There is no single mechanism to blame — the causes are * scattered across unrelated subsystems and each is locally reasonable: * * - **Precedence chains.** Margin resolves `margin_pct` → `margin_per_slot` → `budget / slots`; * leverage resolves `default_leverage` → `maxLeverage × multiplier(trading_risk)`. Both are * first-match-wins, so a field below the winning branch is inert. Setting `trading_risk` on a * recipe that also sets `default_leverage` changes nothing, and nothing says so. * - **Silent clamps.** `clampInterval` raises every time-based cut to the DSL tick cadence; * `timeout_seconds` defaults to `interval_seconds`. Both are `Math.max`/`??` with no warning. * - **Dead fields.** `strategy.enabled` is parsed, mapped onto `StrategyConfig`, and read by * nothing. * * The individual fixes are cheap but numerous, and each new one adds another place to forget. One * report that states the effective value and names what was masked kills the whole class at once, * which is why this exists as a resolver rather than as ten scattered warnings. * * ## Pure by construction * * Nothing here reads the network, the venue, or disk. Rules that genuinely depend on live data — * `margin_pct` is a percentage of *withdrawable*, `default_leverage` is clamped by the venue's * per-asset maximum — are reported as the RULE that will apply, never as a number this function * cannot know. A note that invented a dollar figure from a stale balance would be worse than no * note at all. */ import type { RuntimeConfig } from "./runtime-schema.js"; /** Why a field's effective value differs from what was authored. */ export type EffectiveNoteKind = /** Authored, but a higher-precedence field wins and this is never consulted. */ "masked" /** Authored, but a bound moved it. The effective value is what actually applies. */ | "clamped" /** Absent, and another field supplies the value. */ | "defaulted" /** Parsed and mapped, but read by no code path. */ | "inert" /** Nothing is wrong; this names which branch of a precedence chain won. */ | "resolved"; export interface EffectiveNote { /** Dotted path into the recipe, matching `Finding.where.yaml_path` convention. */ yamlPath: string; kind: EffectiveNoteKind; /** What the recipe says. Omitted for `defaulted` (the field is absent). */ authored?: unknown; /** What actually applies. Omitted for `masked` and `inert` — the field has no effect at all. */ effective?: unknown; /** For `masked`: the field that won. */ supersededBy?: string; /** One sentence, rendered verbatim into both the update report and validate output. */ detail: string; } export interface EffectiveConfig { notes: EffectiveNote[]; } /** * Every place this recipe's effective behaviour differs from its authored text. * * An empty `notes` array means every field is doing what it says — which is worth reporting as * such, not as silence. */ export declare function resolveEffectiveConfig(config: RuntimeConfig): EffectiveConfig; /** Notes that mean a field the author wrote is doing nothing at all. */ export declare function ineffectiveNotes(effective: EffectiveConfig): EffectiveNote[]; //# sourceMappingURL=effective-config.d.ts.map