@lokascript/framework 4.6.0 → 4.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (116) hide show
  1. package/CHANGELOG.md +121 -1
  2. package/dist/api/create-dsl.d.ts +18 -2
  3. package/dist/api/create-dsl.d.ts.map +1 -1
  4. package/dist/api/index.d.ts +1 -0
  5. package/dist/api/index.d.ts.map +1 -1
  6. package/dist/api/index.js +1799 -713
  7. package/dist/api/index.js.map +1 -1
  8. package/dist/api/translation-loss.d.ts +84 -0
  9. package/dist/api/translation-loss.d.ts.map +1 -0
  10. package/dist/core/index.d.ts +1 -0
  11. package/dist/core/index.d.ts.map +1 -1
  12. package/dist/core/index.js +1057 -684
  13. package/dist/core/index.js.map +1 -1
  14. package/dist/core/matching/confidence-model.d.ts +54 -0
  15. package/dist/core/matching/confidence-model.d.ts.map +1 -0
  16. package/dist/core/matching/index.d.ts +12 -0
  17. package/dist/core/matching/index.d.ts.map +1 -0
  18. package/dist/core/matching/match-skeleton.d.ts +114 -0
  19. package/dist/core/matching/match-skeleton.d.ts.map +1 -0
  20. package/dist/core/matching/stock-confidence.d.ts +15 -0
  21. package/dist/core/matching/stock-confidence.d.ts.map +1 -0
  22. package/dist/core/matching/stock-reader.d.ts +63 -0
  23. package/dist/core/matching/stock-reader.d.ts.map +1 -0
  24. package/dist/core/matching/stock-reading.d.ts +44 -0
  25. package/dist/core/matching/stock-reading.d.ts.map +1 -0
  26. package/dist/core/matching/value-reader.d.ts +62 -0
  27. package/dist/core/matching/value-reader.d.ts.map +1 -0
  28. package/dist/core/pattern-matching/index.js +561 -660
  29. package/dist/core/pattern-matching/index.js.map +1 -1
  30. package/dist/core/pattern-matching/pattern-matcher.d.ts +0 -117
  31. package/dist/core/pattern-matching/pattern-matcher.d.ts.map +1 -1
  32. package/dist/core/types.d.ts +53 -0
  33. package/dist/core/types.d.ts.map +1 -1
  34. package/dist/core/types.js.map +1 -1
  35. package/dist/generation/index.d.ts +2 -0
  36. package/dist/generation/index.d.ts.map +1 -1
  37. package/dist/generation/index.js +593 -1
  38. package/dist/generation/index.js.map +1 -1
  39. package/dist/generation/pattern-render.d.ts +72 -0
  40. package/dist/generation/pattern-render.d.ts.map +1 -0
  41. package/dist/generation/schema-patterns.d.ts +152 -0
  42. package/dist/generation/schema-patterns.d.ts.map +1 -0
  43. package/dist/index.cjs +2016 -904
  44. package/dist/index.cjs.map +1 -1
  45. package/dist/index.js +1998 -903
  46. package/dist/index.js.map +1 -1
  47. package/dist/ir/index.js.map +1 -1
  48. package/dist/parsing/index.js +561 -660
  49. package/dist/parsing/index.js.map +1 -1
  50. package/dist/testing/gate/index.d.ts +8 -0
  51. package/dist/testing/gate/index.d.ts.map +1 -0
  52. package/dist/testing/gate/index.js +664 -0
  53. package/dist/testing/gate/index.js.map +1 -0
  54. package/dist/testing/gate/lint/format.d.ts +7 -0
  55. package/dist/testing/gate/lint/format.d.ts.map +1 -0
  56. package/dist/testing/gate/lint/index.d.ts +30 -0
  57. package/dist/testing/gate/lint/index.d.ts.map +1 -0
  58. package/dist/testing/gate/lint/rules/extractor-coverage.d.ts +18 -0
  59. package/dist/testing/gate/lint/rules/extractor-coverage.d.ts.map +1 -0
  60. package/dist/testing/gate/lint/rules/index.d.ts +14 -0
  61. package/dist/testing/gate/lint/rules/index.d.ts.map +1 -0
  62. package/dist/testing/gate/lint/rules/keyword-classification.d.ts +19 -0
  63. package/dist/testing/gate/lint/rules/keyword-classification.d.ts.map +1 -0
  64. package/dist/testing/gate/lint/rules/keyword-coverage.d.ts +13 -0
  65. package/dist/testing/gate/lint/rules/keyword-coverage.d.ts.map +1 -0
  66. package/dist/testing/gate/lint/rules/marker-tokenization.d.ts +20 -0
  67. package/dist/testing/gate/lint/rules/marker-tokenization.d.ts.map +1 -0
  68. package/dist/testing/gate/lint/rules/position-ordering.d.ts +25 -0
  69. package/dist/testing/gate/lint/rules/position-ordering.d.ts.map +1 -0
  70. package/dist/testing/gate/lint/rules/renderer-coherence.d.ts +24 -0
  71. package/dist/testing/gate/lint/rules/renderer-coherence.d.ts.map +1 -0
  72. package/dist/testing/gate/lint/rules/schema-rules.d.ts +10 -0
  73. package/dist/testing/gate/lint/rules/schema-rules.d.ts.map +1 -0
  74. package/dist/testing/gate/lint/types.d.ts +74 -0
  75. package/dist/testing/gate/lint/types.d.ts.map +1 -0
  76. package/dist/testing/gate/round-trip.d.ts +184 -0
  77. package/dist/testing/gate/round-trip.d.ts.map +1 -0
  78. package/package.json +6 -2
  79. package/src/__test__/fixtures/sql-dsl.ts +8 -2
  80. package/src/api/create-dsl-engine.test.ts +150 -0
  81. package/src/api/create-dsl.test.ts +7 -0
  82. package/src/api/create-dsl.ts +173 -22
  83. package/src/api/index.ts +1 -0
  84. package/src/api/translation-loss.test.ts +89 -0
  85. package/src/api/translation-loss.ts +128 -0
  86. package/src/core/index.ts +1 -0
  87. package/src/core/matching/confidence-model.ts +58 -0
  88. package/src/core/matching/index.ts +12 -0
  89. package/src/core/matching/match-skeleton.test.ts +179 -0
  90. package/src/core/matching/match-skeleton.ts +621 -0
  91. package/src/core/matching/stock-confidence.ts +238 -0
  92. package/src/core/matching/stock-reader.test.ts +265 -0
  93. package/src/core/matching/stock-reader.ts +205 -0
  94. package/src/core/matching/stock-reading.ts +681 -0
  95. package/src/core/matching/value-reader.ts +85 -0
  96. package/src/core/pattern-matching/pattern-matcher.ts +11 -876
  97. package/src/core/types.ts +53 -0
  98. package/src/generation/index.ts +2 -0
  99. package/src/generation/pattern-render.ts +378 -0
  100. package/src/generation/schema-patterns.test.ts +139 -0
  101. package/src/generation/schema-patterns.ts +974 -0
  102. package/src/testing/gate/index.ts +8 -0
  103. package/src/testing/gate/lint/format.ts +23 -0
  104. package/src/testing/gate/lint/index.ts +73 -0
  105. package/src/testing/gate/lint/lint.test.ts +131 -0
  106. package/src/testing/gate/lint/rules/extractor-coverage.ts +63 -0
  107. package/src/testing/gate/lint/rules/index.ts +37 -0
  108. package/src/testing/gate/lint/rules/keyword-classification.ts +52 -0
  109. package/src/testing/gate/lint/rules/keyword-coverage.ts +38 -0
  110. package/src/testing/gate/lint/rules/marker-tokenization.ts +129 -0
  111. package/src/testing/gate/lint/rules/position-ordering.ts +87 -0
  112. package/src/testing/gate/lint/rules/renderer-coherence.ts +100 -0
  113. package/src/testing/gate/lint/rules/schema-rules.ts +35 -0
  114. package/src/testing/gate/lint/types.ts +83 -0
  115. package/src/testing/gate/round-trip.test.ts +208 -0
  116. package/src/testing/gate/round-trip.ts +503 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/testing/gate/round-trip.ts","../../../src/testing/gate/lint/rules/schema-rules.ts","../../../src/testing/gate/lint/rules/keyword-coverage.ts","../../../src/testing/gate/lint/rules/marker-tokenization.ts","../../../src/testing/gate/lint/rules/keyword-classification.ts","../../../src/testing/gate/lint/rules/extractor-coverage.ts","../../../src/testing/gate/lint/rules/position-ordering.ts","../../../src/testing/gate/lint/rules/renderer-coherence.ts","../../../src/testing/gate/lint/rules/index.ts","../../../src/testing/gate/lint/format.ts","../../../src/testing/gate/lint/index.ts"],"sourcesContent":["/**\n * The round-trip gate kit (domains rethink Phase 4.6, `docs-internal/DOMAINS_RETHINK.md`): every\n * command of a grammar, written in each of its languages by its own renderer and read back by its own\n * parser. Moved from lokascript-domains' `packages/domains/gate/round-trip.ts` (the domain gate, Phase\n * 2), so any grammar on the framework runs the same gate; the domains repo keeps its fixtures\n * (`gate/values.ts`), baselines and regen tool.\n *\n * A case is a command (bracket syntax, parsed in English) and a language:\n *\n * - **round trip**: `render(ref, lang)`, then `parse`. `pass` when the parse is the reference node, or,\n * for a grammar whose compiled output does not depend on the language, when the text compiles to what\n * the command itself (its bracket form) compiles to (a parser may keep a translated word that the\n * generator normalizes: bdd ja `#box0 が 表示 前提` is `visible`). English is a lane like the others.\n * `refused` when the render is null or the parse throws: the caller is told. `silent` otherwise: it\n * reads, or compiles to, something else.\n * - **leftover**: the same render plus one unknown word. `refused` is the expected outcome: the parse\n * throws, or the input does not compile. `pass` where the word is READ: the parse is the text's own\n * with the word in one role (an open-text role extended by it, `where active zzqx`, or a role the\n * text left empty, `list zzqx`), and the word is in the code (for a command that never compiles on\n * its own, `englishDoesNotCompile`, the parse alone). `silent` otherwise: the input is accepted and\n * read as something else (`when clicks zzqx`: actor `clicks`, action `zzqx`), or the word goes\n * nowhere (voice `back zzqx` compiles as `back`).\n *\n * The outcomes are the command-shape gate's (hyperfixi `testing-framework`). A baseline lists every\n * case that is not at its best outcome; `compare` reports a case worse than listed and one better than\n * listed, so a gate built on it only shrinks.\n */\n\nexport type Outcome = 'pass' | 'refused' | 'silent';\nexport type CaseKind = 'roundTrip' | 'leftover';\n\n/** Best first. A round trip's best is `pass`; leftover input's is `refused`. */\nexport const RANK: Record<CaseKind, Record<Outcome, number>> = {\n roundTrip: { pass: 2, refused: 1, silent: 0 },\n leftover: { refused: 2, pass: 2, silent: 0 },\n};\nexport const BEST: Record<CaseKind, Outcome> = { roundTrip: 'pass', leftover: 'refused' };\n\n/** Appended for the leftover cases: a word no grammar's vocabulary has. */\nexport const UNKNOWN_WORD = 'zzqx';\n\nexport interface CaseResult {\n /** The grammar (a domain's name). */\n grammar: string;\n kind: CaseKind;\n /** The command, in bracket syntax (read in English). */\n source: string;\n lang: string;\n outcome: Outcome;\n /** What the renderer wrote (plus the unknown word, for leftover). */\n text?: string;\n /** Why it is not `pass`: the error, or what the parse read instead. */\n why?: string;\n /** The round trip's parse, summarized (`summarize`); absent when it was refused. */\n read?: string;\n}\n\n/** What the gate reads of a role in a schema. */\nexport interface GateRoleSpec {\n readonly role: string;\n readonly required?: boolean;\n readonly expectedTypes?: readonly string[];\n}\n\n/** What the gate reads of a command schema. */\nexport interface GateSchema {\n readonly action: string;\n readonly roles: readonly GateRoleSpec[];\n}\n\n/** What the gate reads of a parse. */\nexport interface GateNode {\n readonly action: string;\n readonly roles: ReadonlyMap<string, unknown> | Readonly<Record<string, unknown>>;\n}\n\n/** The grammar's DSL, as the gate drives it. */\nexport interface GateDSL {\n parse(input: string, language: string): GateNode;\n compile(input: string, language: string): { ok: boolean; code?: string; errors?: string[] };\n render?(node: GateNode, language: string): string | null;\n getSupportedLanguages(): string[];\n}\n\n/**\n * The values the gate writes into each role. A role not listed gets a plain name no vocabulary uses\n * (`v0x`, or `#box0` where only a selector fits): its value must come back verbatim. A role that takes\n * FIXED words lists every word the grammar's renderer and generator know, in English: the gate writes\n * the command once per word, in every language, so a word one of the two tables lacks fails in the\n * languages where it is missing.\n */\nexport interface GateFixture {\n /**\n * `compile`: a round trip also passes when the text compiles to the command's own code (the grammar\n * compiles to something that does not depend on the language). `node`: only the parse counts (a\n * grammar that compiles to a sentence in each language).\n */\n oracle: 'compile' | 'node';\n /** Keys are `<action>.<role>` or `<role>` (every action). The first value is the one the other cases use. */\n values: Record<string, string[]>;\n /**\n * Commands (bracket syntax) written besides the generated ones, where one role's value depends on\n * another's and no single list fits both: behaviorspec's `[given subject:#box value:visible]` beside\n * `[given subject:viewport value:1024x768]`.\n */\n commands?: string[];\n /**\n * Actions that do not compile on their own (a gate may check every other one does). Leftover input\n * in such a command is judged by its parse alone.\n */\n englishDoesNotCompile?: string[];\n}\n\n/** A value no vocabulary uses: a selector where only a selector fits, a plain name otherwise. */\nfunction defaultValue(role: GateRoleSpec, i: number): string {\n const types = role.expectedTypes ?? [];\n if (types.includes('selector') && !types.includes('literal') && !types.includes('expression'))\n return `#box${i}`;\n return `v${i}x`;\n}\n\nfunction valuesFor(fixture: GateFixture, action: string, role: GateRoleSpec, i: number): string[] {\n return (\n fixture.values[`${action}.${role.role}`] ?? fixture.values[role.role] ?? [defaultValue(role, i)]\n );\n}\n\n/**\n * The commands a grammar is checked on: for each schema, its required roles alone and all its roles\n * (each with its first value), then each further value of a role that takes fixed words, alone.\n */\nexport function commandsFor(schemas: readonly GateSchema[], fixture: GateFixture): string[] {\n const out = new Set<string>();\n for (const schema of schemas) {\n const required = schema.roles.filter(r => r.required);\n const variants =\n required.length === schema.roles.length ? [required] : [required, [...schema.roles]];\n for (const roles of variants) {\n const values = roles.map((r, i) => valuesFor(fixture, schema.action, r, i));\n const write = (pick: string[]) =>\n `[${schema.action}${roles.map((r, i) => ` ${r.role}:${pick[i]}`).join('')}]`;\n const first = values.map(v => v[0]!);\n out.add(write(first));\n values.forEach((vs, i) => {\n for (const v of vs.slice(1)) out.add(write(first.map((f, j) => (j === i ? v : f))));\n });\n }\n }\n for (const command of fixture.commands ?? []) out.add(command);\n return [...out];\n}\n\nfunction roleEntries(node: GateNode): [string, unknown][] {\n return node.roles instanceof Map\n ? [...(node.roles as ReadonlyMap<string, unknown>)]\n : Object.entries(node.roles ?? {});\n}\n\nfunction roleValues(node: GateNode): Record<string, string> {\n return Object.fromEntries(\n roleEntries(node)\n .map(([k, v]): [string, string] => [\n k,\n String((v as { value?: unknown })?.value ?? (v as { raw?: unknown })?.raw ?? v),\n ])\n .sort(([a], [b]) => (a < b ? -1 : 1))\n );\n}\n\n/** A parse as one line: `action role:type=value …`, roles sorted. What the parse snapshot records. */\nexport function summarize(node: GateNode): string {\n const roles = roleEntries(node)\n .map(([k, v]) => {\n const t = (v as { type?: unknown })?.type;\n const raw = (v as { value?: unknown })?.value ?? (v as { raw?: unknown })?.raw ?? v;\n const text = String(raw);\n return `${k}${t ? `:${String(t)}` : ''}=${/[\\s=]/.test(text) ? JSON.stringify(text) : text}`;\n })\n .sort();\n return [node.action, ...roles].join(' ');\n}\n\nfunction describeRead(want: Record<string, string>, ref: GateNode, back: GateNode): string {\n if (back.action !== ref.action) return `another command: ${back.action}`;\n const got = roleValues(back);\n const lost = Object.keys(want).filter(k => !(k in got));\n const added = Object.keys(got).filter(k => !(k in want));\n const changed = Object.keys(want).filter(k => k in got && got[k] !== want[k]);\n const parts = [\n ...lost.map(k => `${k} lost`),\n ...added.map(k => `${k} added (${got[k]})`),\n ...changed.map(k => `${k}: ${got[k]}`),\n ];\n return parts.join(', ');\n}\n\nconst message = (e: unknown) => String((e as Error)?.message ?? e).slice(0, 160);\n\n/** What `text` compiles to, or `undefined` when it does not compile. */\nfunction codeOf(dsl: GateDSL, text: string, lang: string): string | undefined {\n try {\n const c = dsl.compile(text, lang);\n return c.ok && typeof c.code === 'string' ? c.code : undefined;\n } catch {\n return undefined;\n }\n}\n\n/**\n * Whether `read`, the parse of a text plus `word`, is `back`, the text's own parse, with the word read\n * into it: the same action and roles, except one role, whose value is its value in `back` followed by\n * the word (an open-text role at the end: sql `where active zzqx`) or, where `back` has no such role,\n * the word itself (todo `list zzqx`).\n */\nexport function readsWord(back: GateNode, read: GateNode, word: string): boolean {\n if (read.action !== back.action) return false;\n const was = roleValues(back);\n const now = roleValues(read);\n let changed = 0;\n for (const role of new Set([...Object.keys(was), ...Object.keys(now)])) {\n if (was[role] === now[role]) continue;\n if (now[role] !== (role in was ? `${was[role]} ${word}` : word)) return false;\n changed++;\n }\n return changed === 1;\n}\n\n/** What a command (bracket syntax) compiles to; null when it does not compile on its own. */\nexport function referenceCode(dsl: GateDSL, source: string): string | null {\n try {\n const c = dsl.compile(source, 'en');\n return c.ok && typeof c.code === 'string' ? c.code : null;\n } catch {\n return null;\n }\n}\n\n/** Run every case of one grammar: each command of `commandsFor` in each of the DSL's languages. */\nexport function runRoundTrip(\n grammar: string,\n dsl: GateDSL,\n schemas: readonly GateSchema[],\n fixture: GateFixture\n): CaseResult[] {\n const languages = dsl.getSupportedLanguages();\n const results: CaseResult[] = [];\n\n for (const source of commandsFor(schemas, fixture)) {\n const ref = dsl.parse(source, 'en');\n const want = roleValues(ref);\n const sameNode = (back: GateNode) =>\n back.action === ref.action && JSON.stringify(roleValues(back)) === JSON.stringify(want);\n\n // The code the command itself compiles to: its bracket form, not its English render, or the English\n // lane would be measured against itself (bdd's `When double-click` reads `double`, and compiled to a\n // comment that every correct language then \"differed\" from). Null when the command does not compile\n // alone; then only the node can pass.\n const refCode = fixture.oracle === 'compile' ? referenceCode(dsl, source) : null;\n\n for (const lang of languages) {\n const at = { grammar, source, lang };\n const text = dsl.render?.(ref, lang) ?? null;\n if (!text) {\n results.push({ ...at, kind: 'roundTrip', outcome: 'refused', why: 'no render' });\n continue;\n }\n let back: GateNode;\n try {\n back = dsl.parse(text, lang);\n } catch (e) {\n results.push({ ...at, kind: 'roundTrip', outcome: 'refused', text, why: message(e) });\n continue;\n }\n\n let outcome: Outcome = 'silent';\n let why: string | undefined;\n if (sameNode(back)) outcome = 'pass';\n else if (refCode !== null) {\n let code: string | undefined;\n try {\n const c = dsl.compile(text, lang);\n code = c.ok ? c.code : undefined;\n } catch {\n code = undefined;\n }\n if (code === refCode) outcome = 'pass';\n else why = `compiles differently; reads ${describeRead(want, ref, back)}`;\n } else why = `reads ${describeRead(want, ref, back)}`;\n results.push({\n ...at,\n kind: 'roundTrip',\n outcome,\n text,\n ...(why ? { why } : {}),\n read: summarize(back),\n });\n\n // Leftover input: only where the text itself reads, or a refusal would say nothing.\n const extended = `${text} ${UNKNOWN_WORD}`;\n let read: GateNode;\n try {\n read = dsl.parse(extended, lang);\n } catch {\n results.push({ ...at, kind: 'leftover', outcome: 'refused', text: extended });\n continue;\n }\n // Read with the word, the word must reach the code. Where the input does not compile, compile\n // tells the caller (`refused`), except for a command that never compiles on its own (bdd's\n // `and`), where only the parse can say.\n const kept = readsWord(back, read, UNKNOWN_WORD);\n const code = kept ? codeOf(dsl, extended, lang) : undefined;\n const alone = fixture.englishDoesNotCompile?.includes(read.action) ?? false;\n const [left, leftWhy]: [Outcome, string | undefined] = !kept\n ? [\n 'silent',\n `accepted as ${read.action}; reads ${describeRead(roleValues(back), back, read)}`,\n ]\n : code !== undefined\n ? code.includes(UNKNOWN_WORD)\n ? ['pass', undefined]\n : ['silent', `accepted as ${read.action}; the word does not reach the code`]\n : alone\n ? ['pass', undefined]\n : ['refused', 'does not compile'];\n results.push({\n ...at,\n kind: 'leftover',\n outcome: left,\n text: extended,\n ...(leftWhy ? { why: leftWhy } : {}),\n read: summarize(read),\n });\n }\n }\n return results;\n}\n\n// ---------------------------------------------------------------------------------------------------\n// The baseline\n// ---------------------------------------------------------------------------------------------------\n\nexport interface ListedCase {\n outcome: Exclude<Outcome, 'pass'>;\n text?: string;\n why?: string;\n}\n\nexport interface GateBaseline {\n /** The upstream packages this was measured on (the grammar's lockfile). */\n upstream: Record<string, string>;\n /** Per grammar and kind: cases and outcomes. Written by the regen tool for the reader; not checked. */\n counts: Record<string, Record<CaseKind, Record<Outcome | 'cases', number>>>;\n /** Per grammar, command and language: every round trip that is not `pass`. */\n roundTrip: Record<string, Record<string, Record<string, ListedCase>>>;\n /** Per grammar and command: the languages whose leftover case was accepted (`silent`). */\n leftover: Record<string, Record<string, string[]>>;\n}\n\nexport interface DriftCase {\n kind: CaseKind;\n source: string;\n lang: string;\n listed: Outcome;\n now: Outcome;\n text?: string;\n why?: string;\n}\n\nexport interface Drift {\n worse: DriftCase[];\n better: DriftCase[];\n /** Listed cases that are no longer cases at all. */\n stale: { kind: CaseKind; source: string; lang: string }[];\n}\n\nfunction listedOutcome(\n baseline: GateBaseline,\n r: Pick<CaseResult, 'grammar' | 'kind' | 'source' | 'lang'>\n): Outcome {\n if (r.kind === 'roundTrip')\n return baseline.roundTrip[r.grammar]?.[r.source]?.[r.lang]?.outcome ?? 'pass';\n return baseline.leftover[r.grammar]?.[r.source]?.includes(r.lang) ? 'silent' : 'refused';\n}\n\n/** How one grammar's results moved against the baseline. */\nexport function compare(grammar: string, results: CaseResult[], baseline: GateBaseline): Drift {\n const drift: Drift = { worse: [], better: [], stale: [] };\n const seen = new Set<string>();\n for (const r of results) {\n seen.add(`${r.kind}\\u0000${r.source}\\u0000${r.lang}`);\n const listed = listedOutcome(baseline, r);\n const rank = RANK[r.kind];\n const entry = { kind: r.kind, source: r.source, lang: r.lang, listed, now: r.outcome };\n if (rank[r.outcome] < rank[listed])\n drift.worse.push({\n ...entry,\n ...(r.text !== undefined ? { text: r.text } : {}),\n ...(r.why !== undefined ? { why: r.why } : {}),\n });\n else if (rank[r.outcome] > rank[listed]) drift.better.push(entry);\n }\n const listedCases: { kind: CaseKind; source: string; lang: string }[] = [\n ...Object.entries(baseline.roundTrip[grammar] ?? {}).flatMap(([source, langs]) =>\n Object.keys(langs).map(lang => ({ kind: 'roundTrip' as const, source, lang }))\n ),\n ...Object.entries(baseline.leftover[grammar] ?? {}).flatMap(([source, langs]) =>\n langs.map(lang => ({ kind: 'leftover' as const, source, lang }))\n ),\n ];\n for (const c of listedCases)\n if (!seen.has(`${c.kind}\\u0000${c.source}\\u0000${c.lang}`)) drift.stale.push(c);\n return drift;\n}\n\n/** The baseline entries for one grammar's results. */\nexport function listingOf(results: CaseResult[]): {\n roundTrip: Record<string, Record<string, ListedCase>>;\n leftover: Record<string, string[]>;\n} {\n const roundTrip: Record<string, Record<string, ListedCase>> = {};\n const leftover: Record<string, string[]> = {};\n for (const r of results) {\n if (r.kind === 'roundTrip' && r.outcome !== 'pass') {\n (roundTrip[r.source] ??= {})[r.lang] = {\n outcome: r.outcome,\n ...(r.text ? { text: r.text } : {}),\n ...(r.why ? { why: r.why } : {}),\n };\n } else if (r.kind === 'leftover' && r.outcome === 'silent')\n (leftover[r.source] ??= []).push(r.lang);\n }\n return { roundTrip, leftover };\n}\n\nexport function countsOf(\n results: CaseResult[]\n): Record<CaseKind, Record<Outcome | 'cases', number>> {\n const counts = {\n roundTrip: { cases: 0, pass: 0, refused: 0, silent: 0 },\n leftover: { cases: 0, pass: 0, refused: 0, silent: 0 },\n };\n for (const r of results) {\n counts[r.kind].cases++;\n counts[r.kind][r.outcome]++;\n }\n return counts;\n}\n\n// ---------------------------------------------------------------------------------------------------\n// The parse snapshot\n// ---------------------------------------------------------------------------------------------------\n\n/**\n * Every round trip's parse, so a change that moves a parse shows it, whether or not an outcome moved\n * (a value's type, a role a passing case gained, what a silent case reads now). `refused` where the\n * parse threw. Leftover cases are not in it: their outcome is the baseline's.\n */\nexport interface ParseSnapshot {\n upstream: Record<string, string>;\n /** Per grammar, command and language. */\n parses: Record<string, Record<string, Record<string, string>>>;\n}\n\nexport function snapshotOf(results: CaseResult[]): Record<string, Record<string, string>> {\n const out: Record<string, Record<string, string>> = {};\n for (const r of results)\n if (r.kind === 'roundTrip') (out[r.source] ??= {})[r.lang] = r.read ?? 'refused';\n return out;\n}\n\nexport interface SnapshotChange {\n source: string;\n lang: string;\n was?: string;\n now?: string;\n}\n\n/** Every round trip whose parse differs from the snapshot's, for one grammar. */\nexport function snapshotChanges(\n grammar: string,\n results: CaseResult[],\n snapshot: ParseSnapshot\n): SnapshotChange[] {\n const now = snapshotOf(results);\n const was = snapshot.parses[grammar] ?? {};\n const changes: SnapshotChange[] = [];\n for (const source of new Set([...Object.keys(was), ...Object.keys(now)]))\n for (const lang of new Set([\n ...Object.keys(was[source] ?? {}),\n ...Object.keys(now[source] ?? {}),\n ])) {\n const a = was[source]?.[lang];\n const b = now[source]?.[lang];\n if (a !== b)\n changes.push({\n source,\n lang,\n ...(a !== undefined ? { was: a } : {}),\n ...(b !== undefined ? { now: b } : {}),\n });\n }\n return changes;\n}\n","/**\n * Schema-structure rules (R1, R2).\n *\n * Delegates to the validator the input brings (`DomainLintInput.validateSchema`): the domains pass\n * `@lokascript/semantic`'s `validateCommandSchema`, whose checks are already solid. We only surface its\n * error- and warning-level items; notes are dropped to keep linter output focused.\n */\n\nimport type { LintFinding, LintRule } from '../types';\n\nconst RULE_ID = 'schema-structure';\n\nexport const schemaStructureRule: LintRule = input => {\n const findings: LintFinding[] = [];\n if (!input.validateSchema) return findings;\n\n for (const schema of input.schemas) {\n const result = input.validateSchema(schema);\n for (const item of result.items) {\n if (item.severity === 'note') continue; // skip notes — not actionable at lint time\n findings.push({\n rule: `${RULE_ID}:${item.code}`,\n severity: item.severity,\n message: `[${schema.action}] ${item.message}`,\n context: {\n domain: input.name,\n action: String(schema.action),\n code: item.code,\n },\n });\n }\n }\n\n return findings;\n};\n","/**\n * R6: Keyword coverage across profiles.\n *\n * For every schema action + every registered profile, the profile must have\n * a `keywords[action]` entry. Without it, `createMultilingualDSL()` throws\n * at pattern generation time (\"No keyword translation for 'X' in 'Y'\").\n *\n * This rule catches the \"I added a new schema but only wired it into the\n * English profile\" class of drift.\n */\n\nimport type { LintFinding, LintRule } from '../types';\n\nconst RULE_ID = 'keyword-coverage';\n\nexport const keywordCoverageRule: LintRule = input => {\n const findings: LintFinding[] = [];\n\n for (const profile of input.profiles) {\n for (const schema of input.schemas) {\n const action = schema.action;\n if (!profile.keywords[action]) {\n findings.push({\n rule: RULE_ID,\n severity: 'error',\n message: `profile [${profile.code}] has no keyword for action '${String(action)}'`,\n context: {\n domain: input.name,\n lang: profile.code,\n action: String(action),\n },\n });\n }\n }\n }\n\n return findings;\n};\n","/**\n * R7: Marker and keyword tokenization.\n *\n * Every lexical item the parser needs to match atomically — marker words,\n * profile keyword primaries, and profile keyword alternatives — must\n * tokenize as a single token in its target language. A word that splits\n * across multiple tokens (e.g., `añadir` → `[\"a\", \"ñadir\"]` when the\n * tokenizer lacks `LatinExtendedIdentifierExtractor`) will never match a\n * pattern's literal token.\n *\n * This is the exact gap that bit us on the Spanish tokenizer mid-session.\n */\n\nimport type { LintFinding, LintRule, DomainLintInput } from '../types';\nimport type { LanguageTokenizer } from '../../../../core/types';\n\nconst RULE_ID = 'marker-tokenization';\n\nfunction tokenCount(tokenizer: LanguageTokenizer, word: string): number {\n const stream = tokenizer.tokenize(word);\n return stream.tokens.length;\n}\n\nexport function collectLexicalItems(input: DomainLintInput): Array<{\n lang: string;\n word: string;\n source: string; // \"schema marker <action>.<role>\" | \"profile keyword <action>.primary\" | ...\n}> {\n const items: Array<{ lang: string; word: string; source: string }> = [];\n\n // Schema marker overrides (per language)\n for (const schema of input.schemas) {\n for (const role of schema.roles) {\n if (!role.markerOverride) continue;\n for (const [lang, word] of Object.entries(role.markerOverride)) {\n items.push({\n lang,\n word,\n source: `schema markerOverride ${String(schema.action)}.${role.role}`,\n });\n }\n }\n }\n\n // Profile keywords (primary + alternatives)\n for (const profile of input.profiles) {\n for (const [action, entry] of Object.entries(profile.keywords)) {\n if (entry.primary) {\n items.push({\n lang: profile.code,\n word: entry.primary,\n source: `profile keyword ${action}.primary`,\n });\n }\n for (const alt of entry.alternatives ?? []) {\n items.push({\n lang: profile.code,\n word: alt,\n source: `profile keyword ${action}.alternatives`,\n });\n }\n }\n }\n\n // Profile role markers (primary + alternatives)\n for (const profile of input.profiles) {\n for (const [role, marker] of Object.entries(profile.roleMarkers ?? {})) {\n if (marker.primary) {\n items.push({\n lang: profile.code,\n word: marker.primary,\n source: `profile roleMarker ${role}.primary`,\n });\n }\n for (const alt of marker.alternatives ?? []) {\n items.push({\n lang: profile.code,\n word: alt,\n source: `profile roleMarker ${role}.alternatives`,\n });\n }\n }\n }\n\n return items;\n}\n\nexport const markerTokenizationRule: LintRule = input => {\n const findings: LintFinding[] = [];\n const items = collectLexicalItems(input);\n\n for (const { lang, word, source } of items) {\n const tokenizer = input.tokenizers[lang];\n if (!tokenizer) {\n // Tokenizer for this language isn't registered with the linter.\n // R6 (keyword-coverage) catches the schema side; nothing to do here.\n continue;\n }\n\n // Empty-string markers mean \"no marker in this language\" (an explicit\n // opt-out pattern used by domain-learn to signal that a role is\n // positional in some languages but marker-bearing in others).\n if (word === '') continue;\n\n // Whitespace-containing markers (e.g., 'mettre-à-jour' is one word, but\n // 'ask X to Y' wouldn't be) — tokenize the raw word and expect 1 token.\n // Multi-word markers with explicit spaces are out of scope for this rule;\n // skip them rather than flag false positives.\n if (/\\s/.test(word)) continue;\n\n const count = tokenCount(tokenizer, word);\n if (count !== 1) {\n findings.push({\n rule: RULE_ID,\n severity: 'error',\n message: `\"${word}\" tokenizes as ${count} tokens in ${lang} (${source})`,\n context: {\n domain: input.name,\n lang,\n word,\n source,\n tokenCount: count,\n },\n });\n }\n }\n\n return findings;\n};\n","/**\n * R5: Keyword classification — profile keywords ⊆ tokenizer keyword lists.\n *\n * Every lexical item the parser must recognize as a keyword — profile\n * keyword primaries/alternatives, profile role markers, and schema\n * `markerOverride` words — must actually *classify* as `'keyword'` in that\n * language's tokenizer. This is stricter than R7 (marker-tokenization):\n * a word can tokenize as a single token yet come back as `'identifier'`\n * when the tokenizer's keyword list doesn't contain it.\n *\n * Under the bridge (`buildDomainTokenizer`) profile and tokenizer derive\n * from the same vocab file, so this rule is vacuously green there. It\n * exists for hand-authored tokenizers (e.g. domain-learn) and custom\n * tokenizer overrides, where a vocab/profile edit can silently go stale\n * against the tokenizer's keyword list.\n */\n\nimport type { LintFinding, LintRule } from '../types';\nimport { collectLexicalItems } from './marker-tokenization';\n\nconst RULE_ID = 'keyword-classification';\n\nexport const keywordClassificationRule: LintRule = input => {\n const findings: LintFinding[] = [];\n\n for (const { lang, word, source } of collectLexicalItems(input)) {\n const tokenizer = input.tokenizers[lang];\n if (!tokenizer) continue; // R6 catches unregistered languages.\n\n // Same opt-outs as R7: '' means \"no marker in this language\"; multi-word\n // items can't be a single keyword token and are out of scope.\n if (word === '' || /\\s/.test(word)) continue;\n\n const kind = tokenizer.classifyToken(word);\n if (kind !== 'keyword' && kind !== 'particle') {\n findings.push({\n rule: RULE_ID,\n severity: 'error',\n message: `\"${word}\" classifies as '${kind}' (not 'keyword') in ${lang} (${source})`,\n context: {\n domain: input.name,\n lang,\n word,\n source,\n kind,\n },\n });\n }\n }\n\n return findings;\n};\n","/**\n * R8: Latin-script tokenizers should extract diacritic identifiers as single tokens.\n *\n * The default `IdentifierExtractor` matches `[a-zA-Z0-9_]*` and stops at any\n * diacritic. Words like `añadir` split into `[\"a\", \"ñadir\"]` unless the\n * tokenizer also registers `LatinExtendedIdentifierExtractor`. This rule\n * probes the behavior by tokenizing a canonical diacritic-containing string\n * for each language and warning if it doesn't come back as a single token.\n *\n * Warning (not error) because:\n * - English tokenizers legitimately don't need the extended extractor.\n * - A domain might deliberately split compounds for its own reasons.\n * The warning prompts the author to audit; an explicit waiver can add an\n * opt-out once we have a mechanism for it.\n */\n\nimport type { LintFinding, LintRule } from '../types';\n\nconst RULE_ID = 'extractor-coverage';\n\n/**\n * Canonical diacritic samples per language. Each must be a single logical\n * word in the target language. Chosen to exercise the specific characters\n * that tend to split under the default ASCII extractor.\n */\nconst DIACRITIC_SAMPLES: Record<string, string> = {\n es: 'añadir', // ñ\n fr: 'déjà', // é, à\n pt: 'coração', // ç, ã\n de: 'größer', // ö, ß\n tr: 'değiştir', // ğ, ş\n it: 'però', // ò\n pl: 'kochać', // ć\n cs: 'příliš', // ř, í\n // Not run for en, ja, ar, ko, zh, ru, hi, etc. — either pure ASCII\n // (en) or non-Latin scripts where the Unicode extractor handles them.\n};\n\nexport const extractorCoverageRule: LintRule = input => {\n const findings: LintFinding[] = [];\n\n for (const [lang, sample] of Object.entries(DIACRITIC_SAMPLES)) {\n const tokenizer = input.tokenizers[lang];\n if (!tokenizer) continue; // language not registered in this domain\n\n const stream = tokenizer.tokenize(sample);\n if (stream.tokens.length !== 1) {\n findings.push({\n rule: RULE_ID,\n severity: 'warning',\n message: `[${lang}] diacritic probe \"${sample}\" tokenizes as ${stream.tokens.length} tokens — tokenizer likely missing LatinExtendedIdentifierExtractor`,\n context: {\n domain: input.name,\n lang,\n sample,\n tokenCount: stream.tokens.length,\n },\n });\n }\n }\n\n return findings;\n};\n","/**\n * R9: Role position uniqueness within a schema.\n *\n * `sortRolesByWordOrder` in the pattern generator sorts roles descending by\n * svoPosition or sovPosition (higher value = earlier in surface form). If two\n * roles share the same position value, their relative order is undefined\n * (falls through to array stability) — a latent footgun because token\n * ordering silently changes if someone reorders the roles array.\n *\n * This rule warns when a schema has multiple roles AND any two share an\n * explicit position for the same word order.\n *\n * Warning (not error) because:\n * - Single-role schemas have nothing to order.\n * - A schema might intentionally tie two roles when only one is present\n * at a time (e.g., mutually-exclusive alternatives). In practice we\n * haven't seen this, but the rule should prompt a review, not block.\n *\n * The `sovPosition: 0` default assigned when omitted is NOT flagged — only\n * explicit non-zero collisions count. Otherwise most schemas with 3+\n * optional roles would warn spuriously.\n */\n\nimport type { LintFinding, LintRule } from '../types';\n\nconst RULE_ID = 'position-ordering';\n\nfunction findCollisions(\n values: Array<{ role: string; position: number }>\n): Array<{ position: number; roles: string[] }> {\n const byPosition = new Map<number, string[]>();\n for (const { role, position } of values) {\n const existing = byPosition.get(position) ?? [];\n existing.push(role);\n byPosition.set(position, existing);\n }\n return Array.from(byPosition.entries())\n .filter(([, roles]) => roles.length > 1)\n .map(([position, roles]) => ({ position, roles }));\n}\n\nexport const positionOrderingRule: LintRule = input => {\n const findings: LintFinding[] = [];\n\n for (const schema of input.schemas) {\n if (schema.roles.length < 2) continue;\n\n const svoValues = schema.roles\n .filter(r => typeof r.svoPosition === 'number' && r.svoPosition !== 0)\n .map(r => ({ role: r.role, position: r.svoPosition! }));\n const sovValues = schema.roles\n .filter(r => typeof r.sovPosition === 'number' && r.sovPosition !== 0)\n .map(r => ({ role: r.role, position: r.sovPosition! }));\n\n for (const { position, roles } of findCollisions(svoValues)) {\n findings.push({\n rule: RULE_ID,\n severity: 'warning',\n message: `[${String(schema.action)}] roles ${roles.join(', ')} share svoPosition=${position} — order is undefined`,\n context: {\n domain: input.name,\n action: String(schema.action),\n wordOrder: 'SVO',\n position,\n roles: roles.join(','),\n },\n });\n }\n\n for (const { position, roles } of findCollisions(sovValues)) {\n findings.push({\n rule: RULE_ID,\n severity: 'warning',\n message: `[${String(schema.action)}] roles ${roles.join(', ')} share sovPosition=${position} — order is undefined`,\n context: {\n domain: input.name,\n action: String(schema.action),\n wordOrder: 'SOV',\n position,\n roles: roles.join(','),\n },\n });\n }\n }\n\n return findings;\n};\n","/**\n * R10: Renderer-table coherence (opt-in).\n *\n * Some domains ship a natural-language renderer that maintains its own\n * per-language keyword/marker tables separate from the profile data. This\n * rule — when the domain passes `input.renderer` — asserts that:\n * - Every `commandKeywords[action][lang]` matches `profile.keywords[action].primary`\n * for that language.\n * - Every `markers[name][lang]` appears as a markerOverride value somewhere\n * in the schemas for that language (can't pin to a specific role since\n * renderer marker names are arbitrary labels).\n *\n * Warnings only. Domains without `input.renderer` get a no-op pass — this\n * rule is inherently opt-in because not every domain has round-trip\n * rendering and not every renderer uses the same table shape.\n *\n * Catches the exact drift we fixed earlier this session: domain-sql's\n * Turkish source marker was `dan` in the `get` schema but `den` in the\n * renderer's MARKERS table. Had this rule existed, it would have warned\n * the moment `get` was added with the wrong Turkish variant.\n */\n\nimport type { LintFinding, LintRule } from '../types';\n\nconst RULE_ID = 'renderer-coherence';\n\nexport const rendererCoherenceRule: LintRule = input => {\n if (!input.renderer) return [];\n const findings: LintFinding[] = [];\n\n // commandKeywords ↔ profile primaries\n for (const [action, langMap] of Object.entries(input.renderer.commandKeywords)) {\n for (const [lang, rendererWord] of Object.entries(langMap)) {\n const profile = input.profiles.find(p => p.code === lang);\n if (!profile) continue; // lang not registered; R6 handles that\n const profileEntry = profile.keywords[action];\n if (!profileEntry) {\n findings.push({\n rule: RULE_ID,\n severity: 'warning',\n message: `renderer has '${action}' keyword for ${lang} (\"${rendererWord}\") but profile has no entry for that action`,\n context: { domain: input.name, lang, action, rendererWord },\n });\n continue;\n }\n const profilePrimary = profileEntry.primary;\n const profileAlts = profileEntry.alternatives ?? [];\n if (rendererWord !== profilePrimary && !profileAlts.includes(rendererWord)) {\n findings.push({\n rule: RULE_ID,\n severity: 'warning',\n message: `renderer '${action}' keyword for ${lang} is \"${rendererWord}\" but profile has primary=\"${profilePrimary}\"${profileAlts.length ? ` (alternatives: ${profileAlts.join(', ')})` : ''}`,\n context: {\n domain: input.name,\n lang,\n action,\n rendererWord,\n profilePrimary,\n },\n });\n }\n }\n }\n\n // markers ↔ schema markerOverride values (loose check — any role in any\n // schema for that lang must have this marker value somewhere)\n const markerByLang = new Map<string, Set<string>>();\n for (const schema of input.schemas) {\n for (const role of schema.roles) {\n if (!role.markerOverride) continue;\n for (const [lang, word] of Object.entries(role.markerOverride)) {\n const set = markerByLang.get(lang) ?? new Set<string>();\n set.add(word);\n markerByLang.set(lang, set);\n }\n }\n }\n\n for (const [markerName, langMap] of Object.entries(input.renderer.markers)) {\n for (const [lang, rendererWord] of Object.entries(langMap)) {\n const schemaMarkers = markerByLang.get(lang);\n if (!schemaMarkers) continue; // lang has no markerOverride anywhere; skip\n if (!schemaMarkers.has(rendererWord)) {\n findings.push({\n rule: RULE_ID,\n severity: 'warning',\n message: `renderer marker \"${markerName}\" in ${lang} is \"${rendererWord}\" but no schema role has that markerOverride value — likely drift`,\n context: {\n domain: input.name,\n lang,\n markerName,\n rendererWord,\n },\n });\n }\n }\n }\n\n return findings;\n};\n","/**\n * Rule registry — runs all enabled rules and aggregates findings.\n */\n\nimport type { DomainLintInput, LintFinding, LintRule } from '../types';\nimport { schemaStructureRule } from './schema-rules';\nimport { keywordCoverageRule } from './keyword-coverage';\nimport { keywordClassificationRule } from './keyword-classification';\nimport { markerTokenizationRule } from './marker-tokenization';\nimport { extractorCoverageRule } from './extractor-coverage';\nimport { positionOrderingRule } from './position-ordering';\nimport { rendererCoherenceRule } from './renderer-coherence';\n\n/**\n * All rules in execution order.\n * Phase 1: schema structure (delegates to @lokascript/semantic)\n * Phase 2: keyword coverage + keyword classification + marker tokenization\n * Phase 3: extractor coverage + position ordering (warnings only)\n * Phase 3b: renderer coherence (opt-in via DomainLintInput.renderer)\n */\nexport const ALL_RULES: readonly LintRule[] = [\n schemaStructureRule,\n keywordCoverageRule,\n keywordClassificationRule,\n markerTokenizationRule,\n extractorCoverageRule,\n positionOrderingRule,\n rendererCoherenceRule,\n];\n\nexport function runRules(input: DomainLintInput): LintFinding[] {\n const findings: LintFinding[] = [];\n for (const rule of ALL_RULES) {\n findings.push(...rule(input));\n }\n return findings;\n}\n","/**\n * Pretty-print lint results for test output.\n */\n\nimport type { LintFinding, LintResult, Severity } from './types';\n\nconst SEVERITY_GLYPH: Record<Severity, string> = {\n error: '✗',\n warning: '⚠',\n note: 'ℹ',\n};\n\nexport function formatFinding(finding: LintFinding): string {\n const glyph = SEVERITY_GLYPH[finding.severity];\n return ` ${glyph} [${finding.rule}] ${finding.message}`;\n}\n\nexport function formatResult(result: LintResult): string {\n const header = `domain-toolkit: lint(${result.domain}) — ${result.errorCount} error(s), ${result.warningCount} warning(s)`;\n if (result.findings.length === 0) return `${header}\\n (clean)`;\n const body = result.findings.map(formatFinding).join('\\n');\n return `${header}\\n${body}`;\n}\n","/**\n * The domain linter (domains rethink Phase 4.6): moved from lokascript-domains'\n * `@lokascript/domain-toolkit`. Checks schema structure and cross-file\n * invariants (profile ↔ tokenizer ↔ schema ↔ renderer agreement); a grammar\n * runs it from one lint test.\n *\n * Implemented rules:\n * - R1/R2 (schema-structure): delegates to the validator the input\n * brings (`validateSchema`: the domains pass @lokascript/semantic's).\n * - R5 (keyword-classification): profile keywords/markers classify as\n * 'keyword' in the language's tokenizer.\n * - R6 (keyword-coverage): every schema command has profile keywords for\n * all declared languages.\n * - R7 (marker-tokenization): every marker/keyword tokenizes as a single\n * token.\n * - R8 (extractor-coverage): Latin-script tokenizers handle diacritic\n * identifiers (warning).\n * - R9 (position-ordering): schema role positions are unique per\n * word-order (warning).\n * - R10 (renderer-coherence): renderer table keywords match\n * profile/schema translations (opt-in via `renderer`).\n */\n\nimport { runRules } from './rules';\nimport type { DomainLintInput, LintFinding, LintResult, LintWaiver } from './types';\n\nexport type {\n DomainLintInput,\n LintResult,\n LintFinding,\n LintRule,\n LintWaiver,\n Severity,\n RendererTables,\n SchemaValidationFinding,\n SchemaValidator,\n} from './types';\nexport { formatResult, formatFinding } from './format';\n\nfunction matchesWaiver(finding: LintFinding, waiver: LintWaiver): boolean {\n if (finding.rule !== waiver.rule) return false;\n if (!waiver.matches) return true;\n const ctx = finding.context ?? {};\n for (const [key, expected] of Object.entries(waiver.matches)) {\n if (ctx[key] !== expected) return false;\n }\n return true;\n}\n\nfunction applyWaivers(\n findings: LintFinding[],\n waivers: readonly LintWaiver[] | undefined\n): LintFinding[] {\n if (!waivers || waivers.length === 0) return findings;\n return findings.filter(f => !waivers.some(w => matchesWaiver(f, w)));\n}\n\n/**\n * Lint a domain against all enabled rules and return a structured result.\n */\nexport function lintDomain(input: DomainLintInput): LintResult {\n const raw = runRules(input);\n const findings = applyWaivers(raw, input.waivers);\n const errorCount = findings.filter(f => f.severity === 'error').length;\n const warningCount = findings.filter(f => f.severity === 'warning').length;\n\n return {\n domain: input.name,\n findings,\n errorCount,\n warningCount,\n };\n}\n"],"mappings":"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","names":["code","RULE_ID","RULE_ID","RULE_ID","RULE_ID","RULE_ID","RULE_ID"]}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Pretty-print lint results for test output.
3
+ */
4
+ import type { LintFinding, LintResult } from './types';
5
+ export declare function formatFinding(finding: LintFinding): string;
6
+ export declare function formatResult(result: LintResult): string;
7
+ //# sourceMappingURL=format.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../../../../src/testing/gate/lint/format.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAAY,MAAM,SAAS,CAAC;AAQjE,wBAAgB,aAAa,CAAC,OAAO,EAAE,WAAW,GAAG,MAAM,CAG1D;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,CAKvD"}
@@ -0,0 +1,30 @@
1
+ /**
2
+ * The domain linter (domains rethink Phase 4.6): moved from lokascript-domains'
3
+ * `@lokascript/domain-toolkit`. Checks schema structure and cross-file
4
+ * invariants (profile ↔ tokenizer ↔ schema ↔ renderer agreement); a grammar
5
+ * runs it from one lint test.
6
+ *
7
+ * Implemented rules:
8
+ * - R1/R2 (schema-structure): delegates to the validator the input
9
+ * brings (`validateSchema`: the domains pass @lokascript/semantic's).
10
+ * - R5 (keyword-classification): profile keywords/markers classify as
11
+ * 'keyword' in the language's tokenizer.
12
+ * - R6 (keyword-coverage): every schema command has profile keywords for
13
+ * all declared languages.
14
+ * - R7 (marker-tokenization): every marker/keyword tokenizes as a single
15
+ * token.
16
+ * - R8 (extractor-coverage): Latin-script tokenizers handle diacritic
17
+ * identifiers (warning).
18
+ * - R9 (position-ordering): schema role positions are unique per
19
+ * word-order (warning).
20
+ * - R10 (renderer-coherence): renderer table keywords match
21
+ * profile/schema translations (opt-in via `renderer`).
22
+ */
23
+ import type { DomainLintInput, LintResult } from './types';
24
+ export type { DomainLintInput, LintResult, LintFinding, LintRule, LintWaiver, Severity, RendererTables, SchemaValidationFinding, SchemaValidator, } from './types';
25
+ export { formatResult, formatFinding } from './format';
26
+ /**
27
+ * Lint a domain against all enabled rules and return a structured result.
28
+ */
29
+ export declare function lintDomain(input: DomainLintInput): LintResult;
30
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/testing/gate/lint/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAGH,OAAO,KAAK,EAAE,eAAe,EAAe,UAAU,EAAc,MAAM,SAAS,CAAC;AAEpF,YAAY,EACV,eAAe,EACf,UAAU,EACV,WAAW,EACX,QAAQ,EACR,UAAU,EACV,QAAQ,EACR,cAAc,EACd,uBAAuB,EACvB,eAAe,GAChB,MAAM,SAAS,CAAC;AACjB,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAoBvD;;GAEG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,eAAe,GAAG,UAAU,CAY7D"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * R8: Latin-script tokenizers should extract diacritic identifiers as single tokens.
3
+ *
4
+ * The default `IdentifierExtractor` matches `[a-zA-Z0-9_]*` and stops at any
5
+ * diacritic. Words like `añadir` split into `["a", "ñadir"]` unless the
6
+ * tokenizer also registers `LatinExtendedIdentifierExtractor`. This rule
7
+ * probes the behavior by tokenizing a canonical diacritic-containing string
8
+ * for each language and warning if it doesn't come back as a single token.
9
+ *
10
+ * Warning (not error) because:
11
+ * - English tokenizers legitimately don't need the extended extractor.
12
+ * - A domain might deliberately split compounds for its own reasons.
13
+ * The warning prompts the author to audit; an explicit waiver can add an
14
+ * opt-out once we have a mechanism for it.
15
+ */
16
+ import type { LintRule } from '../types';
17
+ export declare const extractorCoverageRule: LintRule;
18
+ //# sourceMappingURL=extractor-coverage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extractor-coverage.d.ts","sourceRoot":"","sources":["../../../../../src/testing/gate/lint/rules/extractor-coverage.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAe,QAAQ,EAAE,MAAM,UAAU,CAAC;AAsBtD,eAAO,MAAM,qBAAqB,EAAE,QAwBnC,CAAC"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Rule registry — runs all enabled rules and aggregates findings.
3
+ */
4
+ import type { DomainLintInput, LintFinding, LintRule } from '../types';
5
+ /**
6
+ * All rules in execution order.
7
+ * Phase 1: schema structure (delegates to @lokascript/semantic)
8
+ * Phase 2: keyword coverage + keyword classification + marker tokenization
9
+ * Phase 3: extractor coverage + position ordering (warnings only)
10
+ * Phase 3b: renderer coherence (opt-in via DomainLintInput.renderer)
11
+ */
12
+ export declare const ALL_RULES: readonly LintRule[];
13
+ export declare function runRules(input: DomainLintInput): LintFinding[];
14
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/testing/gate/lint/rules/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AASvE;;;;;;GAMG;AACH,eAAO,MAAM,SAAS,EAAE,SAAS,QAAQ,EAQxC,CAAC;AAEF,wBAAgB,QAAQ,CAAC,KAAK,EAAE,eAAe,GAAG,WAAW,EAAE,CAM9D"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * R5: Keyword classification — profile keywords ⊆ tokenizer keyword lists.
3
+ *
4
+ * Every lexical item the parser must recognize as a keyword — profile
5
+ * keyword primaries/alternatives, profile role markers, and schema
6
+ * `markerOverride` words — must actually *classify* as `'keyword'` in that
7
+ * language's tokenizer. This is stricter than R7 (marker-tokenization):
8
+ * a word can tokenize as a single token yet come back as `'identifier'`
9
+ * when the tokenizer's keyword list doesn't contain it.
10
+ *
11
+ * Under the bridge (`buildDomainTokenizer`) profile and tokenizer derive
12
+ * from the same vocab file, so this rule is vacuously green there. It
13
+ * exists for hand-authored tokenizers (e.g. domain-learn) and custom
14
+ * tokenizer overrides, where a vocab/profile edit can silently go stale
15
+ * against the tokenizer's keyword list.
16
+ */
17
+ import type { LintRule } from '../types';
18
+ export declare const keywordClassificationRule: LintRule;
19
+ //# sourceMappingURL=keyword-classification.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keyword-classification.d.ts","sourceRoot":"","sources":["../../../../../src/testing/gate/lint/rules/keyword-classification.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EAAe,QAAQ,EAAE,MAAM,UAAU,CAAC;AAKtD,eAAO,MAAM,yBAAyB,EAAE,QA6BvC,CAAC"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * R6: Keyword coverage across profiles.
3
+ *
4
+ * For every schema action + every registered profile, the profile must have
5
+ * a `keywords[action]` entry. Without it, `createMultilingualDSL()` throws
6
+ * at pattern generation time ("No keyword translation for 'X' in 'Y'").
7
+ *
8
+ * This rule catches the "I added a new schema but only wired it into the
9
+ * English profile" class of drift.
10
+ */
11
+ import type { LintRule } from '../types';
12
+ export declare const keywordCoverageRule: LintRule;
13
+ //# sourceMappingURL=keyword-coverage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keyword-coverage.d.ts","sourceRoot":"","sources":["../../../../../src/testing/gate/lint/rules/keyword-coverage.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAe,QAAQ,EAAE,MAAM,UAAU,CAAC;AAItD,eAAO,MAAM,mBAAmB,EAAE,QAsBjC,CAAC"}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * R7: Marker and keyword tokenization.
3
+ *
4
+ * Every lexical item the parser needs to match atomically — marker words,
5
+ * profile keyword primaries, and profile keyword alternatives — must
6
+ * tokenize as a single token in its target language. A word that splits
7
+ * across multiple tokens (e.g., `añadir` → `["a", "ñadir"]` when the
8
+ * tokenizer lacks `LatinExtendedIdentifierExtractor`) will never match a
9
+ * pattern's literal token.
10
+ *
11
+ * This is the exact gap that bit us on the Spanish tokenizer mid-session.
12
+ */
13
+ import type { LintRule, DomainLintInput } from '../types';
14
+ export declare function collectLexicalItems(input: DomainLintInput): Array<{
15
+ lang: string;
16
+ word: string;
17
+ source: string;
18
+ }>;
19
+ export declare const markerTokenizationRule: LintRule;
20
+ //# sourceMappingURL=marker-tokenization.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"marker-tokenization.d.ts","sourceRoot":"","sources":["../../../../../src/testing/gate/lint/rules/marker-tokenization.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAe,QAAQ,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAUvE,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,eAAe,GAAG,KAAK,CAAC;IACjE,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC,CA0DD;AAED,eAAO,MAAM,sBAAsB,EAAE,QAyCpC,CAAC"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * R9: Role position uniqueness within a schema.
3
+ *
4
+ * `sortRolesByWordOrder` in the pattern generator sorts roles descending by
5
+ * svoPosition or sovPosition (higher value = earlier in surface form). If two
6
+ * roles share the same position value, their relative order is undefined
7
+ * (falls through to array stability) — a latent footgun because token
8
+ * ordering silently changes if someone reorders the roles array.
9
+ *
10
+ * This rule warns when a schema has multiple roles AND any two share an
11
+ * explicit position for the same word order.
12
+ *
13
+ * Warning (not error) because:
14
+ * - Single-role schemas have nothing to order.
15
+ * - A schema might intentionally tie two roles when only one is present
16
+ * at a time (e.g., mutually-exclusive alternatives). In practice we
17
+ * haven't seen this, but the rule should prompt a review, not block.
18
+ *
19
+ * The `sovPosition: 0` default assigned when omitted is NOT flagged — only
20
+ * explicit non-zero collisions count. Otherwise most schemas with 3+
21
+ * optional roles would warn spuriously.
22
+ */
23
+ import type { LintRule } from '../types';
24
+ export declare const positionOrderingRule: LintRule;
25
+ //# sourceMappingURL=position-ordering.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"position-ordering.d.ts","sourceRoot":"","sources":["../../../../../src/testing/gate/lint/rules/position-ordering.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,KAAK,EAAe,QAAQ,EAAE,MAAM,UAAU,CAAC;AAkBtD,eAAO,MAAM,oBAAoB,EAAE,QA6ClC,CAAC"}
@@ -0,0 +1,24 @@
1
+ /**
2
+ * R10: Renderer-table coherence (opt-in).
3
+ *
4
+ * Some domains ship a natural-language renderer that maintains its own
5
+ * per-language keyword/marker tables separate from the profile data. This
6
+ * rule — when the domain passes `input.renderer` — asserts that:
7
+ * - Every `commandKeywords[action][lang]` matches `profile.keywords[action].primary`
8
+ * for that language.
9
+ * - Every `markers[name][lang]` appears as a markerOverride value somewhere
10
+ * in the schemas for that language (can't pin to a specific role since
11
+ * renderer marker names are arbitrary labels).
12
+ *
13
+ * Warnings only. Domains without `input.renderer` get a no-op pass — this
14
+ * rule is inherently opt-in because not every domain has round-trip
15
+ * rendering and not every renderer uses the same table shape.
16
+ *
17
+ * Catches the exact drift we fixed earlier this session: domain-sql's
18
+ * Turkish source marker was `dan` in the `get` schema but `den` in the
19
+ * renderer's MARKERS table. Had this rule existed, it would have warned
20
+ * the moment `get` was added with the wrong Turkish variant.
21
+ */
22
+ import type { LintRule } from '../types';
23
+ export declare const rendererCoherenceRule: LintRule;
24
+ //# sourceMappingURL=renderer-coherence.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"renderer-coherence.d.ts","sourceRoot":"","sources":["../../../../../src/testing/gate/lint/rules/renderer-coherence.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,KAAK,EAAe,QAAQ,EAAE,MAAM,UAAU,CAAC;AAItD,eAAO,MAAM,qBAAqB,EAAE,QAyEnC,CAAC"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Schema-structure rules (R1, R2).
3
+ *
4
+ * Delegates to the validator the input brings (`DomainLintInput.validateSchema`): the domains pass
5
+ * `@lokascript/semantic`'s `validateCommandSchema`, whose checks are already solid. We only surface its
6
+ * error- and warning-level items; notes are dropped to keep linter output focused.
7
+ */
8
+ import type { LintRule } from '../types';
9
+ export declare const schemaStructureRule: LintRule;
10
+ //# sourceMappingURL=schema-rules.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema-rules.d.ts","sourceRoot":"","sources":["../../../../../src/testing/gate/lint/rules/schema-rules.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAe,QAAQ,EAAE,MAAM,UAAU,CAAC;AAItD,eAAO,MAAM,mBAAmB,EAAE,QAsBjC,CAAC"}
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Core types for the domain linter.
3
+ */
4
+ import type { CommandSchema } from '../../../schema';
5
+ import type { LanguageTokenizer } from '../../../core/types';
6
+ import type { PatternGenLanguageProfile } from '../../../generation/pattern-generator';
7
+ export type Severity = 'error' | 'warning' | 'note';
8
+ export interface LintFinding {
9
+ readonly rule: string;
10
+ readonly severity: Severity;
11
+ readonly message: string;
12
+ /** Optional structured context: domain, language code, command action, role name, etc. */
13
+ readonly context?: Record<string, string | number | boolean>;
14
+ }
15
+ export interface LintResult {
16
+ readonly domain: string;
17
+ readonly findings: readonly LintFinding[];
18
+ /** Count helpers — derived from findings. */
19
+ readonly errorCount: number;
20
+ readonly warningCount: number;
21
+ }
22
+ /**
23
+ * Optional renderer metadata for R10 (renderer-coherence check).
24
+ * Domains without a renderer or without standard table shape omit this field.
25
+ */
26
+ export interface RendererTables {
27
+ /** Keyed by action name → lang code → keyword. */
28
+ readonly commandKeywords: Record<string, Record<string, string>>;
29
+ /** Keyed by marker name → lang code → marker word. */
30
+ readonly markers: Record<string, Record<string, string>>;
31
+ }
32
+ /**
33
+ * Matcher for an acknowledged-but-unfixed finding. Each entry is an object
34
+ * whose fields are compared against the finding's rule + context. A finding
35
+ * matches a waiver if every field in the waiver matches the corresponding
36
+ * field on the finding (or finding.context). Matched findings are dropped
37
+ * from the returned result entirely.
38
+ *
39
+ * Use sparingly — waivers are real debt that should have a plan to resolve.
40
+ */
41
+ export interface LintWaiver {
42
+ readonly rule: string;
43
+ readonly reason: string;
44
+ readonly matches?: Record<string, string | number | boolean>;
45
+ }
46
+ export interface DomainLintInput {
47
+ readonly name: string;
48
+ readonly schemas: readonly CommandSchema[];
49
+ readonly profiles: readonly PatternGenLanguageProfile[];
50
+ readonly tokenizers: Readonly<Record<string, LanguageTokenizer>>;
51
+ readonly renderer?: RendererTables;
52
+ readonly waivers?: readonly LintWaiver[];
53
+ /**
54
+ * The schema validator R1/R2 (schema-structure) run. The domains pass `@lokascript/semantic`'s
55
+ * `validateCommandSchema`; framework cannot import semantic, which depends on it. Without one, the
56
+ * rule finds nothing.
57
+ */
58
+ readonly validateSchema?: SchemaValidator;
59
+ }
60
+ /** One finding of a schema validator. */
61
+ export interface SchemaValidationFinding {
62
+ readonly code: string;
63
+ readonly severity: Severity;
64
+ readonly message: string;
65
+ }
66
+ /** A schema validator: `@lokascript/semantic`'s `validateCommandSchema` has this shape. */
67
+ export type SchemaValidator = (schema: CommandSchema) => {
68
+ readonly items: readonly SchemaValidationFinding[];
69
+ };
70
+ /**
71
+ * A single rule is a pure function producing findings.
72
+ */
73
+ export type LintRule = (input: DomainLintInput) => LintFinding[];
74
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/testing/gate/lint/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AACrD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAC7D,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,uCAAuC,CAAC;AAEvF,MAAM,MAAM,QAAQ,GAAG,OAAO,GAAG,SAAS,GAAG,MAAM,CAAC;AAEpD,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC;IAC5B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,0FAA0F;IAC1F,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,CAAC;CAC9D;AAED,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,QAAQ,EAAE,SAAS,WAAW,EAAE,CAAC;IAC1C,6CAA6C;IAC7C,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;CAC/B;AAED;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC7B,kDAAkD;IAClD,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IACjE,sDAAsD;IACtD,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;CAC1D;AAED;;;;;;;;GAQG;AACH,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,CAAC;CAC9D;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,SAAS,aAAa,EAAE,CAAC;IAC3C,QAAQ,CAAC,QAAQ,EAAE,SAAS,yBAAyB,EAAE,CAAC;IACxD,QAAQ,CAAC,UAAU,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC,CAAC;IACjE,QAAQ,CAAC,QAAQ,CAAC,EAAE,cAAc,CAAC;IACnC,QAAQ,CAAC,OAAO,CAAC,EAAE,SAAS,UAAU,EAAE,CAAC;IACzC;;;;OAIG;IACH,QAAQ,CAAC,cAAc,CAAC,EAAE,eAAe,CAAC;CAC3C;AAED,yCAAyC;AACzC,MAAM,WAAW,uBAAuB;IACtC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC;IAC5B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAED,2FAA2F;AAC3F,MAAM,MAAM,eAAe,GAAG,CAAC,MAAM,EAAE,aAAa,KAAK;IACvD,QAAQ,CAAC,KAAK,EAAE,SAAS,uBAAuB,EAAE,CAAC;CACpD,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,QAAQ,GAAG,CAAC,KAAK,EAAE,eAAe,KAAK,WAAW,EAAE,CAAC"}