/** Native KERN handler-body AST eligibility — slice α-3. * * Replaces slice 5a's regex pre-screen (in `native-eligibility.ts`) with a * TS-AST walk that mirrors the migrator's `mapStatement` rules in * `packages/cli/src/commands/migrate-native-handlers.ts`. After this slice, * the diagnostic and the migrator agree by construction: * * classifyHandlerBodyAst(body).eligible === true * ⟺ kern migrate native-handlers will emit a `lang="kern"` rewrite for it. * * Why this matters: the slice 5a regex disagreed with the migrator's deeper * TS-AST shape check on ~34% of "eligible" bodies (agon scan, 2026-05-04). * Promoting the diagnostic from `info` to `warn` at that disagreement rate * would surface fix-or-suppress noise on bodies the migrator silently bails * on — exactly the no-unused-vars trust-collapse pattern. AST agreement is * the prerequisite for the future warn promotion. * * The reason strings here are deliberately specific (e.g. `var-destructure`, * `try-finally`, `expr-stmt-mutation`) so users running * `kern migrate native-handlers` and `kern review` see actionable hints * instead of a generic "ineligible". */ import ts from 'typescript'; /** A snapshot of the recorded coherence violations (reasons emitted with no * taxonomy row). Empty in a coherent build. */ export declare function coherenceViolations(): readonly string[]; /** Test-only: clear the recorded coherence violations between runs. */ export declare function resetCoherenceViolations(): void; export interface AstEligibilityResult { eligible: boolean; /** When eligible: 'empty' | 'ok'. * When ineligible: a short kebab-case slug naming the first blocking shape. * Examples: 'comments-present', 'ts-parse-error', 'var-destructure', * 'var-non-const', 'try-finally', 'for-stmt', 'expr-stmt-mutation', * 'return-bad-expr', 'unsupported-stmt-'. */ reason: string; } /** True when `exprText` parses cleanly under KERN's parser-expression. * * Multi-line input is accepted: the parser itself is whitespace-insensitive, * and the migrator round-trips through `canonicalKernExpression` before * emitting into a quoted attribute value, so the original line shape never * reaches the .kern serializer. * * Exported so the migrator (`migrate-native-handlers.ts`) shares the * same predicate the classifier uses — slice α-3 gemini review pulled * the formerly duplicated helper into core to prevent the migrator's * bail conditions from drifting away from the classifier's pass * conditions. */ export declare function isValidKernExpression(exprText: string): boolean; export declare function isValidKernAssignmentTarget(exprText: string): boolean; export declare function isValidKernAssignmentValue(exprText: string): boolean; /** Return a single-line form of `exprText` suitable for a quoted KERN * attribute value. The migrator uses this so multi-line const initializers * (`const x = {\n a: 1,\n};`) can lift to `let name=x value="{ a: 1 }"` * without leaking the source line shape into the .kern serialization. * * Returns `null` if: * - KERN's `parseExpression` rejects it (caller would bail anyway), or * - the TS parser rejects the wrapped form, or * - the expression contains a multi-line template literal whose newlines * are semantically significant and cannot be collapsed. * * Why not `emitExpression`: that serializer translates KERN stdlib calls * to their TS-native form (`List.map(arr, fn)` → `arr.map(fn)`), which is * correct for codegen but wrong here — the migrator must keep the surface * call shape (`List.map(...)`) so the next round-trip parses to the same * KERN IR. TS printer + newline-collapse preserves the surface form while * normalizing whitespace outside string/template literals. * * The migrator's `--verify` pre/post codegen diff catches any drift the * normalization introduces. */ export declare function canonicalKernExpression(exprText: string): string | null; export declare function canonicalObjectEntriesSource(expr: ts.Expression, sf: ts.SourceFile): string | null; export declare function isValidKernTypeAnnotation(typeText: string): boolean; /** True when a TS template-literal body contains an escape sequence the * cross-target `fmt` codegen can't safely lower. We only admit escapes that * have the **same runtime semantics** in both TS template literals and * Python f-strings: * * `\n` `\t` `\r` `\b` `\f` `\v` `\0` `\\` `\'` `\"` * `\xNN` (exactly 2 hex digits) * `\uNNNN` (exactly 4 hex digits — NOT the ES2015 `\u{…}` brace form, * which Python f-strings reject) * `` \` `` (TS-only escape — Python emitter drops the `\`) * `\${` (TS-only escape — Python emitter emits `${{` to render literal `${`) * * Anything else — including TS identity escapes like `\{`, `\}`, `\a`, `\?` * — drifts in Python (TS silently drops the backslash; Python either errors * on `\{` or interprets `\a` as BEL 0x07). Reject those bodies so they stay * in raw `<<<>>>` handlers instead of producing invalid or divergent Python. * * Exported so the migrator applies the same predicate (eligibility ≡ * migrator invariant). (Codex impl-review P2 fix: widened from just * rejecting `\u{` to a full cross-target safe-set check.) */ export declare function hasTsOnlyTemplateEscape(body: string): boolean; /** True when `bodyText` contains any line or block comment. The migrator * drops comments silently on rewrite, so a body containing them is * ineligible — preserving the comment is the user's responsibility. * Exported (slice α-3 gemini review) so the migrator imports the same * scanner predicate and comment-detection cannot diverge between the * two sides. */ export declare function hasComments(bodyText: string): boolean; export declare function hasOnlyMigratableComments(bodyText: string): boolean; /** Classify a raw `<<<…>>>` handler body — the AST-aware replacement for * the slice 5a regex pass. Returns `eligible: true` only if every top-level * statement (and every nested if/try branch) maps to a body-statement form * the migrator can emit. */ export declare function classifyHandlerBodyAst(rawBody: string, opts?: { allowNonBlock?: boolean; }): AstEligibilityResult;