import { type RawRecord } from './sidecar.js'; import type { BlockDef, CompiledDefinition, ConceptIR, Finding, Row } from './types.js'; export interface ParseDocResult { concept: ConceptIR | null; findings: Finding[]; } export interface ParseDocOptions { /** raw text of `.records.yaml`, or null when the document * has none. Passed IN rather than read here: this module is pure by * contract. */ sidecar?: string | null; /** raw text of `.records.local.yaml` — the untracked * working-layer mirror (L6) — or null when the document has none. A * separate field, not a merged text: the two files answer to different * tier rules, and merging before parsing would erase which file a record * arrived in, which is the only fact those rules judge. */ localSidecar?: string | null; } /** `Flows/Checkout.md` → `Flows/Checkout.records.yaml`. The pairing rule, in * one place — a sidecar is found by NAME, never by a frontmatter declaration. * * `.records.yaml`, not bare `.yaml` (0017 q4): the suffix makes sidecar * status OPT-IN, so an unrelated `X.yaml` beside `X.md` — an OpenAPI spec, a * config — is never hard-errored as a sidecar with unknown blocks, and no * escape hatch has to exist. The suffix also declares intent on its own, * which is what lets an ORPHANED records file be reported instead of * vanishing silently into the asset list. */ export declare function sidecarPathFor(docPath: string): string; /** The values a `ref:` cell carries. A cell may name SEVERAL sibling rows — * "t1 · t3" — split on the list separators the vault's records already use * (` · ` between displayed values, `,` in hand-written cells). Two guards * keep the splitter honest about ids that LOOK like lists: * · the WHOLE cell is tried first against `isId` (the caller's "does a row * by this exact name exist" test) — an id needs no grammar, so a * free-text id may legally contain `,` or `·`, and a cell that names one * row must not be torn into fragments that all dangle; * · a cell that is nothing but separators ("·", ",,") comes back as ITSELF, * one refutable value — pre-split it earned a grammar-mismatch or * dangling finding, and [] would let it pass silently. * A cell with no separator is its own single value, so single-value refs * keep every behaviour (and every finding, byte for byte) they had. Declared * empty markers are dropped per value the same way they are per cell. */ export declare const refCellValues: (def: CompiledDefinition, cell: string, isId?: (v: string) => boolean) => string[]; /** * Rows a sidecar carries — routed through the SAME `validateRows` a table * goes through, so the resulting `Row[]` is indistinguishable downstream. * Nothing in derive, links, render, the dashboard or the graph learns that * sidecars exist. * * Exported for exactly ONE importer: `records.ts`'s `validateRecords` facade, * the public door a write gate (the cloud's op write door, its referee) walks * through to judge records by the identical rules this parser applies. Reach * for the facade, not this function — the facade is the API surface; this * export exists so there is one funnel, not a re-implementation of it. */ export declare function rowsFromRecords(sidecarFile: string, block: BlockDef, records: RawRecord[], def: CompiledDefinition, findings: Finding[]): Row[]; export declare function parseDoc(path: string, source: string, def: CompiledDefinition, opts?: ParseDocOptions): ParseDocResult; /** * ONE row's mention scan — EXPORTED as the cell-scan parity seam (review of * PR6c, rulings L11/L13). mentionItemKey hashes `context` into the read-state * key, and the cloud's `applyOpPatch` must rebuild `mention_live` for a * patched operational row with EXACTLY the spelling the baked D.mentions * carries — person-column detection via the display style, the trim, the * 200-char context clamp, the owner fields — or the same assignee mention * gets two item_keys and read state splits between the inbox and the baked * page. Before this export that composition lived inline in the parse loop, * and the parity contract rested on the cloud re-implementing it * byte-faithfully; now both sides call the one function (the cloud through * the same deep-dist import path it uses for scanMentions itself). * * The rules, restated where they are pinned by test: * · a PERSON-styled display column is split as handles (`source: * 'person'` — an Assignee value is the strongest mention and carries no * `@`); every other column is @-scanned (`source: 'cell'`). One column, * one scanner — running both over a person cell would index `@felipe` * in it twice under two sources (the dedupe L11's UNION is built on); * · `context` is the trimmed cell value clamped to 200 chars (§2.6); * · the row's address is L13's OwnerSource spelling: display id in `row`, * uuid beside it when the record carries one — mentionItemKey's idOf * ladder then prefers the uuid, so a mention on an operational row * survives a display-column rename; * · empty cells (including the format's declared empty markers) mint * nothing. * Unknown block → [] (total like the rest of the mention seam — the write * door validates block names before it ever scans). */ export declare function scanRowMentions(def: CompiledDefinition, block: string, row: Row): NonNullable; /** Vault-relative path for a local media src; null for remote/data URLs. */ export declare function resolveMediaPath(docPath: string, src: string): string | null; /** Reserved files (index.md, log.md, …) must not declare a concept type, and * a log must be readable as a history: OKF §9 wants date-grouped entries, * newest first. An out-of-order log is the one that stops being read. */ export declare function lintReserved(path: string, source: string, findings: Finding[]): void;