@rsconcept/rstool 1.7.0 → 1.9.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 (76) hide show
  1. package/README.md +5 -5
  2. package/dist/{agent-workflow-DY-hfAap.d.ts → agent-workflow-BqEC_Foh.d.ts} +5 -6
  3. package/dist/{analysis-ktWFu4BY.d.ts → analysis-C7GrmB17.d.ts} +3 -4
  4. package/dist/{common-D6TFl6l_.d.ts → common-oafVB-wX.d.ts} +1 -2
  5. package/dist/{constituenta-HTnFtmxt.d.ts → constituenta-CwJogNKY.d.ts} +4 -5
  6. package/dist/{diagnostic-CODFUU-Q.d.ts → diagnostic-CtxXifJY.d.ts} +4 -3
  7. package/dist/{diagnostic-assembly-B-6vIA5I.js → diagnostic-assembly-B-r0NyHL.js} +3 -2
  8. package/dist/diagnostic-assembly-B-r0NyHL.js.map +1 -0
  9. package/dist/{evaluation-6A7rK4t7.d.ts → evaluation-BHv2xNwf.d.ts} +3 -4
  10. package/dist/index.d.ts +11 -10
  11. package/dist/index.js +1 -1
  12. package/dist/mappers/model-adapter.d.ts +9 -4
  13. package/dist/mappers/model-adapter.js +29 -4
  14. package/dist/mappers/model-adapter.js.map +1 -1
  15. package/dist/mappers/schema-adapter.d.ts +5 -5
  16. package/dist/mappers/schema-adapter.js +1 -1
  17. package/dist/mappers/types.d.ts +5 -4
  18. package/dist/mappers/types.js +1 -1
  19. package/dist/mappers/types.js.map +1 -1
  20. package/dist/{model-value-BUhr2IjA.d.ts → model-value-C0QA-ji2.d.ts} +2 -3
  21. package/dist/models/agent-workflow.d.ts +1 -1
  22. package/dist/models/analysis.d.ts +1 -1
  23. package/dist/models/common.d.ts +1 -1
  24. package/dist/models/constituenta.d.ts +1 -1
  25. package/dist/models/diagnostic.d.ts +1 -1
  26. package/dist/models/diagnostic.js +1 -1
  27. package/dist/models/evaluation.d.ts +1 -1
  28. package/dist/models/index.d.ts +10 -10
  29. package/dist/models/index.js +1 -1
  30. package/dist/models/model-value.d.ts +1 -1
  31. package/dist/models/rstool-agent.d.ts +1 -1
  32. package/dist/models/rstool-agent.js +1 -1
  33. package/dist/models/session.d.ts +1 -1
  34. package/dist/models/tool-contract.d.ts +1 -1
  35. package/dist/models/tool-contract.js +1 -1
  36. package/dist/models/tool-contract.js.map +1 -1
  37. package/dist/{models-BKmWx92e.js → models-D06g8LId.js} +8 -7
  38. package/dist/{models-BKmWx92e.js.map → models-D06g8LId.js.map} +1 -1
  39. package/dist/{rsform-builder-DKwZfJvN.js → rsform-builder-BlXEe5Ow.js} +2 -2
  40. package/dist/{rsform-builder-DKwZfJvN.js.map → rsform-builder-BlXEe5Ow.js.map} +1 -1
  41. package/dist/{rstool-agent-D-w4DlEo.d.ts → rstool-agent-DLq0mtym.d.ts} +9 -10
  42. package/dist/session/session-store.d.ts +3 -2
  43. package/dist/session/session-store.js +1 -1
  44. package/dist/{session-Uc_vHrq9.d.ts → session-OaLqLq0H.d.ts} +3 -4
  45. package/dist/{tool-contract-i0v2gbWs.d.ts → tool-contract-ZVyh7hhp.d.ts} +10 -11
  46. package/dist/wrapper/client.d.ts +12 -5
  47. package/dist/wrapper/client.js +79 -12
  48. package/dist/wrapper/client.js.map +1 -1
  49. package/dist/wrapper/stdio-wrapper.d.ts +1 -1
  50. package/dist/wrapper/stdio-wrapper.js +18 -10
  51. package/dist/wrapper/stdio-wrapper.js.map +1 -1
  52. package/docs/CONCEPTUAL-SCHEMA.md +1 -1
  53. package/docs/CONSTRUCTIONS.md +372 -0
  54. package/docs/DIAGNOSTICS.md +70 -28
  55. package/docs/DOMAIN.md +7 -2
  56. package/docs/MODEL-TESTING.md +48 -0
  57. package/docs/README.md +1 -0
  58. package/docs/SYNTAX.md +1 -1
  59. package/docs/TYPIFICATION.md +11 -2
  60. package/examples/diagnostics-utils.ts +3 -4
  61. package/package.json +20 -19
  62. package/skills/README.md +1 -1
  63. package/skills/rstool-helper/EXAMPLES.md +24 -0
  64. package/skills/rstool-helper/GUIDE.md +49 -22
  65. package/skills/rstool-helper/REFERENCE.md +24 -5
  66. package/skills/rstool-helper/SKILL.md +7 -4
  67. package/src/mappers/diagnostic-assembly.ts +5 -2
  68. package/src/mappers/model-adapter.ts +34 -1
  69. package/src/mappers/types.ts +1 -0
  70. package/src/models/rstool-agent.test.ts +95 -0
  71. package/src/models/tool-contract.ts +1 -1
  72. package/src/wrapper/client.test.ts +43 -4
  73. package/src/wrapper/client.ts +95 -16
  74. package/src/wrapper/stdio-handler.test.ts +43 -0
  75. package/src/wrapper/stdio-handler.ts +19 -5
  76. package/dist/diagnostic-assembly-B-6vIA5I.js.map +0 -1
