@ai-matrx/records 0.76.10 → 0.76.11

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.
@@ -823,7 +823,7 @@ declare function reusesOf(plan: DescribePlan): Array<{
823
823
  * S5 step 1 and assembly. `doors` and `views` may be missing (a failed step): the views fall back to the default, the
824
824
  * forms stay empty (a missing front door is for the check to say, never for code to invent).
825
825
  */
826
- declare function mergeDescribeParts(plan: DescribePlan, tables: TablesPart, doors: DoorsPart | null | undefined, views: ViewsPart | null | undefined, existingTables?: ExistingTable[]): TemplateSpec;
826
+ declare function mergeDescribeParts(plan: DescribePlan, tables: TablesPart, doors: DoorsPart | null | undefined, views: ViewsPart | null | undefined, existingTables?: ExistingTable[], notes?: string[]): TemplateSpec;
827
827
 
828
828
  interface AutofixResult {
829
829
  spec: TemplateSpec;
@@ -915,4 +915,17 @@ declare const VIEW_SHAPE_FACTS: {
915
915
  /** The store's closed lists, as the mandate's `vocabulary` value — read from the package, never typed by a caller. */
916
916
  declare function describeVocabulary(): Record<string, unknown>;
917
917
 
918
- export { AgentSnapshotRow, type AggregateOutcome, type ApplyResult, type AutofixResult, BAD_OUTCOME, BOOKING_AVAILABILITY_FACTS, type CapabilityStatus, CapabilityWord, DESIGNATED_REAL_NUMBERS, type DerivedUseCase, type DescribeCheckResult, type DescribeCtx, type DescribePlan, type DescribePlanDoor, type DescribePlanTable, type DescribeProblem, type DescribeResult, type DoorsPart, type ExistingTable, FORMULA_FUNCTIONS, type FieldRequest, GALLERY_ONLY, GOOD_OUTCOME, INSTALL_PLAN_VERSION, IndustryGroup, MAIN_TABLE_ROWS_MAX, MAIN_TABLE_ROWS_MIN, PUBLIC_MAIL_DOMAINS, RECORDS_UI_CALENDAR_MODES, RECORDS_UI_FIELD_SURFACES, RECORDS_UI_VIEW_LAYOUTS, type RepairAnswer, type RepairPatch, type RepairSlice, SERVER_DATE_GRAMMAR, type SeedRow, SharedBlockId, TABLES_MAX, TABLES_MIN, TEMPLATE_PLAN_DOORS, TIME_BOUND_STAGE, TITLE_DISTINCT_SHARE, type TablesPart, TeachesFeature, TemplateAgent, type TemplateAgentBinding, type TemplateAgentCopyRequest, type TemplateAssetRow, type TemplateAttachment, TemplateAutomation, TemplateBooking, TemplateBookingWindow, type TemplateCard, TemplateChecklist, TemplateDashboard, type TemplateDeclaration, TemplateDigest, TemplateDimensions, TemplateDocument, type TemplateDoorAnswer, type TemplateDoorRun, TemplateDrillDown, type TemplateFamilyDoor, TemplateField, TemplateFootprint, TemplateForm, type TemplateHostExtras, type TemplateInstallPlan, TemplateJob, type TemplateMadeKind, TemplateMergeBinding, TemplateNotification, type TemplatePlanAgent, type TemplatePlanBinding, type TemplatePlanDoor, type TemplatePlanExtraAgent, type TemplatePlanStep, TemplatePortal, TemplateRowAction, TemplateSpec, type TemplateSpecWithAttachments, TemplateStageRules, type TemplateStatus, TemplateTable, TemplateTryIt, type TemplateUpgradeAnswer, type TemplateUpgradeHint, type TemplateUpgradeRun, type TemplateVerdict, TemplateView, TemplateViewKind, TemplateWorkflow, type TrimResult, VIEW_SHAPE_FACTS, type ValidateOptions, type ViewsPart, agentCopyRequest, allTables, anyOfWhere, applyRepairPatches, assertTemplate, attachmentProblems, autofixDescribeSpec, automationHeld, blockTable, bookingArgs, bookingColumnsProblem, capabilitiesUsed, capabilityStatus, checklistSpec, choiceDisplayFormat, compileHostExtras, compileInstallPlan, dashboardBlocks, deferredRelationFields, deferredRelations, derivedFieldSpec, describeCheck, describeSpec, describeVocabulary, digestSpec, documentBody, domainsIn, drillDownDashboard, evalFormula, extrasProblems, fallbackView, foreignTextProblems, formArgs, formulaFieldSpec, formulaOrder, formulaRefs, foundationTokens, isSelfDeclaredField, isTimedField, lastResortTrim, mergeDescribeParts, normalizeForPhones, notificationParts, numberText, numberedStreet, packageDescribeResult, parseAt, parseFormula, parseNumeric, pathOfAt, peopleOf, phonesOutsideFictionalRange, planTables, portalArgs, relationFieldSpec, relationPatches, relativeHasTime, repairSlices, requiredCircleProblem, resolveDateToken, resolvePath, resolveRelativeDates, reusesOf, rowActionList, rowLevels, rowsFor, ruleBreak, runTemplateDoor, selfDeclaredFieldSpec, sensitivityFloorOf, shellsPayload, stageFieldOf, stageRulesSpec, storeAggregate, storeFilterMatches, storeText, tableShellPayload, templateCard, templateDeclaration, templateText, templateUpgradeHint, timedFieldSpec, toUseCase, unwrap, upgradeTemplateInstall, useCaseProblemsOf, validateTemplate, viewMatches, viewSpec, zoneOffset };
918
+ /** The content words of a key or label, stemmed, as a sorted unique list. */
919
+ declare function meaningWords(text: string): string[];
920
+ /** Two fields of one table mean the same when their kinds agree and their key words, or label words, are the same set. */
921
+ declare function sameMeaning(a: {
922
+ key: string;
923
+ label?: string;
924
+ parityType: string;
925
+ }, b: {
926
+ key: string;
927
+ label?: string;
928
+ parityType: string;
929
+ }): boolean;
930
+
931
+ export { AgentSnapshotRow, type AggregateOutcome, type ApplyResult, type AutofixResult, BAD_OUTCOME, BOOKING_AVAILABILITY_FACTS, type CapabilityStatus, CapabilityWord, DESIGNATED_REAL_NUMBERS, type DerivedUseCase, type DescribeCheckResult, type DescribeCtx, type DescribePlan, type DescribePlanDoor, type DescribePlanTable, type DescribeProblem, type DescribeResult, type DoorsPart, type ExistingTable, FORMULA_FUNCTIONS, type FieldRequest, GALLERY_ONLY, GOOD_OUTCOME, INSTALL_PLAN_VERSION, IndustryGroup, MAIN_TABLE_ROWS_MAX, MAIN_TABLE_ROWS_MIN, PUBLIC_MAIL_DOMAINS, RECORDS_UI_CALENDAR_MODES, RECORDS_UI_FIELD_SURFACES, RECORDS_UI_VIEW_LAYOUTS, type RepairAnswer, type RepairPatch, type RepairSlice, SERVER_DATE_GRAMMAR, type SeedRow, SharedBlockId, TABLES_MAX, TABLES_MIN, TEMPLATE_PLAN_DOORS, TIME_BOUND_STAGE, TITLE_DISTINCT_SHARE, type TablesPart, TeachesFeature, TemplateAgent, type TemplateAgentBinding, type TemplateAgentCopyRequest, type TemplateAssetRow, type TemplateAttachment, TemplateAutomation, TemplateBooking, TemplateBookingWindow, type TemplateCard, TemplateChecklist, TemplateDashboard, type TemplateDeclaration, TemplateDigest, TemplateDimensions, TemplateDocument, type TemplateDoorAnswer, type TemplateDoorRun, TemplateDrillDown, type TemplateFamilyDoor, TemplateField, TemplateFootprint, TemplateForm, type TemplateHostExtras, type TemplateInstallPlan, TemplateJob, type TemplateMadeKind, TemplateMergeBinding, TemplateNotification, type TemplatePlanAgent, type TemplatePlanBinding, type TemplatePlanDoor, type TemplatePlanExtraAgent, type TemplatePlanStep, TemplatePortal, TemplateRowAction, TemplateSpec, type TemplateSpecWithAttachments, TemplateStageRules, type TemplateStatus, TemplateTable, TemplateTryIt, type TemplateUpgradeAnswer, type TemplateUpgradeHint, type TemplateUpgradeRun, type TemplateVerdict, TemplateView, TemplateViewKind, TemplateWorkflow, type TrimResult, VIEW_SHAPE_FACTS, type ValidateOptions, type ViewsPart, agentCopyRequest, allTables, anyOfWhere, applyRepairPatches, assertTemplate, attachmentProblems, autofixDescribeSpec, automationHeld, blockTable, bookingArgs, bookingColumnsProblem, capabilitiesUsed, capabilityStatus, checklistSpec, choiceDisplayFormat, compileHostExtras, compileInstallPlan, dashboardBlocks, deferredRelationFields, deferredRelations, derivedFieldSpec, describeCheck, describeSpec, describeVocabulary, digestSpec, documentBody, domainsIn, drillDownDashboard, evalFormula, extrasProblems, fallbackView, foreignTextProblems, formArgs, formulaFieldSpec, formulaOrder, formulaRefs, foundationTokens, isSelfDeclaredField, isTimedField, lastResortTrim, meaningWords, mergeDescribeParts, normalizeForPhones, notificationParts, numberText, numberedStreet, packageDescribeResult, parseAt, parseFormula, parseNumeric, pathOfAt, peopleOf, phonesOutsideFictionalRange, planTables, portalArgs, relationFieldSpec, relationPatches, relativeHasTime, repairSlices, requiredCircleProblem, resolveDateToken, resolvePath, resolveRelativeDates, reusesOf, rowActionList, rowLevels, rowsFor, ruleBreak, runTemplateDoor, sameMeaning, selfDeclaredFieldSpec, sensitivityFloorOf, shellsPayload, stageFieldOf, stageRulesSpec, storeAggregate, storeFilterMatches, storeText, tableShellPayload, templateCard, templateDeclaration, templateText, templateUpgradeHint, timedFieldSpec, toUseCase, unwrap, upgradeTemplateInstall, useCaseProblemsOf, validateTemplate, viewMatches, viewSpec, zoneOffset };
@@ -823,7 +823,7 @@ declare function reusesOf(plan: DescribePlan): Array<{
823
823
  * S5 step 1 and assembly. `doors` and `views` may be missing (a failed step): the views fall back to the default, the
824
824
  * forms stay empty (a missing front door is for the check to say, never for code to invent).
825
825
  */
826
- declare function mergeDescribeParts(plan: DescribePlan, tables: TablesPart, doors: DoorsPart | null | undefined, views: ViewsPart | null | undefined, existingTables?: ExistingTable[]): TemplateSpec;
826
+ declare function mergeDescribeParts(plan: DescribePlan, tables: TablesPart, doors: DoorsPart | null | undefined, views: ViewsPart | null | undefined, existingTables?: ExistingTable[], notes?: string[]): TemplateSpec;
827
827
 
828
828
  interface AutofixResult {
829
829
  spec: TemplateSpec;
@@ -915,4 +915,17 @@ declare const VIEW_SHAPE_FACTS: {
915
915
  /** The store's closed lists, as the mandate's `vocabulary` value — read from the package, never typed by a caller. */
916
916
  declare function describeVocabulary(): Record<string, unknown>;
917
917
 
918
- export { AgentSnapshotRow, type AggregateOutcome, type ApplyResult, type AutofixResult, BAD_OUTCOME, BOOKING_AVAILABILITY_FACTS, type CapabilityStatus, CapabilityWord, DESIGNATED_REAL_NUMBERS, type DerivedUseCase, type DescribeCheckResult, type DescribeCtx, type DescribePlan, type DescribePlanDoor, type DescribePlanTable, type DescribeProblem, type DescribeResult, type DoorsPart, type ExistingTable, FORMULA_FUNCTIONS, type FieldRequest, GALLERY_ONLY, GOOD_OUTCOME, INSTALL_PLAN_VERSION, IndustryGroup, MAIN_TABLE_ROWS_MAX, MAIN_TABLE_ROWS_MIN, PUBLIC_MAIL_DOMAINS, RECORDS_UI_CALENDAR_MODES, RECORDS_UI_FIELD_SURFACES, RECORDS_UI_VIEW_LAYOUTS, type RepairAnswer, type RepairPatch, type RepairSlice, SERVER_DATE_GRAMMAR, type SeedRow, SharedBlockId, TABLES_MAX, TABLES_MIN, TEMPLATE_PLAN_DOORS, TIME_BOUND_STAGE, TITLE_DISTINCT_SHARE, type TablesPart, TeachesFeature, TemplateAgent, type TemplateAgentBinding, type TemplateAgentCopyRequest, type TemplateAssetRow, type TemplateAttachment, TemplateAutomation, TemplateBooking, TemplateBookingWindow, type TemplateCard, TemplateChecklist, TemplateDashboard, type TemplateDeclaration, TemplateDigest, TemplateDimensions, TemplateDocument, type TemplateDoorAnswer, type TemplateDoorRun, TemplateDrillDown, type TemplateFamilyDoor, TemplateField, TemplateFootprint, TemplateForm, type TemplateHostExtras, type TemplateInstallPlan, TemplateJob, type TemplateMadeKind, TemplateMergeBinding, TemplateNotification, type TemplatePlanAgent, type TemplatePlanBinding, type TemplatePlanDoor, type TemplatePlanExtraAgent, type TemplatePlanStep, TemplatePortal, TemplateRowAction, TemplateSpec, type TemplateSpecWithAttachments, TemplateStageRules, type TemplateStatus, TemplateTable, TemplateTryIt, type TemplateUpgradeAnswer, type TemplateUpgradeHint, type TemplateUpgradeRun, type TemplateVerdict, TemplateView, TemplateViewKind, TemplateWorkflow, type TrimResult, VIEW_SHAPE_FACTS, type ValidateOptions, type ViewsPart, agentCopyRequest, allTables, anyOfWhere, applyRepairPatches, assertTemplate, attachmentProblems, autofixDescribeSpec, automationHeld, blockTable, bookingArgs, bookingColumnsProblem, capabilitiesUsed, capabilityStatus, checklistSpec, choiceDisplayFormat, compileHostExtras, compileInstallPlan, dashboardBlocks, deferredRelationFields, deferredRelations, derivedFieldSpec, describeCheck, describeSpec, describeVocabulary, digestSpec, documentBody, domainsIn, drillDownDashboard, evalFormula, extrasProblems, fallbackView, foreignTextProblems, formArgs, formulaFieldSpec, formulaOrder, formulaRefs, foundationTokens, isSelfDeclaredField, isTimedField, lastResortTrim, mergeDescribeParts, normalizeForPhones, notificationParts, numberText, numberedStreet, packageDescribeResult, parseAt, parseFormula, parseNumeric, pathOfAt, peopleOf, phonesOutsideFictionalRange, planTables, portalArgs, relationFieldSpec, relationPatches, relativeHasTime, repairSlices, requiredCircleProblem, resolveDateToken, resolvePath, resolveRelativeDates, reusesOf, rowActionList, rowLevels, rowsFor, ruleBreak, runTemplateDoor, selfDeclaredFieldSpec, sensitivityFloorOf, shellsPayload, stageFieldOf, stageRulesSpec, storeAggregate, storeFilterMatches, storeText, tableShellPayload, templateCard, templateDeclaration, templateText, templateUpgradeHint, timedFieldSpec, toUseCase, unwrap, upgradeTemplateInstall, useCaseProblemsOf, validateTemplate, viewMatches, viewSpec, zoneOffset };
918
+ /** The content words of a key or label, stemmed, as a sorted unique list. */
919
+ declare function meaningWords(text: string): string[];
920
+ /** Two fields of one table mean the same when their kinds agree and their key words, or label words, are the same set. */
921
+ declare function sameMeaning(a: {
922
+ key: string;
923
+ label?: string;
924
+ parityType: string;
925
+ }, b: {
926
+ key: string;
927
+ label?: string;
928
+ parityType: string;
929
+ }): boolean;
930
+
931
+ export { AgentSnapshotRow, type AggregateOutcome, type ApplyResult, type AutofixResult, BAD_OUTCOME, BOOKING_AVAILABILITY_FACTS, type CapabilityStatus, CapabilityWord, DESIGNATED_REAL_NUMBERS, type DerivedUseCase, type DescribeCheckResult, type DescribeCtx, type DescribePlan, type DescribePlanDoor, type DescribePlanTable, type DescribeProblem, type DescribeResult, type DoorsPart, type ExistingTable, FORMULA_FUNCTIONS, type FieldRequest, GALLERY_ONLY, GOOD_OUTCOME, INSTALL_PLAN_VERSION, IndustryGroup, MAIN_TABLE_ROWS_MAX, MAIN_TABLE_ROWS_MIN, PUBLIC_MAIL_DOMAINS, RECORDS_UI_CALENDAR_MODES, RECORDS_UI_FIELD_SURFACES, RECORDS_UI_VIEW_LAYOUTS, type RepairAnswer, type RepairPatch, type RepairSlice, SERVER_DATE_GRAMMAR, type SeedRow, SharedBlockId, TABLES_MAX, TABLES_MIN, TEMPLATE_PLAN_DOORS, TIME_BOUND_STAGE, TITLE_DISTINCT_SHARE, type TablesPart, TeachesFeature, TemplateAgent, type TemplateAgentBinding, type TemplateAgentCopyRequest, type TemplateAssetRow, type TemplateAttachment, TemplateAutomation, TemplateBooking, TemplateBookingWindow, type TemplateCard, TemplateChecklist, TemplateDashboard, type TemplateDeclaration, TemplateDigest, TemplateDimensions, TemplateDocument, type TemplateDoorAnswer, type TemplateDoorRun, TemplateDrillDown, type TemplateFamilyDoor, TemplateField, TemplateFootprint, TemplateForm, type TemplateHostExtras, type TemplateInstallPlan, TemplateJob, type TemplateMadeKind, TemplateMergeBinding, TemplateNotification, type TemplatePlanAgent, type TemplatePlanBinding, type TemplatePlanDoor, type TemplatePlanExtraAgent, type TemplatePlanStep, TemplatePortal, TemplateRowAction, TemplateSpec, type TemplateSpecWithAttachments, TemplateStageRules, type TemplateStatus, TemplateTable, TemplateTryIt, type TemplateUpgradeAnswer, type TemplateUpgradeHint, type TemplateUpgradeRun, type TemplateVerdict, TemplateView, TemplateViewKind, TemplateWorkflow, type TrimResult, VIEW_SHAPE_FACTS, type ValidateOptions, type ViewsPart, agentCopyRequest, allTables, anyOfWhere, applyRepairPatches, assertTemplate, attachmentProblems, autofixDescribeSpec, automationHeld, blockTable, bookingArgs, bookingColumnsProblem, capabilitiesUsed, capabilityStatus, checklistSpec, choiceDisplayFormat, compileHostExtras, compileInstallPlan, dashboardBlocks, deferredRelationFields, deferredRelations, derivedFieldSpec, describeCheck, describeSpec, describeVocabulary, digestSpec, documentBody, domainsIn, drillDownDashboard, evalFormula, extrasProblems, fallbackView, foreignTextProblems, formArgs, formulaFieldSpec, formulaOrder, formulaRefs, foundationTokens, isSelfDeclaredField, isTimedField, lastResortTrim, meaningWords, mergeDescribeParts, normalizeForPhones, notificationParts, numberText, numberedStreet, packageDescribeResult, parseAt, parseFormula, parseNumeric, pathOfAt, peopleOf, phonesOutsideFictionalRange, planTables, portalArgs, relationFieldSpec, relationPatches, relativeHasTime, repairSlices, requiredCircleProblem, resolveDateToken, resolvePath, resolveRelativeDates, reusesOf, rowActionList, rowLevels, rowsFor, ruleBreak, runTemplateDoor, sameMeaning, selfDeclaredFieldSpec, sensitivityFloorOf, shellsPayload, stageFieldOf, stageRulesSpec, storeAggregate, storeFilterMatches, storeText, tableShellPayload, templateCard, templateDeclaration, templateText, templateUpgradeHint, timedFieldSpec, toUseCase, unwrap, upgradeTemplateInstall, useCaseProblemsOf, validateTemplate, viewMatches, viewSpec, zoneOffset };
@@ -4366,8 +4366,8 @@ function validateTemplate(raw, options = {}) {
4366
4366
  for (const k of rule.filled ?? []) if (!filled(val(k))) say(at, `"${row.key}" is ${st}, so "${fieldOf(table, k)?.label ?? k}" is filled; it is empty.`);
4367
4367
  for (const [k, v] of Object.entries(rule.equals ?? {})) {
4368
4368
  const got = val(k) ?? null;
4369
- const same = sameJson(got, v) || typeof v === "number" && parseNumeric(storeText(got) ?? "") !== null && qCmp(parseNumeric(storeText(got) ?? ""), parseNumeric(String(v))) === 0;
4370
- if (!same) say(at, `"${row.key}" is ${st}, so "${fieldOf(table, k)?.label ?? k}" is ${JSON.stringify(v)}; it is ${JSON.stringify(got)}.`);
4369
+ const same2 = sameJson(got, v) || typeof v === "number" && parseNumeric(storeText(got) ?? "") !== null && qCmp(parseNumeric(storeText(got) ?? ""), parseNumeric(String(v))) === 0;
4370
+ if (!same2) say(at, `"${row.key}" is ${st}, so "${fieldOf(table, k)?.label ?? k}" is ${JSON.stringify(v)}; it is ${JSON.stringify(got)}.`);
4371
4371
  }
4372
4372
  }
4373
4373
  };
@@ -5704,7 +5704,7 @@ ${e.body}`.matchAll(MERGE_TOKEN)].map((m) => m[1]));
5704
5704
  }
5705
5705
  }
5706
5706
  const rows = t.rows;
5707
- const same = (x, y) => {
5707
+ const same2 = (x, y) => {
5708
5708
  const a = storeText(x);
5709
5709
  const b = storeText(y);
5710
5710
  if (a === null || b === null) return false;
@@ -5722,7 +5722,7 @@ ${e.body}`.matchAll(MERGE_TOKEN)].map((m) => m[1]));
5722
5722
  };
5723
5723
  for (const sf of t.fields.filter((ff) => !isComputed(ff) && !isRelation(ff) && ff.key !== l.key)) {
5724
5724
  const have = rows.filter((r) => filled(r.values?.[sf.key]));
5725
- if (have.length >= 3 && have.every((r) => same(r.values?.[sf.key], far(r)))) {
5725
+ if (have.length >= 3 && have.every((r) => same2(r.values?.[sf.key], far(r)))) {
5726
5726
  say(`tables (${t.token}).fields (${sf.key})`, `"${sf.label}" repeats "${l.label}" on every row; drop it and use the lookup, which follows the linked record.`);
5727
5727
  }
5728
5728
  }
@@ -6407,6 +6407,33 @@ function describeField(f, table) {
6407
6407
  };
6408
6408
  }
6409
6409
 
6410
+ // src/templates/describe/meaning.ts
6411
+ var FILLER = /* @__PURE__ */ new Set(["of", "the", "a", "an", "for", "to", "in", "on", "and", "or", "your", "their", "is", "are", "was", "when", "what", "which", "with", "my", "our"]);
6412
+ function stemWord(w) {
6413
+ let s = w.toLowerCase().replace(/[^a-z0-9]/g, "");
6414
+ if (s.length > 4 && s.endsWith("ies")) s = s.slice(0, -3) + "y";
6415
+ else if (s.length > 4 && s.endsWith("es") && /(ch|sh|x|ss|s)es$/.test(s)) s = s.slice(0, -2);
6416
+ else if (s.length > 3 && s.endsWith("s") && !s.endsWith("ss")) s = s.slice(0, -1);
6417
+ if (s.length > 6 && s.endsWith("ation")) s = s.slice(0, -5);
6418
+ else if (s.length > 6 && s.endsWith("ion")) s = s.slice(0, -3);
6419
+ if (s.length > 4 && (s.endsWith("y") || s.endsWith("e"))) s = s.slice(0, -1);
6420
+ return s;
6421
+ }
6422
+ function meaningWords(text) {
6423
+ const words = text.replace(/([a-z])([A-Z])/g, "$1 $2").split(/[^A-Za-z0-9]+/).filter(Boolean).map((w) => w.toLowerCase()).filter((w) => !FILLER.has(w)).map(stemWord).filter(Boolean);
6424
+ return [...new Set(words)].sort();
6425
+ }
6426
+ var same = (a, b) => a.length > 0 && a.length === b.length && a.every((w, i) => w === b[i]);
6427
+ function sameMeaning(a, b) {
6428
+ if (a.parityType !== b.parityType) return false;
6429
+ const ka = meaningWords(a.key);
6430
+ const kb = meaningWords(b.key);
6431
+ if (same(ka, kb)) return true;
6432
+ const la = a.label ? meaningWords(a.label) : [];
6433
+ const lb = b.label ? meaningWords(b.label) : [];
6434
+ return same(la, lb);
6435
+ }
6436
+
6410
6437
  // src/templates/describe/merge.ts
6411
6438
  var clone = (v) => v === void 0 ? v : JSON.parse(JSON.stringify(v));
6412
6439
  var labelOfKey = (key) => {
@@ -6439,7 +6466,7 @@ function ensureSkeleton(t, p) {
6439
6466
  t.fields.push({ key: "status", label: "Status", parityType: "select", required: true, choices: [...p.status.choices], default: p.status.default });
6440
6467
  }
6441
6468
  if (p.slot && !field(t, "slot")) t.fields.push({ key: "slot", label: "Visit time", parityType: "datetime", format: "datetime", required: true });
6442
- for (const l of p.links) {
6469
+ for (const l of p.links ?? []) {
6443
6470
  if (field(t, l.key)) continue;
6444
6471
  t.fields.push({ key: l.key, label: l.label, parityType: "relation", relationTarget: l.target, ...l.many ? { multi: true } : { relationMax: 1 } });
6445
6472
  }
@@ -6448,7 +6475,7 @@ function addExistingKeys(t, p, existing) {
6448
6475
  if (!p.reuse) return;
6449
6476
  const e = existing.find((x) => x.id === p.reuse.existing_table_id);
6450
6477
  if (!e) return;
6451
- for (const key of p.reuse.keepKeys) {
6478
+ for (const key of p.reuse.keepKeys ?? []) {
6452
6479
  if (field(t, key)) continue;
6453
6480
  const ef = e.fields.find((f) => f.key === key);
6454
6481
  if (!ef) continue;
@@ -6456,7 +6483,9 @@ function addExistingKeys(t, p, existing) {
6456
6483
  }
6457
6484
  }
6458
6485
  function addRequest(t, r) {
6459
- if (field(t, r.key)) return;
6486
+ if (field(t, r.key)) return void 0;
6487
+ const twin = t.fields.find((f2) => sameMeaning(f2, r));
6488
+ if (twin) return twin.key;
6460
6489
  const f = { key: r.key, label: r.label, parityType: r.parityType };
6461
6490
  if (r.choices?.length) f.choices = [...r.choices];
6462
6491
  if (r.askOfSubject) f.askOfSubject = true;
@@ -6464,6 +6493,7 @@ function addRequest(t, r) {
6464
6493
  if (r.required) f.required = true;
6465
6494
  if (r.help) f.help = r.help;
6466
6495
  t.fields.push(f);
6496
+ return void 0;
6467
6497
  }
6468
6498
  var CHOICE = ["select", "multi_select", "list"];
6469
6499
  var STAGE_KEY = /(^|_)(status|stage)$/;
@@ -6525,7 +6555,7 @@ function sharedBlocksFor(tables) {
6525
6555
  function reusesOf(plan) {
6526
6556
  return plan.tables.filter((t) => t.reuse).map((t) => ({ token: t.token, existing_table_id: t.reuse.existing_table_id }));
6527
6557
  }
6528
- function mergeDescribeParts(plan, tables, doors, views, existingTables = []) {
6558
+ function mergeDescribeParts(plan, tables, doors, views, existingTables = [], notes = []) {
6529
6559
  const out = clone(Array.isArray(tables?.tables) ? tables.tables : []);
6530
6560
  const byToken = new Map(out.map((t) => [t.token, t]));
6531
6561
  for (const p of plan.tables) {
@@ -6538,10 +6568,25 @@ function mergeDescribeParts(plan, tables, doors, views, existingTables = []) {
6538
6568
  ensureSkeleton(t, p);
6539
6569
  addExistingKeys(t, p, existingTables);
6540
6570
  }
6571
+ const repoint = /* @__PURE__ */ new Map();
6541
6572
  for (const r of doors?.fieldRequests ?? []) {
6542
6573
  const t = byToken.get(r.table);
6543
- if (t) addRequest(t, r);
6544
- }
6574
+ if (!t) continue;
6575
+ const twin = addRequest(t, r);
6576
+ if (!twin) continue;
6577
+ if (!repoint.has(r.table)) repoint.set(r.table, /* @__PURE__ */ new Map());
6578
+ repoint.get(r.table).set(r.key, twin);
6579
+ notes.push(`tables.${r.table}.fields.${r.key}: dropped, it means the same as "${twin}" (questions now ask "${twin}")`);
6580
+ }
6581
+ const forms = clone(doors?.forms ?? []);
6582
+ const bookingOut = doors?.booking ? clone(doors.booking) : void 0;
6583
+ const repointQuestions = (table, qs) => {
6584
+ const m = repoint.get(table);
6585
+ if (!m || !Array.isArray(qs)) return;
6586
+ for (const q2 of qs) if (m.has(q2.fieldKey)) q2.fieldKey = m.get(q2.fieldKey);
6587
+ };
6588
+ for (const f of forms) repointQuestions(f.table, f.fields);
6589
+ if (bookingOut) repointQuestions(bookingOut.table, bookingOut.questions);
6545
6590
  const spec = {
6546
6591
  specVersion: 1,
6547
6592
  version: 1,
@@ -6558,9 +6603,9 @@ function mergeDescribeParts(plan, tables, doors, views, existingTables = []) {
6558
6603
  sharedBlocks: sharedBlocksFor(out),
6559
6604
  relationships: clone(tables?.relationships ?? []),
6560
6605
  views: mergeViews(plan, views, byToken),
6561
- forms: clone(doors?.forms ?? []),
6606
+ forms,
6562
6607
  dimensions: [],
6563
- extras: doors?.booking ? [clone(doors.booking)] : []
6608
+ extras: bookingOut ? [bookingOut] : []
6564
6609
  };
6565
6610
  return spec;
6566
6611
  }
@@ -6576,6 +6621,62 @@ var GOOD_ORDER = ["green", "teal", "blue", "violet", "slate", "amber", "orange",
6576
6621
  var BAD_ORDER = ["red", "orange", "gray", "slate", "brown", "pink", "amber", "yellow", "violet", "blue", "teal"];
6577
6622
  var DISTINCT_UP_TO = 7;
6578
6623
  var lower = (s) => s.trim().toLowerCase();
6624
+ function linkedTables(spec, token) {
6625
+ const out = [];
6626
+ const add = (x) => {
6627
+ const t = spec.tables.find((y) => y.token === x);
6628
+ if (t && t.token !== token && !out.includes(t)) out.push(t);
6629
+ };
6630
+ const own = spec.tables.find((t) => t.token === token);
6631
+ for (const f of own?.fields ?? []) if (f.parityType === "relation" && f.relationTarget) add(f.relationTarget);
6632
+ for (const t of spec.tables) if (t.fields.some((f) => f.parityType === "relation" && f.relationTarget === token)) add(t.token);
6633
+ for (const r of spec.relationships ?? []) {
6634
+ if (r.fromTable === token) add(r.toTable);
6635
+ if (r.toTable === token) add(r.fromTable);
6636
+ }
6637
+ return out;
6638
+ }
6639
+ function fixMissingQuestionFields(spec, fixes) {
6640
+ const doors = [];
6641
+ for (const form of spec.forms) doors.push({ where: `forms.${form.token}`, table: form.table, questions: form.fields ?? [], set: (q2) => form.fields = q2 });
6642
+ for (const e of spec.extras ?? []) {
6643
+ if (e.kind !== "booking") continue;
6644
+ const b = e;
6645
+ doors.push({ where: `extras.${b.token}`, table: b.table, questions: b.questions ?? [], set: (q2) => b.questions = q2 });
6646
+ }
6647
+ for (const d of doors) {
6648
+ const t = spec.tables.find((x) => x.token === d.table);
6649
+ if (!t) continue;
6650
+ const kept = [];
6651
+ for (const q2 of d.questions) {
6652
+ if (t.fields.some((f) => f.key === q2.fieldKey)) {
6653
+ kept.push(q2);
6654
+ continue;
6655
+ }
6656
+ const source = linkedTables(spec, t.token).find((o) => o.fields.some((f) => f.key === q2.fieldKey));
6657
+ if (source) {
6658
+ const copy = clone2(source.fields.find((f) => f.key === q2.fieldKey));
6659
+ delete copy.required;
6660
+ delete copy.unique;
6661
+ const isDate2 = copy.parityType === "datetime";
6662
+ if (isDate2 && !copy.absoluteDates && /birth|dob|born/i.test(`${copy.key} ${copy.label}`)) {
6663
+ copy.absoluteDates = true;
6664
+ copy.historicalReason = copy.historicalReason?.trim() || "date of birth";
6665
+ }
6666
+ const hasSchedule = t.fields.some((f) => f.parityType === "datetime" && !f.absoluteDates);
6667
+ t.fields.push(copy);
6668
+ if (isDate2 && !copy.absoluteDates && hasSchedule && !t.before?.length && !t.datesIndependent?.trim()) {
6669
+ t.datesIndependent = `"${copy.label}" is the linked ${source.token}'s own date; it does not depend on the table's other dates.`;
6670
+ }
6671
+ fixes.push(`tables.${t.token}.fields.${q2.fieldKey}: copied from linked table "${source.token}" (${d.where} asks it)`);
6672
+ kept.push(q2);
6673
+ } else if (!q2.required) {
6674
+ fixes.push(`${d.where}.fields.${q2.fieldKey}: question dropped ("${t.token}" declares no such field and no linked table has it)`);
6675
+ } else kept.push(q2);
6676
+ }
6677
+ if (kept.length !== d.questions.length) d.set(kept);
6678
+ }
6679
+ }
6579
6680
  function raiseSensitivity(spec, fixes) {
6580
6681
  for (const t of spec.tables) {
6581
6682
  for (const f of t.fields) {
@@ -6830,6 +6931,7 @@ function switchJob(spec, fixes) {
6830
6931
  function autofixDescribeSpec(input, ctx = {}) {
6831
6932
  const spec = describeSpec(clone2(input));
6832
6933
  const autoFixes = [];
6934
+ fixMissingQuestionFields(spec, autoFixes);
6833
6935
  raiseSensitivity(spec, autoFixes);
6834
6936
  flagAskedOfSubject(spec, ctx, autoFixes);
6835
6937
  fixBooking(spec, autoFixes);
@@ -7163,6 +7265,7 @@ export {
7163
7265
  isSelfDeclaredField,
7164
7266
  isTimedField,
7165
7267
  lastResortTrim,
7268
+ meaningWords,
7166
7269
  membersAddRowsSteps,
7167
7270
  mergeDescribeParts,
7168
7271
  normalizeForPhones,
@@ -7192,6 +7295,7 @@ export {
7192
7295
  rowsFor,
7193
7296
  ruleBreak,
7194
7297
  runTemplateDoor,
7298
+ sameMeaning,
7195
7299
  selfDeclaredFieldSpec,
7196
7300
  sensitivityFloorOf,
7197
7301
  shellsPayload,