import type { InitiativeTracker, Initiative, RolloutStage } from '../core/InitiativeTracker.js';
import type { ApprovalLedger, DivergenceCategory } from '../core/ApprovalLedger.js';
import type { CorrectionLedger } from './CorrectionLedger.js';
/** Risk tiers govern how long a feature may incubate before the window expires. */
export type GrowthRiskTier = 'lowRisk' | 'standard' | 'highRisk';
/** The six notify-rules. R1/R2 = feature maturity (the key lever); R3 =
 *  initiatives left behind; R4 = spec approve-vs-change pattern; R5 = correction
 *  pattern; R6 = dev-gate conformance (a registered dev-gated feature observed
 *  DARK on this dev agent — the forgot-the-gate / misconfig class). */
export type GrowthRuleId = 'R1' | 'R2' | 'R3' | 'R4' | 'R5' | 'R6';
export type GrowthFindingPriority = 'low' | 'normal' | 'high';
export type GrowthSuggestedAction = 'promote' | 'extend-fix-kill' | 'review' | 'bake-in-default' | 'route-correction';
export interface GrowthFinding {
    rule: GrowthRuleId;
    priority: GrowthFindingPriority;
    /** Stable identity of the thing this finding is about (initiative id /
     *  approval decision-class / correction dedupe-key). */
    subjectId: string;
    title: string;
    detail: string;
    suggestedAction: GrowthSuggestedAction;
    stage?: RolloutStage;
    daysInStage?: number;
    windowDays?: number;
    proved?: boolean | 'unknown';
}
export interface GrowthDigestCounts {
    incubating: number;
    promotionReady: number;
    expiredUnproven: number;
    stalling: number;
    specPatterns: number;
    correctionPatterns: number;
    /** R6: dev-gated features observed DARK on this dev agent (should be live). */
    devGateDark: number;
}
export interface GrowthDigest {
    generatedAt: string;
    /** True when NOTHING crossed a notify-rule. The digest still renders a short
     *  "all healthy" line so the operator knows the analyst ran — the deliberate
     *  reversal of the over-silence default that made the old digest never speak. */
    calm: boolean;
    summary: string;
    findings: GrowthFinding[];
    counts: GrowthDigestCounts;
    /** Days until the soonest incubation window closes (drives the calm message:
     *  "next window closes in Xd"). Undefined when nothing is incubating. */
    nextWindowClosesInDays?: number;
}
/** Resolved, defaulted settings (mirrors the `monitoring.growthAnalyst` config). */
export interface GrowthAnalystSettings {
    enabled: boolean;
    incubationWindows: Record<GrowthRiskTier, number>;
    proofOfLifeMinActivations: number;
    /** Per-rule enable flags. */
    rules: {
        promotionReady: boolean;
        incubationExpired: boolean;
        initiativeStalling: boolean;
        specPattern: boolean;
        correctionPattern: boolean;
        devGateConformance: boolean;
    };
    /** R4: minimum decisions in a class before a spec-pattern is worth surfacing. */
    specPatternMinTotal: number;
    /** R4: fraction of decisions that must be approved-with-change. */
    specPatternMinChangeRatio: number;
    /** R5: occurrences before a correction pattern surfaces. */
    correctionPatternMinOccurrences: number;
    /** Whether to render the "all healthy" line even when calm (default true). */
    digestEvenWhenCalm: boolean;
}
export declare const DEFAULT_GROWTH_WINDOWS: Record<GrowthRiskTier, number>;
export declare const DEFAULT_GROWTH_SETTINGS: GrowthAnalystSettings;
/** Resolve raw config (possibly partial / undefined) into fully-defaulted
 *  settings. Pure — safe to unit test, and used by both the class and routes. */
export declare function resolveGrowthSettings(raw?: Partial<GrowthAnalystSettings> | Record<string, unknown>): GrowthAnalystSettings;
export interface StageObservation {
    stage: RolloutStage;
    firstObservedAt: string;
}
export type StageJournal = Record<string, StageObservation>;
export type RolloutClassification = 'incubating' | 'promotion-ready' | 'expired-unproven' | 'terminal';
export interface RolloutVerdict {
    classification: RolloutClassification;
    stage: RolloutStage;
    daysInStage: number;
    windowDays: number;
    /** true / false / 'unknown' (no evidence source wired — treated as NOT proved
     *  for promotion, but surfaced honestly as unknown rather than asserted). */
    proved: boolean | 'unknown';
}
/** Whole calendar-ish days between an ISO timestamp and `now` (fractional). */
export declare function daysSince(iso: string, now: Date): number;
/** Map a feature to a risk tier. Heuristic for now (overridable later via an
 *  explicit per-feature tag). default-on/live features that already shipped are
 *  treated 'standard'; everything dark is 'standard' unless flagged lowRisk. */
