/**
 * Per-action body emitter — Phase 1 scaffolding for the Realize L3
 * per-action redesign (proposal: 2026-05-07-REALIZE-L3-PER-ACTION).
 *
 * Reframes the unit of work for action body emission from per-step
 * (one helper per spec step bullet, glued by step1Result → step2Result
 * envelopes) to per-action (one coherent method body per action, with
 * step bullets as spec input rather than code-output structure).
 *
 * Three branches, in priority order:
 *
 * 1. **Full-convention coverage** — the L2 convention matcher returns
 *    a non-AI-fallback `MatchResult` for every step in the action.
 *    The matched per-step TS snippets are assembled deterministically
 *    into the method body (no LLM call).
 *
 * 2. **LLM-driven emission** — at least one step lacks convention
 *    coverage AND a real LLM is available. We delegate to the
 *    microcall-orchestrator hook with the convention-matched scaffolding
 *    as pre-baked context (DR3-A: pre-baked TS shown to LLM as
 *    scaffolding, structural validator post-emission). Phase 1 stubs
 *    the prompt name (`realize-action.prompt.yaml`) — that asset lands
 *    in Phase 2; here we just thread the call site.
 *
 * 3. **Stub-mode body** (DR4-γ) — when neither full-convention nor LLM
 *    is available, emit a body that:
 *      a. Throws `Error("<action>: not implemented")` at function entry
 *         so the failure is unambiguous at runtime.
 *      b. Preserves the convention-matched scaffolding as a code-block
 *         comment beneath the throw, with explicit "Gaps:" entries
 *         enumerating the steps the matcher couldn't cover.
 *    Captures stub-mode review value without misleading partial execution.
 *
 * Phase 1 scope:
 *   - Just the orchestrator + emitter scaffolding. The realize-time
 *     wiring (replacing the per-step `.ai.ts` emission path), the
 *     `realize-action.prompt.yaml` asset, and the actual LLM call
 *     plumbing all land in subsequent phases (3, 2, and 3 respectively).
 *   - Out of scope: `.ai.ts` deletion (DR2: keep the filename, repurpose
 *     content shape — Phase 3 work).
 */
import type { runPrompt as runPromptFn } from '../ai/prompt-runner.js';
export type { SharedStepContext, SharedConvention, MatchResult, MatcherFn, } from '@specverse/types';
import type { SharedStepContext, SharedConvention, MatchResult, MatcherFn } from '@specverse/types';
/**
 * The unit of work the emitter is asked to fill. A spec-derived view of
 * an action / operation. Mirrors the AST `ExecutablePropertiesSpec`
 * shape but kept local + plain so callers in different phases of the
 * realize pipeline can construct it without coupling to AST internals.
 */
export interface ActionSpec {
    /** Action / operation name as it appears in the spec (e.g. `processOrder`). */
    name: string;
    /** Owning component / class — for prompt context + identity. */
    ownerName: string;
    /** Whether this owner is a controller, service, or model.behaviors host. */
    ownerKind: 'controller' | 'service' | 'model';
    /** The spec step bullets — what the LLM (or matcher) is filling in. */
    steps: string[];
    /** Action-level metadata, threaded into both the matcher and the LLM. */
    description?: string;
    parameters?: Record<string, string>;
    returns?: string;
    requires?: string[];
    ensures?: string[];
    publishes?: string[];
}
/**
 * Per-action emission context. Carries everything the emitter needs to
 * dispatch a step against the L2 matcher and / or hand off to the LLM.
 *
 * Phase 1 keeps this minimal + additive — Phases 2/3 will add fields
 * (cross-action sibling context, processContextByMethodKey, manifest
 * resolved factory, …) without breaking existing call sites.
 */
