/** * Gate-layer visible-typography reclassifier (W5 rule-8 ruling, 2026-07-21 FINAL * calibration pass). * * The shared trust detector flags every non-ASCII occurrence as * `trust.hidden-unicode`, and the GATE maps that code to `high` (see * `DANGER_SEVERITY` in `scan-gate.ts`). Per the ruling, visible typography in * prose, comments, and human-facing strings is ADVISORY, not blocking — but the * raw detector evidence and severity must be preserved. This module is a * gate-layer OVERLAY that decides, per file, whether a `trust.hidden-unicode` * finding may be DEMOTED to advisory. It NEVER edits the detector (the vet lane's * vendor-lock reproducibility is untouched) and it is applied by `decide()` ONLY * for a seeded selected-profile closure — never for legacy or W4 full-tree paths. * * The classification is fail-closed and PER-FILE (ruling): a file's finding * demotes ONLY if EVERY non-ASCII occurrence in that file is advisory-eligible. * A single always-blocking char (bidi/zero-width/control/format/soft-hyphen/ * suspicious-whitespace/default-ignorable) or a single unproven-context char * (code, identifier, key, heredoc, unquoted scalar, unknown) keeps the finding * high/blocking. * * Decorative eligibility is an EXPLICIT allow-list (dashes, box-drawing, block * elements, geometric shapes, arrows, check/cross, and four curated punctuation * marks) — NOT the broad `\p{S}`/`\p{Pd}` categories — so U+FFFD and unrelated * symbols (e.g. U+00A9) never demote. This module ALSO exposes * {@link classifySentinelLineShape}, the line-shape helper the rule-8 * "EXPECTED_SANITIZER_SENTINEL_LITERAL" acceptances are proven against. */ import type { ScanSeverity } from "./scan-gate.js"; /** The structured overlay attached to a demoted finding (raw severity stays visible). */ export interface TypographyAdvisory { /** The detector/gate severity this finding carried before demotion (always "high"). */ reclassifiedFrom: ScanSeverity; /** The dominant advisory context that justified the demotion (e.g. "comment", "prose"). */ contextClass: string; } /** Per-file verdict from {@link classifyFileTypography}. */ export interface FileTypographyVerdict { /** True iff the file has ≥1 non-ASCII occurrence and ALL of them are advisory. */ demote: boolean; /** Total non-ASCII occurrences examined. */ occurrences: number; /** Dominant advisory context (present when `demote`). */ contextClass?: string; /** Why the file stays blocking (present when NOT `demote`) — the first blocker found. */ blockingReason?: string; } /** One non-ASCII occurrence as the tokenizer saw it (read-only enumeration). */ export interface TypographyOccurrence { char: string; codepoint: string; context: string; alwaysBlocking: boolean; displayDecorative: boolean; } /** * Enumerate every non-ASCII occurrence with the SAME tokenizer contexts the * verdict uses. Read-only reporting/calibration API — carries no policy of its * own (the policy lives in {@link classifyFileTypography}); exists so evidence * tooling can analyze rule variants against the real tokenizer instead of * approximating contexts. */ export declare function enumerateTypography(path: string, text: string): TypographyOccurrence[]; /** * Classify a file's visible typography. Returns `demote: true` only when the file * has ≥1 non-ASCII occurrence and EVERY one is advisory-eligible (visible * typography in a proven prose/comment/human-facing-string context, a decorative * display char in a fence/inline-code/display-string/cat-heredoc, a letter in a * ts/js string or markdown inline code, or an expected emoji presentation * selector). One always-blocking char or one unproven-context char yields * `demote: false` with the first `blockingReason`. */ export declare function classifyFileTypography(path: string, text: string): FileTypographyVerdict; /** The verdict of {@link classifySentinelLineShape}. */ export type SentinelLineShape = "detection-replacement" | "other"; /** * Classify a single source LINE as carrying its special/hidden characters as an * explicit detection/replacement VALUE ("detection-replacement") or not * ("other"), the rule-8 proof that an EXPECTED_SANITIZER_SENTINEL_LITERAL * acceptance rests on: acceptance is valid ONLY where the characters are regex * literals, `.replace(...)` operands, or named string/char-constant tables — and * NOT executable identifiers, commands, paths, keys, or syntax. Fails closed to * "other" for anything unrecognized, and treats command/import/path shapes as * "other" even if they superficially resemble a value. */ export declare function classifySentinelLineShape(line: string): SentinelLineShape;