/**
 * Schema loader — parses every `platform/plugins/memory/references/schema-*.md`
 * file at server startup and returns a structured LoadedSchema that the
 * validator consults on every write.
 *
 * Design decisions:
 *
 * - Header-keyed table parsing. The base schema has 5 columns
 *   (Entity | Neo4j Label | Schema.org Type | Interchange Standard |
 *   Required Properties) while vertical schemas have 4 columns (no
 *   Interchange Standard). We resolve "Neo4j Label" and "Required Properties"
 *   by header name, never by index.
 *
 * - Parenthetical annotations in the Required Properties column are type /
 *   enum / policy hints, not property names:
 *       customer (id)                 -> "customer"
 *       listingType (sale/let)        -> "listingType"
 *       status (scope must be `admin`) -> "status"
 *   We strip the first `(...)` suffix and its contents from every comma-split
 *   token before emitting the property name.
 *
 * - Disjunctions in the Required Properties column use the literal word `or`:
 *       `email` or `telephone`        -> ["email", "telephone"] (any-of)
 *   We split on `\s+or\s+` per token (not per cell) so that a row like
 *   "accountId, email or telephone" yields two groups: one simple, one any-of.
 *
 * - Inheritance sentinel. estate-agent's Applicant and hospitality's
 *   GuestProfile use the literal phrase "Base Person properties + …" in the
 *   required-properties column. A second pass resolves these by merging the
 *   referenced parent label's groups.
 *
 * - Property naming rules (schema-base.md only). The `| Correct | Wrong |`
 *   table yields a `wrongToCorrect` map. Rows whose wrong-column contains
 *   "(on" are context-dependent (e.g. `status` is wrong on Order but valid
 *   on Task/Lead) and are skipped to avoid false positives. A future
 *   per-label synonym system could reintroduce them.
 *
 * Failures throw. A memory server running with a half-parsed schema is
 * worse than one that fails to start.
 */
/** A single required-property rule for a label. */
export type RequiredGroup = {
    kind: "simple";
    name: string;
} | {
    kind: "any-of";
    names: string[];
};
export interface LabelDefinition {
    /** Neo4j label (the string the agent puts in `labels`) */
    label: string;
    /** Required-property groups. Every group must be satisfied. */
    required: RequiredGroup[];
    /** Source file that declared this label (for error messages) */
    sourceFile: string;
    /**
     * Raw inheritance sentinel, if the cell was "Base X properties + extras".
     * Consumed and cleared during the second pass.
     */
    inheritsFrom?: string;
}
export interface LoadedSchema {
    /**
     * Markdown-documented label -> definition. The map carries the
     * required-property and source-file metadata used by the validator's
     * required-property check. It does NOT determine label existence —
     * existence is established by `LiveSchemaSource` (live `db.labels()` ∪
     * `schema.cypher` declarations). A label not present here is "documented
     * by markdown? no" → required-property check is skipped (with a structured
     * log line) but the write is still accepted if the live source recognises
     * the label. (Renamed from `labels` to make the role explicit.)
     */
    markdownLabels: Map<string, LabelDefinition>;
    /** wrong property name -> correct property name (from schema-base.md only) */
    wrongToCorrect: Map<string, {
        correct: string;
        sourceFile: string;
    }>;
    /**
     * forbidden properties per label. Parsed from any
     * "Forbidden Properties" table in any loaded schema markdown file.
     * Validator rejects writes that include any of these property keys for
     * the matching primary label, after synonyms have been rewritten.
     * Source file is preserved for the error message so operators can grep
     * the rule in the canonical reference.
     */
    forbiddenByLabel: Map<string, Map<string, {
        rationale: string;
        sourceFile: string;
    }>>;
    /** Absolute paths of every schema file that was read */
    sourceFiles: string[];
    /**
     * Brand-declared default vertical (the file basename without `.md`, e.g.
     * `"schema-estate-agent"`) or `null` when the brand has no vertical (Maxy
     * default). Read from `brand.json#vertical` at boot.
     *
     * The validator scopes per-write rules to base + the active vertical. When
     * a write resolves a LocalBusiness whose `businessType` differs from this
     * default, the per-LocalBusiness override wins; otherwise this value is
     * the resolved vertical.
     */
    brandVertical: string | null;
}
/**
 * Split a pipe-delimited markdown row into cells, stripping the optional
 * leading/trailing pipes. `|` characters inside a backtick code-span (e.g.
 * `` `Person|Organization` ``) are treated as literal content and do not
 * split the row — without this, a single union-typed cell would shred into
 * multiple cells and the `findTables` width check would throw at module load
 *. Backtick toggling is single-tick only; nested or escaped
 * backticks are out of scope (no schema doc uses them today).
 */
export declare function splitPipeRow(row: string): string[];
export interface LoadSchemaOptions {
    /**
     * Override the brand.json discovery path. When omitted, the loader reads
     * `${process.env.MAXY_PLATFORM_ROOT}/config/brand.json` if the env var is
     * set, otherwise treats the install as dev-default (`brandVertical = null`).
     * Tests inject an absolute fixture path here.
     */
    brandJsonPath?: string;
}
/**
 * Load every `schema-*.md` file from the memory plugin's references directory.
 *
 * The directory is resolved relative to this source file, matching the
 * pattern used by neo4j.ts for config paths. Both src/ and dist/ locations
 * end up four hops above the references directory
 * (src/lib/schema-loader.ts -> src/ -> mcp/ -> memory/ -> references/).
 *
 * Brand vertical resolution: reads `brand.json#vertical` at boot. The path
 * is resolved per `LoadSchemaOptions.brandJsonPath`, falling back to
 * `${MAXY_PLATFORM_ROOT}/config/brand.json`. If the named vertical file is
 * absent from the references directory, the loader throws at boot — a
 * memory server running against a misconfigured brand is worse than one
 * that fails to start. Mirrors paths.ts two-mode resolution: env-unset AND
 * no explicit path = dev-default (`brandVertical = null`).
 */
export declare function loadSchema(opts?: LoadSchemaOptions): LoadedSchema;
//# sourceMappingURL=schema-loader.d.ts.map