export interface RealizeContext {
    /** Component name the owning entity lives in. */
    componentName: string;
    /** Spec model name powering this controller / service (e.g. `OrderProcessor`). */
    modelName: string;
    /**
     * The shared matcher walker (`matchAgainstConventions`) lives under
     * `engines/libs/instance-factories/services/templates/_shared/` —
     * outside `rootDir: 'src'`. Phase 1 keeps the emitter decoupled from
     * the libs path by accepting the matcher as an injected callable.
     * The realize-time wire-in (Phase 3) will dynamic-import it the same
     * way `realize/index.ts` does for the per-step matcher; tests inject
     * a lightweight stand-in.
     *
     * Optional in Phase 1: when omitted, every step is treated as
     * "unmatched", exercising the LLM / stub branches uniformly.
     */
    matcher?: MatcherFn;
    /**
     * The convention list to walk. The shared matcher
     * (`matchAgainstConventions`) is library-agnostic but the conventions
     * array carries the ORM / driver-specific patterns. Caller passes the
     * resolved factory's conventions (prisma / mongodb-native /
     * postgres-native / future).
     */
    conventions?: ReadonlyArray<SharedConvention<SharedStepContext>>;
    /**
     * When the matcher leaves a step unmatched it computes an AI-fallback
     * call site shape. The expression for that call's input object varies
     * per-ORM (prisma uses bare `{ a, b, c }`; pg-native uses
     * `{ args: { a, b, c } }`). Caller passes its ORM's expression. Phase
     * 1 default keeps things ORM-neutral.
     */
    aiArgsExpr?: (inputs: string[], paramNames: string[]) => string;
    /**
     * Hook for the per-action LLM emission. Phase 1 threads this through
     * but does NOT wire into a real prompt (the `realize-action.prompt.yaml`
     * asset is a Phase 2 deliverable). Tests inject a stub; production
     * Phase 3 will inject a microcall-orchestrator-backed implementation.
     *
     * Returning `null` is equivalent to "LLM unavailable" → falls through
     * to the stub-mode γ branch.
     */
    llmEmit?: LlmEmitFn;
    /**
     * Override for "is the LLM available?". Default: respects
     * `SPECVERSE_AI_PROVIDER` — `stub` → false, anything else → true.
     * Tests use this to deterministically force the stub-γ branch.
     */
    canCallLLM?: () => boolean;
    /**
     * V2 P3 Round 3 follow-up: clients exposed to the LLM via the prompt's
     * `availableClients` slot. Per-component scope — every action in the
     * same owner sees the same string. Built from the consumer component's
     * `import:` block by the runner. When absent, the LLM emit hook falls
     * back to its constructor-time `opts.availableClients`, then to a
     * "(none)" placeholder.
     */
    availableClients?: string;
}
/**
 * The LLM emission hook. Phase 1 is a thin pass-through to a microcall-
 * orchestrator-style handler; Phase 3 will replace this with a wrapper
 * around `runActionMicrocalls` (or a direct `runPrompt` call) using the
 * `realize-action.prompt.yaml` asset.
 */
export type LlmEmitFn = (request: LlmEmitRequest) => Promise<string | null>;
/**
 * Inputs handed to the LLM emission hook. Carries the action's full
 * spec body plus the per-step scaffolding produced by the L2 matcher
 * (DR3-A pre-baked TS shown to LLM).
 */
export interface LlmEmitRequest {
    action: ActionSpec;
    context: RealizeContext;
    /** Matcher output for each step, in spec order. `null` means unmatched. */
    perStepMatches: Array<MatchResult | null>;
    /**
     * The pre-baked TS scaffolding (matched steps' `.call` strings joined).
     * Phase 2's prompt feeds this to the LLM verbatim alongside the spec
     * step bullets.
     */
    scaffoldingSnippet: string;
    /**
     * Phase 1 placeholder. The prompt name the orchestrator should use.
     * The asset itself doesn't exist yet (Phase 2 deliverable); Phase 3
     * implementations should pin this to `realize-action.prompt.yaml`.
     */
    promptName: string;
    /**
     * Optional thread-through to the underlying `runPrompt`. Lets tests
     * assert the call shape without standing up an LLM provider.
     */
    runPromptOverride?: typeof runPromptFn;
}
/**
 * When every step is convention-matched, assemble the method body
 * deterministically by joining the matcher's `.call` strings.
 *
 * The call strings come pre-indented (4 spaces — enough for "inside a
 * method body") and trailing-newline-free. We wrap them with a
 * try-block + a `return` at the end where possible (last step's
 * resultVar). Phase 3 will refine the assembly (typed signatures,
 * error-flow shapes) once the realize-time wiring decides on a single
 * controller / service template path; Phase 1 keeps it minimal so the
 * emitter's contract is stable.
 */
export declare function assembleFromConventions(action: ActionSpec, perStepMatches: MatchResult[]): string;
/**
 * Emit the γ-shaped stub body (DR4 resolution). The function body opens
 * with a `throw new Error("<action>: not implemented")` so the failure
 * surfaces unambiguously at runtime, and the convention-matched
 * scaffolding is preserved as a code-block comment beneath the throw
 * for reviewer context.
 *
 * Format:
 *   throw new Error("processOrder: not implemented");
 *   /* Convention-matched scaffolding (preserved for review):
 *    *   <pre-baked TS for matched steps>
 *    * Gaps:
 *    *   - <step text for unmatched steps>
 *    *\/
 */
export declare function emitStubGammaBody(action: ActionSpec, perStepMatches: Array<MatchResult | null>): string;
/**
 * Emit the body for a single action.
 *
 * Branch order:
 *   1. Full-convention coverage  → assembled deterministically.
 *   2. Mixed coverage + LLM available + `llmEmit` returns text → LLM result.
 *   3. Otherwise (stub mode, no LLM, or `llmEmit` returned null) → γ stub.
 *
 * The signature returns just the body (not the wrapping method
 * declaration / params / return type); Phase 3 will integrate this
 * with the controller / service template emitters.
 */
export declare function emitActionBody(action: ActionSpec, context: RealizeContext): Promise<string>;
//# sourceMappingURL=per-action-emitter.d.ts.map