export declare function riskTierForInitiative(init: Initiative): GrowthRiskTier;
/** The core verdict. PURE. `proofCount === undefined` ⇒ no evidence source wired
 *  ⇒ proved:'unknown' ⇒ cannot be promotion-ready (honest: never promote a
 *  feature we can't prove ran). */
export declare function classifyRollout(stage: RolloutStage, daysInStage: number, windowDays: number, proofCount: number | undefined, minProof: number): RolloutVerdict;
export interface GrowthMilestoneAnalystDeps {
    /** Where to persist the stage journal (the agent stateDir root). */
    stateDir: string;
    /** Resolved settings (use resolveGrowthSettings on the raw config). */
    settings: GrowthAnalystSettings;
    /** Initiatives + feature-rollout state (required). */
    tracker: Pick<InitiativeTracker, 'list' | 'digest'>;
    /** Spec approve-vs-change ledger (optional — R4 skipped when absent). */
    approvalLedger?: Pick<ApprovalLedger, 'summarize'> | null;
    /** Corrections ledger (optional — R5 skipped when absent). */
    correctionLedger?: Pick<CorrectionLedger, 'list'> | null;
    /** Proof-of-life counter: how many times did this feature actually fire?
     *  Returns undefined when no evidence source is wired (⇒ proved:'unknown'). */
    evidenceCounter?: (init: Initiative) => number | undefined;
    /** Injectable clock for deterministic tests. */
    now?: () => Date;
    /** Optional error sink. */
    onError?: (where: string, err: unknown) => void;
    /** R6: the live agent config, for the dev-gate conformance cross-check. Only
     *  meaningful on a development agent; absent/null ⇒ R6 is skipped. Read with
     *  getConfigByPath (which accepts unknown), so no index signature is required. */
    liveConfig?: {
        developmentAgent?: boolean;
    } | null;
}
export declare class GrowthMilestoneAnalyst {
    private readonly stateDir;
    private readonly journalPath;
    private readonly deps;
    private settings;
    constructor(deps: GrowthMilestoneAnalystDeps);
    private now;
    isEnabled(): boolean;
    private loadJournal;
    private saveJournal;
    /**
     * Observe the current rollout stage of every staged feature and reconcile the
     * journal: new feature → record; stage changed → reset firstObservedAt; gone →
     * prune. Returns the updated journal (also persisted). PUBLIC so a tick can run
     * the observation step independently of finding computation.
     */
    observeStages(now?: Date): StageJournal;
    private stagedInitiatives;
    /** R1 + R2: feature maturity via the tight incubation window. */
    computeRolloutFindings(now: Date, journal: StageJournal): GrowthFinding[];
    /** R3: initiatives left behind — reuse the tracker's own staleness digest. */
    computeStallingFindings(now: Date): GrowthFinding[];
    /** R4: spec approve-vs-change pattern — "you keep changing X the same way." */
    computeSpecPatternFindings(): GrowthFinding[];
    /** R5: correction pattern — a recurring correction worth surfacing. The
     *  CORRECTION-PREFERENCE spec owns the routing; this only SURFACES it. */
    computeCorrectionFindings(now: Date): GrowthFinding[];
    /** Run the full observation + computation pass. Returns all findings. */
    computeFindings(now?: Date): GrowthFinding[];
    /** R6 (dev-gate conformance): on a development agent, every registered
     *  dev-gated feature MUST resolve LIVE. One observed DARK means it was wired
     *  wrong / misconfigured (a hardcoded enabled:false default, or an operator
     *  override) — the forgot-the-gate class the Slice-1 lint can't see and the
     *  Slice-2 default test only catches at the default level. Surfacing it here
     *  makes it a live finding on the very dev agent it affects.
     *  Spec: DEV-AGENT-DARK-GATE-CONFORMANCE-SPEC (Slice 3). */
    private computeDevGateConformanceFindings;
    /** Build the operator-facing digest. Calm digests still render so the operator
     *  knows the analyst ran (fixes the old "near-silent → never speaks" failure). */
    buildDigest(now?: Date): GrowthDigest;
    /** Lightweight status for a GET route / dashboard. */
    getStatus(now?: Date): {
        enabled: boolean;
        settings: GrowthAnalystSettings;
        counts: GrowthDigestCounts;
        nextWindowClosesInDays?: number;
    };
    private countIncubating;
    private nextWindowClose;
    private safeProof;
    private calmSummary;
    private activeSummary;
}
/** The divergence category with the highest count (ties → first by declaration),
 *  or null when there are no divergences at all. PURE. */
export declare function dominantDivergence(counts: Record<DivergenceCategory, number>): {
    category: DivergenceCategory;
    count: number;
} | null;
//# sourceMappingURL=GrowthMilestoneAnalyst.d.ts.map