@@ -35,6 +35,16 @@ await tool.setModelValues({
35
35
 
36
36
  Здесь `X1` получает подписи элементов, а `S1: ℬ(X1×X1)` получает множество пар по индексам элементов `X1`.
37
37
 
38
+ - Порядок элементов и дубликаты во входе не важны: `setModelValues` приводит значения `S#` к канонической форме (сортировка `compare`, без повторов). В `getModelState` и в результатах вычисления множества всегда отсортированы — сравнивай с ожиданием как с отсортированным массивом.
39
+ - Кортежи — `[TUPLE_ID, …]`; вложенные множества — обычные массивы (`ℬℬ(X1)`: `[[0, 1], [2]]`).
40
+ - `status` в `evaluate` / `recalculateModel` — числовой `EvalStatus` (`7` `HAS_DATA`, `6` `EMPTY`, `5` `AXIOM_FALSE`, `4` `EVAL_FAIL`); таблица — в `REFERENCE.md`.
41
+
42
+ ## Чтение результата
43
+
44
+ - Конституента: `evaluate({ constituentId })` — вычисляет её и все поставщики.
45
+ - Scratch: `evaluate({ expression, cstType })`. `cstType` задаёт ожидаемую ступень: `'term'` — множество или число, `'axiom'` — логическое выражение (`P1[S1]`, `D2⊆S1`). В scratch-выражении можно ссылаться на любые конституенты схемы, включая производные `D#`.
46
+ - Литералов элементов `X#` в ЯРЭ нет: выражай тестовые элементы через схему (`debool(D3)`, `Pr1(S1)\Pr2(S1)`), а не через подписи.
47
+
38
48
  ## Скрипты-тесты
39
49
 
40
50
  Если проверка влияет на поведение схемы или есть риск регрессии, вынеси ее в маленький скрипт или colocated `*.test.ts`:
@@ -46,6 +56,44 @@ await tool.setModelValues({
46
56
 
47
57
  Тест должен проверять смысл формулы, а не только отсутствие диагностик. Для аксиом отдельно проверяй положительный и отрицательный набор данных: корректная модель должна давать истинную аксиому, ошибочная — `AXIOM_FALSE` или диагностируемый сбой вычисления.
48
58
 
59
+ ```ts
60
+ import { EvalStatus, TUPLE_ID } from '@rsconcept/domain';
61
+
62
+ // X1 — вершины, S1: ℬ(X1×X1) — рёбра, A1 ::= ∀α∈X1 α∉F4[α] — ацикличность
63
+ const X1 = 1,
64
+ S1 = 2,
65
+ A1 = 17;
66
+ await tool.setModelValues({
67
+ set: [
68
+ { target: X1, value: { 0: 'a', 1: 'b', 2: 'c' } },
69
+ {
70
+ target: S1,
71
+ value: [
72
+ [TUPLE_ID, 0, 1],
73
+ [TUPLE_ID, 1, 2]
74
+ ]
75
+ }
76
+ ]
77
+ });
78
+ let a1 = tool.evaluate({ constituentId: A1 });
79
+ // a1.status === EvalStatus.HAS_DATA, a1.value === 1
80
+
81
+ await tool.setModelValues({
82
+ set: [
83
+ {
84
+ target: S1,
85
+ value: [
86
+ [TUPLE_ID, 0, 1],
87
+ [TUPLE_ID, 1, 2],
88
+ [TUPLE_ID, 2, 0]
89
+ ]
90
+ }
91
+ ]
92
+ });
93
+ a1 = tool.evaluate({ constituentId: A1 });
94
+ // a1.status === EvalStatus.AXIOM_FALSE, a1.value === 0
95
+ ```
96
+
49
97
  ## Ограничения
50
98
 
51
99
  - Маленькая КМ проверяет семантику на выбранных данных, но не доказывает определение для всех моделей.
package/docs/README.md CHANGED
@@ -11,6 +11,7 @@
11
11
  - **CONSTITUENTA.md** — структура и правила конституент.
12
12
  - **BASE-SELECTION.md** — выбор основания: `X#`, `C#` или `Z`.
13
13
  - **SYNTAX.md** — синтаксис ЯРЭ: операторы, генераторы `D{}`/`I{}`/`R{}`, кванторы, `F#`/`P#`.
14
+ - **CONSTRUCTIONS.md** — объяснение формул и конструкций человеку (карточки, разбор снаружи внутрь).
14
15
  - **TYPIFICATION.md** — типизация (`AnalysisResult.type`, `valueClass`).
15
16
  - **MODEL-TESTING.md** — проверка семантики определений на маленьких концептуальных моделях.
16
17
  - **DIAGNOSTICS.md** — диагностики анализа и способы исправления ошибок.
package/docs/SYNTAX.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Синтаксис ЯРЭ
2
2
 
3
- Единая справка для агента: читай при построении или исправлении `definitionFormal`. Типизация — `TYPIFICATION.md`; ошибки — `DIAGNOSTICS.md`.
3
+ Единая справка для агента: читай при построении или исправлении `definitionFormal`. Типизация — `TYPIFICATION.md`; ошибки — `DIAGNOSTICS.md`. Объяснение человеку — [CONSTRUCTIONS.md](CONSTRUCTIONS.md).
4
4
 
5
5
  ## Имена и литералы
6
6
 
@@ -25,8 +25,17 @@
25
25
  ## `AnalysisResult.type`
26
26
 
27
27
  1. Сначала проверь `analysis.success === true` и `analysis.type !== null`.
28
- 2. Не разбирай объект вручную; используй helpers из `@rsconcept/domain`.
29
- 3. `valueClass` уточняет вычислимость: `Value`, `Property`, `Invalid`.
28
+ 2. Не разбирай объект вручную; печатай через `labelType` из `@rsconcept/domain/rslang/labels`:
29
+
30
+ ```ts
31
+ import { labelType } from '@rsconcept/domain/rslang/labels';
32
+
33
+ labelType(analysis.type); // 'ℬ(X1×X1)', '[X1] → ℬ(X1)', '[ℬ(X1×X1)] → Logic', 'Logic'
34
+ ```
35
+
36
+ Для скрипта вне пакета — `file:///<workspace>/rsconcept/domain/src/rslang/labels.ts` (см. GUIDE, «Как запускать rstool»). `normalizeType` из того же модуля даёт каноническую строку для сравнения типизаций.
37
+
38
+ 3. `valueClass` уточняет вычислимость: `'value'` (вычислимо), `'property'` (только проверка принадлежности), `null` (ошибка). `success: true` не исключает диагностику `error`: квантор по `ℬ(…)` разбирается, но даёт `invalidPropertyUsage` и `valueClass: null`.
30
39
 
31
40
  ## `S#` и `D#`
32
41
 
@@ -5,10 +5,9 @@ export type ExampleDiagnostic = Diagnostic;
5
5
  export function formatDiagnostics(diags: ExampleDiagnostic[], aliasById?: ReadonlyMap<number, string>): string {
6
6
  return diags
7
7
  .map(d => {
8
- const alias =
9
- d.alias ??
10
- (d.constituentId !== undefined ? (aliasById?.get(d.constituentId) ?? '?') : '(scratch)');
11
- return `[${d.kind}] ${alias} ${d.name} ${JSON.stringify(d.params ?? [])}`;
8
+ const alias = d.alias ?? (d.constituentId !== undefined ? (aliasById?.get(d.constituentId) ?? '?') : '(scratch)');
9
+ const stack = d.stack && d.stack.length > 0 ? ` stack=${d.stack.map(frame => frame.alias).join('←')}` : '';
10
+ return `[${d.kind}] ${alias} ${d.name} ${JSON.stringify(d.params ?? [])}${stack}`;
12
11
  })
13
12
  .join('\n');
14
13
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rsconcept/rstool",
3
- "version": "1.7.0",
3
+ "version": "1.9.0",
4
4
  "description": "Agent-facing library for incremental RSForm construction, RSLang analysis, diagnostics, modeling, and evaluation. Wraps @rsconcept/domain with a deterministic session contract.",
5
5
  "license": "MIT",
6
6
  "author": "IRBorisov",
@@ -50,6 +50,23 @@
50
50
  "README.md",
51
51
  "LICENSE"
52
52
  ],
53
+ "dependencies": {
54
+ "eslint": "^10.9.1",
55
+ "eslint-plugin-simple-import-sort": "^14.0.0",
56
+ "@rsconcept/domain": "3.1.1"
57
+ },
58
+ "devDependencies": {
59
+ "@types/node": "^26.4.1",
60
+ "@vitest/coverage-v8": "^4.1.11",
61
+ "tsdown": "^0.22.14",
62
+ "tsx": "^4.23.13",
63
+ "typescript": "^6.0.3",
64
+ "unrun": "^0.3.1",
65
+ "vitest": "^4.1.11"
66
+ },
67
+ "publishConfig": {
68
+ "access": "public"
69
+ },
53
70
  "scripts": {
54
71
  "build": "tsdown",
55
72
  "typecheck": "tsc -p tsconfig.json --noEmit",
@@ -68,22 +85,6 @@
68
85
  "example:build-expression-bank-schema": "tsx examples/expression-bank/build-rsform.ts",
69
86
  "example:build-template-apply-schema": "tsx examples/template-apply/build-rsform.ts",
70
87
  "example:audit-diagnostics": "tsx examples/audit-diagnostics.ts",
71
- "kinship:cli": "tsx examples/kinship/cli.ts",
72
- "prepublishOnly": "pnpm run build"
73
- },
74
- "dependencies": {
75
- "@rsconcept/domain": "workspace:*"
76
- },
77
- "devDependencies": {
78
- "@types/node": "^26.1.0",
79
- "@vitest/coverage-v8": "^4.1.9",
80
- "tsdown": "^0.22.3",
81
- "tsx": "^4.22.5",
82
- "typescript": "^6.0.3",
83
- "unrun": "^0.3.1",
84
- "vitest": "^4.1.9"
85
- },
86
- "publishConfig": {
87
- "access": "public"
88
+ "kinship:cli": "tsx examples/kinship/cli.ts"
88
89
  }
89
- }
90
+ }
package/skills/README.md CHANGED
@@ -9,7 +9,7 @@ Each skill lives in its own subdirectory: `skills/<skill-name>/SKILL.md` (plus r
9
9
  - `rstool-helper/GUIDE.md`: entry point — task→read matrix, workflows (incl. review), checklists (Russian)
10
10
  - `rstool-helper/REFERENCE.md`: API, stdio, MCP names, response shapes, help map (Russian)
11
11
  - `rstool-helper/EXAMPLES.md`: snippets, anti-patterns, diagnostics, `examples/` index (Russian)
12
- - `../docs/*.md`: language reference (DOMAIN, SYNTAX, DIAGNOSTICS, …)
12
+ - `../docs/*.md`: language reference (DOMAIN, SYNTAX, CONSTRUCTIONS, DIAGNOSTICS, …)
13
13
 
14
14
  ## npm workflow
15
15
 
@@ -153,6 +153,7 @@ tool.applySchemaPatch({
153
153
  | `globalFuncParenCall` | `F1(ξ)` | `F1[ξ]` |
154
154
  | `globalFuncWithoutArgs` | голое `F1` в формуле | `F1[…]` |
155
155
  | `radicalUsage` | `R1` в теле формулы или как конституента | `R#` только в `[…]` у `F#`/`P#` |
156
+ | `invalidPropertyUsage` | `∀σ∈ℬ(S1)\{∅} ¬P1[σ]` — квантор по булеану | вычислимая форма через достижимость: `∀α∈X1 α∉F4[α]` |
156
157
  | `schemaMissingConvention` | `X1` без конвенции | добавь предметную конвенцию |
157
158
  | `schemaHomonym` | одинаковый термин у разных конституент | различай термины |
158
159
  | `schemaDependencyCycle` | круг в определениях (`F3 → P1 → F3`) | разорви цикл: убери взаимные ссылки |
@@ -237,6 +238,29 @@ const scratch = tool.evaluate({ expression: '1+2', cstType: CstType.TERM });
237
238
  const recalc = tool.recalculateModel();
238
239
  ```
239
240
 
241
+ Множества `S#` — кортежи `[TUPLE_ID, i, j]` по индексам `X#`; порядок во входе не важен (нормализуется). Scratch может ссылаться на производные конституенты; логические выражения — с `cstType: CstType.AXIOM`:
242
+
243
+ ```ts
244
+ import { EvalStatus, TUPLE_ID } from '@rsconcept/domain';
245
+
246
+ await tool.setModelValues({
247
+ set: [
248
+ { target: 1, value: { 0: 'a', 1: 'b', 2: 'c' } },
249
+ {
250
+ target: 2,
251
+ value: [
252
+ [TUPLE_ID, 1, 2],
253
+ [TUPLE_ID, 0, 1]
254
+ ]
255
+ } // S1: ℬ(X1×X1)
256
+ ]
257
+ });
258
+ const closure = tool.evaluate({ constituentId: 9 }); // D9 — транзитивное замыкание
259
+ closure.status === EvalStatus.HAS_DATA; // status — число: 7
260
+ const check = tool.evaluate({ expression: 'S1⊆D9', cstType: CstType.AXIOM });
261
+ check.value === 1;
262
+ ```
263
+
240
264
  ## Экспорт / импорт и Portal round-trip
241
265
 
242
266
  ```ts
@@ -18,7 +18,7 @@
18
18
  - Stdio/MCP: **плоские** `params` — поля метода и optional `sessionId` на одном уровне (без `params.input`).
19
19
  - Состояние: `getSessionState()` (summary) или `getSessionState('full')`.
20
20
  - Импорт/экспорт Portal: `importData`, `exportPortal({ kind })`.
21
- - Модель: `setModelValues`, `evaluate`, `recalculateModel`.
21
+ - Модель: `setModelValues`, `evaluate`, `recalculateModel`. Статусы вычисления приходят числами (`EvalStatus`, см. REFERENCE).
22
22
  - Перед сохранением сомнительной формулы — `analyzeExpression`.
23
23
 
24
24
  ## Задача → чтение
@@ -31,9 +31,10 @@
31
31
  | Оценить / отревьюить готовую КС (без запроса «перепиши») | этот GUIDE → [ревью / оценка](#ревью--оценка-готовой-кс); [чеклист ревью](#ревью-концептуальной-схемы); [CONCEPTUAL-SCHEMA.md](../../docs/CONCEPTUAL-SCHEMA.md); при импорте — [PORTAL-API.md](../../docs/PORTAL-API.md) | EXAMPLES с batch patch; полный SYNTAX |
32
32
  | Точечные правки существующей схемы | этот GUIDE → [редактирование](#редактирование-и-проверка-схемы); [REFERENCE.md](REFERENCE.md) при вызовах | CONCEPTUAL-SCHEMA целиком |
33
33
  | Починить формулу / диагностики | этот GUIDE → [цикл диагностик](#цикл-диагностик); [DIAGNOSTICS.md](../../docs/DIAGNOSTICS.md); [EXAMPLES.md](EXAMPLES.md) «типичные диагностики» | CONCEPTUAL-SCHEMA; PORTAL-API |
34
+ | Написать формулу; синтаксис `D{}`/`I{}`/`R{}`, `F#`/`P#` | [SYNTAX.md](../../docs/SYNTAX.md); при необходимости [EXAMPLES.md](EXAMPLES.md) | CONSTRUCTIONS; воркфлоу сессий |
35
+ | Объяснить формулу или конструкцию человеку | [CONSTRUCTIONS.md](../../docs/CONSTRUCTIONS.md) | SYNTAX целиком; воркфлоу сессий |
34
36
  | Импорт / правка по ссылке Portal | этот GUIDE → [Portal](#оценка-или-правка-кс-из-portal); [PORTAL-API.md](../../docs/PORTAL-API.md) | SYNTAX; BASE-SELECTION |
35
37
  | Проверить смысл на данных / КМ | этот GUIDE → [КМ](#проверка-на-маленькой-км); [MODEL-TESTING.md](../../docs/MODEL-TESTING.md) | DOMAIN/ОСС; CONCEPTUAL-SCHEMA |
36
- | Объяснить синтаксис / генераторы / `F#`/`P#` | [SYNTAX.md](../../docs/SYNTAX.md); при необходимости [EXAMPLES.md](EXAMPLES.md) | воркфлоу сессий |
37
38
  | Разобрать типизацию / ступени | [TYPIFICATION.md](../../docs/TYPIFICATION.md) | PORTAL-API |
38
39
  | Вызовы API / stdio / MCP | [REFERENCE.md](REFERENCE.md) | docs языка |
39
40
  | Термины КС, ядро/тело, не путать ярлыки | [DOMAIN.md](../../docs/DOMAIN.md) | — |
@@ -56,10 +57,28 @@
56
57
  Из корня workspace Portal (после `pnpm install` и сборки `@rsconcept/domain`):
57
58
 
58
59
  ```bash
59
- pnpm --filter @rsconcept/rstool exec tsx path/to/your/build-schema.ts
60
+ pnpm --filter @rsconcept/rstool exec tsx C:/path/to/review.mts
60
61
  ```
61
62
 
62
- В скрипте импортируй API пакета (`@rsconcept/rstool` или относительный путь к `rsconcept/rstool/src` только если скрипт лежит вне пакета и это осознанный выбор). Либо используй stdio/MCP без отдельного файла.
63
+ Правила для скрипта **вне** `rsconcept/rstool/`:
64
+
65
+ - `import … from '@rsconcept/rstool'` из такого файла **не резолвится** — пакет не поднят в корневой `node_modules`. Импортируй по абсолютному `file://` URL: `file:///<workspace>/rsconcept/rstool/src/index.ts` (или `…/dist/index.js`). На Windows абсолютный путь без `file://` тоже падает (`ERR_UNSUPPORTED_ESM_URL_SCHEME`).
66
+ - Расширение **`.mts`**: `.ts` вне пакета трактуется как CommonJS, а `@rsconcept/*` экспортируют только ESM (`ERR_PACKAGE_PATH_NOT_EXPORTED`).
67
+ - Helpers domain — тем же способом: `file:///…/rsconcept/domain/src/index.ts` (`TUPLE_ID`), `…/domain/src/rslang/labels.ts` (`labelType`).
68
+ - Вывод в файл с `-Encoding utf8` (PowerShell) — в консоли cp1251 символы ЯРЭ ломаются.
69
+
70
+ ```ts
71
+ import { readFileSync } from 'node:fs';
72
+ // <workspace> — абсолютный путь к корню Portal, например D:/work/Portal или /home/me/Portal
73
+ import { RSToolAgent } from 'file:///<workspace>/rsconcept/rstool/src/index.ts';
74
+ import { TUPLE_ID } from 'file:///<workspace>/rsconcept/domain/src/index.ts';
75
+
76
+ const tool = new RSToolAgent();
77
+ tool.importData(readFileSync('C:/tmp/details.json', 'utf-8'));
78
+ console.log(tool.listDiagnostics());
79
+ ```
80
+
81
+ Альтернатива без резолвинга — stdio-обёртка из собранного `dist`: `RSToolWrapperClient({ command: 'node', args: ['<abs>/rsconcept/rstool/dist/wrapper/stdio-wrapper.js'], cwd: '<abs>/rsconcept/rstool' })`. Внутри пакета (тесты, штатные `examples/`) обычный `@rsconcept/rstool` работает.
63
82
 
64
83
  ## Что читать
65
84
 
@@ -72,6 +91,7 @@ pnpm --filter @rsconcept/rstool exec tsx path/to/your/build-schema.ts
72
91
  - Концептуализация, создание схемы: [../../docs/CONCEPTUAL-SCHEMA.md](../../docs/CONCEPTUAL-SCHEMA.md).
73
92
  - Конституенты и валидация: [../../docs/CONSTITUENTA.md](../../docs/CONSTITUENTA.md).
74
93
  - Синтаксис ЯРЭ: [../../docs/SYNTAX.md](../../docs/SYNTAX.md).
94
+ - Объяснение формул и конструкций: [../../docs/CONSTRUCTIONS.md](../../docs/CONSTRUCTIONS.md).
75
95
  - Типизация: [../../docs/TYPIFICATION.md](../../docs/TYPIFICATION.md).
76
96
  - Проверка определений на КМ: [../../docs/MODEL-TESTING.md](../../docs/MODEL-TESTING.md).
77
97
  - Диагностики: [../../docs/DIAGNOSTICS.md](../../docs/DIAGNOSTICS.md).
@@ -130,19 +150,20 @@ kind `auto` (default) определит `portal-details` или `portal-schema`
130
150
  **Правило:** без явного запроса на правку не делай массовый рефакторинг и не заменяй схему «улучшенной версией». Сначала отчёт; правки — только по согласованию или если пользователь сразу сказал «исправь».
131
151
 
132
152
  1. Загрузи схему: текущая сессия, `importData`, или Portal → [PORTAL-API.md](../../docs/PORTAL-API.md).
133
- 2. `listDiagnostics()` — раздели `expression` / `schema` / `model`; зафиксируй ошибки и предупреждения.
134
- 3. `getSessionState('summary')` — обзор состава; при необходимости `full` для формул.
153
+ 2. `listDiagnostics()` — раздели `expression` / `schema` / `model`; зафиксируй ошибки и предупреждения. `analysisSuccess: true` в summary **не** означает отсутствие диагностик (например, `invalidPropertyUsage` даёт `error` при успешном разборе) — опирайся на `listDiagnostics`.
154
+ 3. `getSessionState('summary')` — обзор состава; при необходимости `full` для формул. Типизацию из `analysis.type` печатай через `labelType` ([TYPIFICATION.md](../../docs/TYPIFICATION.md)), а не разбирай AST вручную.
135
155
  4. Структурный проход по [чеклисту ревью](#ревью-концептуальной-схемы) и [CONCEPTUAL-SCHEMA.md](../../docs/CONCEPTUAL-SCHEMA.md):
136
156
  - неопределяемые vs производные (`S#` там, где должно быть `D#`/`F#`/`P#`);
137
157
  - `X#` / `C#` / `Z` ([BASE-SELECTION.md](../../docs/BASE-SELECTION.md));
138
158
  - терм-функции vs множества пар;
139
159
  - радикалы `R#` не объявлены конституентами;
140
160
  - покрытие задачи vs избыточность.
141
- 5. Severity в отчёте:
142
- - **блокер** — диагностики `error`, схема не анализируется / ломает вычисление;
143
- - **важно** — нарушение правил проектирования (лишнее `S#`, график вместо `F#`, смешение экспликаций);
144
- - **замечание** — стиль, именование, возможные упрощения, пробелы покрытия.
145
- 6. Ответ — по [шаблону ревью](#шаблон-ответа-ревью). Предлагай конкретные правки списком; применяй их только по запросу.
161
+ 5. Severity в отчёте — по **смыслу** диагностики, а не только по полю `severity` (инструмент помечает `error` и метаданные):
162
+ - **блокер** — `expression` с `severity: error` (формула не разбирается, невычислима: `invalidPropertyUsage`, …), `schemaDependencyCycle`, `schemaFormalDuplicate`, `schemaHomonym`; схема не анализируется / ломает вычисление;
163
+ - **важно** — нарушение правил проектирования (лишнее `S#`, график вместо `F#`, смешение экспликаций, перекрёстные ссылки `C#`, термин сильнее формулы);
164
+ - **замечание** — `schemaMissingConvention`, `schemaMissingTerm`, `modelEmpty` у структур, не задействованных в проверке; стиль, именование, упрощения, пробелы покрытия.
165
+ 6. Проверь на маленькой КМ ключевые производные и аксиомы ([цикл КМ](#проверка-на-маленькой-км)): позитивный набор данных → ожидаемые значения, негативный → `AXIOM_FALSE`. Это чаще всего даёт главные находки (расхождение термина и формулы, лишние/пропущенные элементы).
166
+ 7. Ответ — по [шаблону ревью](#шаблон-ответа-ревью). Предлагай конкретные правки списком; применяй их только по запросу.
146
167
 
147
168
  ### Цикл диагностик
148
169
 
@@ -150,8 +171,8 @@ kind `auto` (default) определит `portal-details` или `portal-schema`
150
171
 
151
172
  1. Вызови `listDiagnostics()` — смотри `kind`: `expression` (формула), `schema` (омонимы, дубликаты, термин/конвенция), `model` (интерпретация).
152
173
  2. Для черновика формулы — `analyzeExpression` (без сохранения).
153
- 3. Прочитай `name`, `severity`, `alias`, `from`, `to`, `params` в `analysis.diagnostics` или через `listDiagnostics({ kind })`.
154
- 4. Сопоставь `name` (или `code`) с исправлением в [DIAGNOSTICS.md](../../docs/DIAGNOSTICS.md).
174
+ 3. Прочитай `name`, `severity`, `alias`, `from`, `to`, `params`, при вычислении — ещё `stack` (цепочка `F#`/`P#`, см. [DIAGNOSTICS.md](../../docs/DIAGNOSTICS.md)), в `analysis.diagnostics` или через `listDiagnostics({ kind })`.
175
+ 4. Сопоставь `name` (или `code`) с исправлением в [DIAGNOSTICS.md](../../docs/DIAGNOSTICS.md). Для runtime-ошибок внутри вызова смотри `stack[0]` — там реальный locus, а не только call site в корневом выражении.
155
176
  5. Исправь именно диапазон в `definitionFormal` (expression) или метаданные/данные модели (schema/model).
156
177
  6. Не повторяй вызов без изменения ввода.
157
178
  7. После успеха — ещё один `applySchemaPatch` / `setModelValues` с исправлением.
@@ -160,12 +181,14 @@ kind `auto` (default) определит `portal-details` или `portal-schema`
160
181
 
161
182
  Когда синтаксис верен, но смысл формулы неочевиден. Подробности и форма данных — [MODEL-TESTING.md](../../docs/MODEL-TESTING.md).
162
183
 
163
- 1. Отдельная `createSession` или изолированная копия текущей сессии.
184
+ 1. Отдельная `createSession` или изолированная копия текущей сессии (для ревью схемы из Portal достаточно самой импортированной сессии).
164
185
  2. Только нужные поставщики: `X#`, `C#`, `S#` и проверяемые `D#` / `F#` / `P#` / `A#`.
165
186
  3. `analyzeExpression` и `applySchemaPatch`.
166
- 4. Значения: `setModelValues`.
167
- 5. `evaluate`; сравни с ожидаемым `value`.
168
- 6. При нескольких зависимых определениях — `recalculateModel`.
187
+ 4. Значения: `setModelValues`. `X#` — `{ 0: 'a', 1: 'b' }` (индекс → подпись), `S#` — массив кортежей `[TUPLE_ID, i, j]` по индексам `X#`; порядок элементов не важен, значения приводятся к канонической форме автоматически.
188
+ 5. `evaluate({ constituentId })` для конституенты; `evaluate({ expression, cstType })` для scratch-выражения — `cstType` задаёт ожидаемую ступень: множества и числа — `'term'`, логические выражения (`P1[S1]`, `D2⊆S1`) — `'axiom'`. В scratch можно ссылаться на любые конституенты схемы, включая `D#` — зависимости вычисляются автоматически.
189
+ 6. `status` в ответе — **число** (`EvalStatus`): `7` `HAS_DATA`, `6` `EMPTY`, `5` `AXIOM_FALSE`, `4` `EVAL_FAIL`, `3` `INVALID_DATA`, `1` `NO_EVAL`; полный список — [REFERENCE.md](REFERENCE.md). Сравни `value` с ожидаемым.
190
+ 7. При нескольких зависимых определениях — `recalculateModel`.
191
+ 8. Для аксиом — два набора данных: корректный (истина) и нарушающий (`AXIOM_FALSE`).
169
192
 
170
193
  Для регрессий вынеси проверку в скрипт или colocated `*.test.ts` (см. [MODEL-TESTING.md](../../docs/MODEL-TESTING.md)).
171
194
 
@@ -194,8 +217,8 @@ kind `auto` (default) определит `portal-details` или `portal-schema`
194
217
  2. **Диагностики:** число ошибок / предупреждений по `expression` / `schema` / `model` (без полного JSON).
195
218
  3. **Находки** списком: блокер → важно → замечание; у каждой — имя конституенты и суть.
196
219
  4. **Покрытие:** что из задачи отражено; чего не хватает.
197
- 5. **Рекомендации:** конкретные правки; не применяй их без запроса.
198
- 6. **Проверки:** что уже сделано (import, listDiagnostics, выборочный анализ).
220
+ 5. **Рекомендации:** конкретные правки; не применяй их без запроса. Предложенную замену формулы прогони через анализ выражения (и по возможности через КМ) до того, как её советовать.
221
+ 6. **Проверки:** что уже сделано (импорт, диагностики, выборочный анализ) и **КМ**: данные → ожидание → результат для проверенных конституент.
199
222
 
200
223
  ### Шаблон ответа: диагностики
201
224
 
@@ -206,10 +229,14 @@ kind `auto` (default) определит `portal-details` или `portal-schema`
206
229
 
207
230
  ### Шаблон ответа: объяснение формулы
208
231
 
232
+ Полный сценарий и карточки конструкций — [CONSTRUCTIONS.md](../../docs/CONSTRUCTIONS.md). Кратко:
233
+
209
234
  - роль конституенты и термин;
210
235
  - формула как есть;
211
- - по шагам: что вычисляется / утверждается;
212
- - на какие понятия опирается; типизация.
236
+ - ТМВ или ЛВ;
237
+ - снаружи внутрь, только конструкции из формулы;
238
+ - ступень и опоры;
239
+ - вычисление на данных — только по запросу.
213
240
 
214
241
  ### Язык ответа
215
242
 
@@ -252,7 +279,7 @@ kind `auto` (default) определит `portal-details` или `portal-schema`
252
279
  - [ ] Схема проверена; ошибки исправлены.
253
280
  - [ ] Диагностики обработаны до фиксации шага и экспорта.
254
281
  - [ ] В тексте ответа нет английских имён полей и методов API — использованы термины из раздела [Язык ответа](#язык-ответа).
255
- - [ ] Нет неофициальных ярлыков («базис», «базисный слой») — термины КС из [DOMAIN.md](../../docs/DOMAIN.md).
282
+ - [ ] Нет неофициальных ярлыков («базис», «базисы», «базисный слой», «базовые понятия») — термины КС из [DOMAIN.md](../../docs/DOMAIN.md), раздел **«Термины: не путать»**. Для `X#` говори **базисное множество**, не «базис».
256
283
 
257
284
  ### Ревью концептуальной схемы
258
285
 
@@ -5,7 +5,7 @@
5
5
  ## Контракт rstool
6
6
 
7
7
  - Пакет: `@rsconcept/rstool`
8
- - Версия контракта: `3.2.0` (`CONTRACT_VERSION`)
8
+ - Версия контракта: `3.3.0` (`CONTRACT_VERSION`)
9
9
  - Основной класс: `RSToolAgent`
10
10
  - Публичные импорты: `@rsconcept/rstool` и `@rsconcept/rstool/wrapper`
11
11
  - Опции: `new RSToolAgent({ persistenceDir? })` — каталог для сохранения сессий между перезапусками (stdio/MCP: `RSTOOL_PERSISTENCE_DIR`)
@@ -114,9 +114,11 @@ tool.listDiagnostics({ kind: 'expression' });
114
114
  tool.listDiagnostics({ kind: 'schema', constituentId: 3, severity: 'error' });
115
115
  ```
116
116
 
117
- Фильтры: `kind?: 'expression' | 'schema' | 'model'`, `constituentId?`, `severity?: 'error' | 'warning'`.
117
+ Фильтры: `kind?: 'expression' | 'schema' | 'model'`, `constituentId?`, `severity?: 'error' | 'warning'`. В stdio / MCP те же поля передаются **плоско** в `params` (`{ "kind": "schema", "severity": "warning" }`); stdio дополнительно принимает устаревший вложенный `filters`.
118
118
 
119
- Элемент диагностики (агентский вид): `kind`, `code`, `name`, `severity`, `alias?`, `expression`, `from`, `to`, `params?`. Ключ для поиска исправления — `name` ([DIAGNOSTICS.md](../../docs/DIAGNOSTICS.md)).
119
+ `severity` отражает правило инструмента, а не вес для ревью: `schemaMissingConvention` / `schemaMissingTerm` приходят как `error`. Как ранжировать в отчёте — [GUIDE.md](GUIDE.md), «Ревью / оценка готовой КС».
120
+
121
+ Элемент диагностики (агентский вид): `kind`, `code`, `name`, `severity`, `alias?`, `expression`, `from`, `to`, `params?`, `stack?` (цепочка вызовов при ошибке внутри `F#`/`P#`). Ключ для поиска исправления — `name` ([DIAGNOSTICS.md](../../docs/DIAGNOSTICS.md)).
120
122
 
121
123
  ## `analyzeExpression`
122
124
 
@@ -132,6 +134,8 @@ tool.analyzeExpression({
132
134
 
133
135
  **Результат:** `{ success, type, valueClass, diagnostics }`. Не сохраняет конституенту. Для черновика формулы — до `applySchemaPatch`.
134
136
 
137
+ `type` — AST типизации; для чтения используй `labelType` из `@rsconcept/domain/rslang/labels` (`ℬ(X1×X1)`, `[X1] → ℬ(X1)`, `Logic`). `success: true` совместим с диагностикой `error` (например, `invalidPropertyUsage` при `valueClass: null`) — проверяй `diagnostics`.
138
+
135
139
  ## `evaluate`
136
140
 
137
141
  Ровно один вариант входа:
@@ -141,7 +145,19 @@ tool.analyzeExpression({
141
145
 
142
146
  **Результат:** `{ success, value, status, iterations, cacheHits, diagnostics }`.
143
147
 
144
- `status` — статус вычисления КМ (`HAS_DATA`, `EMPTY`, `AXIOM_FALSE`, `EVAL_FAIL`, … — [DOMAIN.md](../../docs/DOMAIN.md)).
148
+ `status` — статус вычисления КМ, **числовой** код `EvalStatus` (`@rsconcept/domain`); смысл — [DOMAIN.md](../../docs/DOMAIN.md):
149
+
150
+ | Код | Имя | Смысл |
151
+ | --: | --------------- | --------------------------------------- |
152
+ | `1` | `NO_EVAL` | не вычисляется (`X#`, `C#`, `F#`, `P#`) |
153
+ | `2` | `NOT_PROCESSED` | вычисление не запускалось |
154
+ | `3` | `INVALID_DATA` | неверная интерпретация |
155
+ | `4` | `EVAL_FAIL` | ошибка вычисления |
156
+ | `5` | `AXIOM_FALSE` | аксиома ложна |
157
+ | `6` | `EMPTY` | результат пуст |
158
+ | `7` | `HAS_DATA` | вычислено, непусто |
159
+
160
+ Scratch-выражение: `cstType` задаёт ожидаемую ступень (`term` — множество / число, `axiom` — логическое). Ссылки на `D#` / `A#` / `T#` внутри scratch допустимы — их значения (и зависимости через `F#` / `P#`) вычисляются перед оценкой.
145
161
 
146
162
  ## `setModelValues` / `getModelState` / `recalculateModel`
147
163
 
@@ -155,8 +171,9 @@ await tool.setModelValues({
155
171
  - `set[]`: `{ target, value, type? }` — `target` = id конституенты; `type` обычно не нужен.
156
172
  - `clear[]`: id для сброса значений.
157
173
  - Метод **async**. После изменения данных пересчитываются диагностики модели.
174
+ - Значения `S#` приводятся к канонической форме (сортировка, без дубликатов) — порядок элементов во входе не важен; в `getModelState` хранится уже нормализованное значение.
158
175
  - `getModelState()` → `{ items: [{ id, type, value }] }`.
159
- - `recalculateModel()` → `{ items: [{ id, alias, value, status }] }`.
176
+ - `recalculateModel()` → `{ items: [{ id, alias, value, status }] }` (`status` — числовой `EvalStatus`, таблица выше).
160
177
 
161
178
  Формы `value` для `X#` / `S#` — [MODEL-TESTING.md](../../docs/MODEL-TESTING.md).
162
179
 
@@ -237,6 +254,7 @@ JSON-строка сессии для последующего `importData(...,
237
254
  {"id":"9","method":"exportPortal","params":{"kind":"schema"}}
238
255
  {"id":"10","method":"importData","params":{"payload":"..."}}
239
256
  {"id":"11","method":"getSessionState","params":{"detail":"summary"}}
257
+ {"id":"12","method":"listDiagnostics","params":{"kind":"expression","severity":"error"}}
240
258
  ```
241
259
 
242
260
  ## Help map: ошибка → документ
@@ -245,6 +263,7 @@ JSON-строка сессии для последующего `importData(...,
245
263
  | -------------------------------------------------- | ------------------------------------------------------- |
246
264
  | Код / `name` диагностики | [DIAGNOSTICS.md](../../docs/DIAGNOSTICS.md) |
247
265
  | Синтаксис операторов, `D{}`/`I{}`/`R{}`, `F#`/`P#` | [SYNTAX.md](../../docs/SYNTAX.md) |
266
+ | Объяснить формулу / конструкцию человеку | [CONSTRUCTIONS.md](../../docs/CONSTRUCTIONS.md) |
248
267
  | Ступени, `AnalysisResult.type` | [TYPIFICATION.md](../../docs/TYPIFICATION.md) |
249
268
  | `X#` vs `C#` vs `Z`, переносимость | [BASE-SELECTION.md](../../docs/BASE-SELECTION.md) |
250
269
  | Проектирование, `F#` vs пары, ревью | [CONCEPTUAL-SCHEMA.md](../../docs/CONCEPTUAL-SCHEMA.md) |
@@ -2,8 +2,9 @@
2
2
  name: rstool-helper
3
3
  description: >-
4
4
  ЯРЭ и концептуальные схемы через rstool: концептуализация, ревью/оценка КС,
5
- правки и диагностики формул, проверка на КМ (evaluate), выбор X#/C#/Z,
6
- импорт/экспорт Portal (/rsforms, /details). Читай GUIDE и docs из node_modules.
5
+ правки и диагностики формул, объяснение конструкций ЯРЭ, проверка на КМ (evaluate),
6
+ выбор X#/C#/Z, импорт/экспорт Portal (/rsforms, /details).
7
+ Читай GUIDE и docs из node_modules.
7
8
  ---
8
9
 
9
10
  # rstool-helper
@@ -39,9 +40,11 @@ description: >-
39
40
  | Собрать / развить КС из текста | GUIDE: концептуализация; `docs/CONCEPTUAL-SCHEMA.md`; `docs/BASE-SELECTION.md` |
40
41
  | Оценить / отревьюить готовую КС | GUIDE: ревью; чеклист ревью; `docs/CONCEPTUAL-SCHEMA.md` |
41
42
  | Починить формулу / диагностики | GUIDE: цикл диагностик; `docs/DIAGNOSTICS.md` |
43
+ | Написать формулу / синтаксис | `docs/SYNTAX.md` |
44
+ | Объяснить формулу / конструкцию | `docs/CONSTRUCTIONS.md` |
42
45
  | Импорт / правка по ссылке Portal | GUIDE: Portal; `docs/PORTAL-API.md` |
43
46
  | Проверить смысл на данных | GUIDE: КМ; `docs/MODEL-TESTING.md` |
44
- | Синтаксис / типизация / вызов API | `docs/SYNTAX.md` / `TYPIFICATION.md` / `REFERENCE.md` |
47
+ | Типизация / вызов API | `docs/TYPIFICATION.md` / `REFERENCE.md` |
45
48
 
46
49
  Перед работой всегда открой **GUIDE.md**; остальное — по строке таблицы.
47
50
 
@@ -51,7 +54,7 @@ description: >-
51
54
 
52
55
  ## Язык ответа пользователю
53
56
 
54
- В тексте для пользователя используй термины предметной области («формальное определение», «конституента», «диагностики»), а не английские имена полей и методов API (`definitionFormal`, `applySchemaPatch`). Идентификаторы API — только в коде, patch-ах и JSON. Словарь API → текст: раздел «Язык ответа» в GUIDE.md. Термины КС (базовые понятия, базисное множество, ядро) — только из `docs/DOMAIN.md`, раздел **«Термины: не путать»**.
57
+ В тексте для пользователя используй термины предметной области («формальное определение», «конституента», «диагностики»), а не английские имена полей и методов API (`definitionFormal`, `applySchemaPatch`). Идентификаторы API — только в коде, patch-ах и JSON. Словарь API → текст: раздел «Язык ответа» в GUIDE.md. Термины КС (**неопределяемые понятия**, **базисное множество**, ядро) — только из `docs/DOMAIN.md`, раздел **«Термины: не путать»**. Не сокращай «базисное множество» до «базис» / «базисы».
55
58
 
56
59
  ## Portal (приложение)
57
60
 
@@ -6,7 +6,7 @@ import {
6
6
  } from '@rsconcept/domain/library';
7
7
  import { type DiagnosticSourceFields } from '@rsconcept/domain/library/rsform-api';
8
8
  import { EvalStatus } from '@rsconcept/domain/library/rsmodel';
9
- import { isCritical, RSErrorCode, type RSErrorDescription } from '@rsconcept/domain/rslang/error';
9
+ import { isCritical, RSErrorCode, type EvalStackFrame, type RSErrorDescription } from '@rsconcept/domain/rslang/error';
10
10
 
11
11
  export { DiagnosticKind, RSDiagnosticCode };
12
12
 
@@ -22,6 +22,8 @@ export interface Diagnostic extends CstDiagnostic {
22
22
  expression: string;
23
23
  from: number;
24
24
  to: number;
25
+ /** Evaluation call chain when the fault is inside a called `F#`/`P#` body. */
26
+ stack?: readonly EvalStackFrame[];
25
27
  }
26
28
 
27
29
  /** Context for attributing a {@link Diagnostic} to a constituent. */
@@ -72,7 +74,8 @@ export function expressionDiagnostic(
72
74
  expression,
73
75
  from: error.from,
74
76
  to: error.to,
75
- ...(error.params ? { params: error.params } : {})
77
+ ...(error.params ? { params: error.params } : {}),
78
+ ...(error.stack ? { stack: error.stack } : {})
76
79
  };
77
80
  }
78
81
 
@@ -1,5 +1,7 @@
1
1
  import { DiagnosticKind, expressionDiagnostic } from './diagnostic-assembly';
2
2
  import { type RSErrorDescription, type Value } from '@rsconcept/domain/rslang';
3
+ import { extractGlobals } from '@rsconcept/domain/rslang/api';
4
+ import { normalizeValue } from '@rsconcept/domain/rslang/eval/value-api';
3
5
  import { isBaseSet } from '@rsconcept/domain/library/rsform-api';
4
6
  import { CstType, type RSForm } from '@rsconcept/domain/library/rsform';
5
7
  import { RSEngine, type RSEngineServices } from '@rsconcept/domain/library/rsengine';
@@ -37,7 +39,11 @@ export class ModelAdapter {
37
39
  const binding = toBasicBinding(input.value as Record<string | number, string>);
38
40
  await engine.setBasicValue(input.target, binding);
39
41
  } else {
40
- await engine.setStructureValue(input.target, input.value as Value);
42
+ // Set operations in the evaluator assume canonical form (sorted, deduplicated);
43
+ // agents may pass elements in arbitrary order.
44
+ const value = structuredClone(input.value) as Value;
45
+ normalizeValue(value);
46
+ await engine.setStructureValue(input.target, value);
41
47
  }
42
48
  session.updatedAt = new Date().toISOString();
43
49
  return structuredClone(session.model);
@@ -64,6 +70,7 @@ export class ModelAdapter {
64
70
 
65
71
  public evaluateExpression(session: SessionState, expression: string, cstType: CstType): EvaluationResult {
66
72
  const engine = this.createEngine(session);
73
+ this.prepareReferencedValues(engine, session, expression);
67
74
  const result = engine.evaluateExpression(expression, cstType);
68
75
  const status =
69
76
  result.value === null
@@ -120,6 +127,32 @@ export class ModelAdapter {
120
127
  return engine;
121
128
  }
122
129
 
130
+ /**
131
+ * Computes values of inferrable constituents referenced by a scratch expression (directly or
132
+ * through `F#` / `P#` bodies). A fresh engine only holds interpreted `X#` / `C#` / `S#` values.
133
+ */
134
+ private prepareReferencedValues(engine: RSEngine, session: SessionState, expression: string): void {
135
+ const schema = engine.schema;
136
+ if (!schema) {
137
+ return;
138
+ }
139
+ const globals = extractGlobals(expression);
140
+ const referenced = session.items.filter(item => globals.has(item.alias)).map(item => item.id);
141
+ if (referenced.length === 0) {
142
+ return;
143
+ }
144
+ const required = new Set([...referenced, ...schema.graph.expandAllInputs(referenced)]);
145
+ for (const cstID of schema.graph.topologicalOrder()) {
146
+ if (!required.has(cstID)) {
147
+ continue;
148
+ }
149
+ const cst = schema.cstByID.get(cstID);
150
+ if (cst && isInferrable(cst.cst_type)) {
151
+ engine.calculateCst(cstID);
152
+ }
153
+ }
154
+ }
155
+
123
156
  private validateSetInput(session: SessionState, input: SetConstituentaValueInput): void {
124
157
  const cst = session.items.find(item => item.id === input.target);
125
158
  if (!cst) {
@@ -9,6 +9,7 @@ export interface DomainErrorLike {
9
9
  from: number;
10
10
  to: number;
11
11
  params?: readonly string[];
12
+ stack?: RSErrorDescription['stack'];
12
13
  }
13
14
 
14
15
  /** Minimal domain analysis shape before mapping to {@link AnalysisResult}. */