@rsconcept/rstool 1.4.3 → 1.6.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 (52) hide show
  1. package/README.md +5 -4
  2. package/dist/{agent-workflow-D22FeODL.d.ts → agent-workflow-BTwrxOoX.d.ts} +9 -2
  3. package/dist/{diagnostic-assembly-DRunIcKQ.js → diagnostic-assembly-B-6vIA5I.js} +3 -1
  4. package/dist/{diagnostic-assembly-DRunIcKQ.js.map → diagnostic-assembly-B-6vIA5I.js.map} +1 -1
  5. package/dist/index.d.ts +3 -3
  6. package/dist/index.js +1 -1
  7. package/dist/mappers/model-adapter.js +127 -1
  8. package/dist/mappers/model-adapter.js.map +1 -0
  9. package/dist/mappers/schema-adapter.d.ts +5 -1
  10. package/dist/mappers/schema-adapter.js +44 -11
  11. package/dist/mappers/schema-adapter.js.map +1 -1
  12. package/dist/mappers/types.js +1 -1
  13. package/dist/models/agent-workflow.d.ts +2 -2
  14. package/dist/models/diagnostic.js +1 -1
  15. package/dist/models/index.d.ts +4 -4
  16. package/dist/models/index.js +1 -1
  17. package/dist/models/rstool-agent.d.ts +1 -1
  18. package/dist/models/rstool-agent.js +1 -1
  19. package/dist/models/tool-contract.d.ts +1 -1
  20. package/dist/models/tool-contract.js +1 -1
  21. package/dist/models/tool-contract.js.map +1 -1
  22. package/dist/{models-B255GaEC.js → models-LMyDB0md.js} +25 -4
  23. package/dist/models-LMyDB0md.js.map +1 -0
  24. package/dist/rsform-builder-DKwZfJvN.js +102 -0
  25. package/dist/rsform-builder-DKwZfJvN.js.map +1 -0
  26. package/dist/{rstool-agent-BGQAzFly.d.ts → rstool-agent-BrAFJcoE.d.ts} +6 -4
  27. package/dist/session/session-store.js +1 -1
  28. package/dist/{tool-contract-sQfr-DYD.d.ts → tool-contract-BUaDRHoO.d.ts} +8 -3
  29. package/dist/wrapper/stdio-wrapper.js +8 -2
  30. package/dist/wrapper/stdio-wrapper.js.map +1 -1
  31. package/docs/CONCEPTUAL-SCHEMA.md +44 -0
  32. package/docs/CONSTITUENTA.md +1 -1
  33. package/docs/DIAGNOSTICS.md +32 -31
  34. package/docs/README.md +3 -3
  35. package/package.json +19 -18
  36. package/skills/README.md +3 -3
  37. package/skills/rstool-helper/EXAMPLES.md +194 -3
  38. package/skills/rstool-helper/GUIDE.md +79 -8
  39. package/skills/rstool-helper/REFERENCE.md +192 -34
  40. package/skills/rstool-helper/SKILL.md +28 -8
  41. package/src/mappers/diagnostic-assembly.ts +3 -0
  42. package/src/mappers/rsform-builder.ts +1 -1
  43. package/src/mappers/schema-adapter.ts +48 -16
  44. package/src/models/agent-workflow.ts +8 -0
  45. package/src/models/index.ts +1 -0
  46. package/src/models/rstool-agent.test.ts +21 -0
  47. package/src/models/rstool-agent.ts +23 -0
  48. package/src/models/tool-contract.ts +8 -1
  49. package/src/wrapper/stdio-handler.ts +8 -1
  50. package/dist/model-adapter-D-YmIvQW.js +0 -225
  51. package/dist/model-adapter-D-YmIvQW.js.map +0 -1
  52. package/dist/models-B255GaEC.js.map +0 -1
@@ -1,34 +1,68 @@
1
1
  # ЯРЭ и rstool — справочник
2
2
 
3
+ Контракт library / stdio / MCP для агентов. Воркфлоу — [GUIDE.md](GUIDE.md). Примеры — [EXAMPLES.md](EXAMPLES.md). Язык — `docs/*.md`.
4
+
3
5
  ## Контракт rstool
4
6
 
5
7
  - Пакет: `@rsconcept/rstool`
6
- - Версия контракта: `3.0.0` (`CONTRACT_VERSION`)
8
+ - Версия контракта: `3.1.0` (`CONTRACT_VERSION`)
7
9
  - Основной класс: `RSToolAgent`
8
10
  - Публичные импорты: `@rsconcept/rstool` и `@rsconcept/rstool/wrapper`
11
+ - Опции: `new RSToolAgent({ persistenceDir? })` — каталог для сохранения сессий между перезапусками (stdio/MCP: `RSTOOL_PERSISTENCE_DIR`)
12
+
13
+ Почти все методы принимают optional `sessionId` вторым аргументом (в stdio/MCP — поле `sessionId` в плоских `params`). Без него используется текущая сессия; при необходимости сессия создаётся лениво.
9
14
 
10
15
  ### Методы library API
11
16
 
12
- | Метод | Назначение |
13
- | ---------------------------------------------- | ------------------------------------------------------------------ |
14
- | `ensureSession(initial?)` | Вернуть текущую сессию или создать новую |
15
- | `createSession(initial?)` | Новая in-memory сессия |
16
- | `getCurrentSession()` | Текущая активная сессия или `null` |
17
- | `setCurrentSession(sessionId)` | Сделать сессию текущей |
18
- | `applySchemaPatch(input, sessionId?)` | Единственный путь правки схемы |
19
- | `getSessionState(detail?, sessionId?)` | `summary` (default) или `full` |
20
- | `listDiagnostics(filters?, sessionId?)` | Активные диагностики (`kind`: `expression` \| `schema` \| `model`) |
21
- | `analyzeExpression(input, sessionId?)` | Разбор без сохранения (`recordDiagnostics?`) |
22
- | `commitStep(message?, sessionId?)` | Ревизия |
23
- | `exportSession(sessionId?)` | JSON сессии |
24
- | `exportPortal({ kind, format? }, sessionId?)` | Portal Load from JSON |
25
- | `importData(payload, kind?)` | Импорт сессии / Portal (`kind` см. ниже) |
26
- | `setModelValues({ set?, clear? }, sessionId?)` | Значения модели (async — нужен `await`) |
27
- | `getModelState(sessionId?)` | Состояние интерпретации |
28
- | `evaluate(input, sessionId?)` | Scratch или конституента |
29
- | `recalculateModel(sessionId?)` | Пересчёт |
30
-
31
- ### `applySchemaPatch`
17
+ | Метод | Назначение |
18
+ | ---------------------------------------------- | ------------------------------------------- |
19
+ | `ensureSession(initial?)` | Вернуть текущую сессию или создать новую |
20
+ | `createSession(initial?)` | Новая in-memory сессия (становится текущей) |
21
+ | `getCurrentSession()` | Текущая активная сессия или `null` |
22
+ | `setCurrentSession(sessionId)` | Сделать сессию текущей |
23
+ | `applySchemaPatch(input, sessionId?)` | Единственный путь правки схемы |
24
+ | `getSessionState(detail?, sessionId?)` | `summary` (default) или `full` |
25
+ | `listDiagnostics(filters?, sessionId?)` | Активные диагностики |
26
+ | `analyzeExpression(input, sessionId?)` | Разбор без сохранения |
27
+ | `commitStep(message?, sessionId?)` | Ревизия |
28
+ | `exportSession(sessionId?)` | JSON-строка сессии |
29
+ | `exportPortal({ kind, format? }, sessionId?)` | Portal Load from JSON |
30
+ | `importData(payload, kind?)` | Импорт сессии / Portal |
31
+ | `setModelValues({ set?, clear? }, sessionId?)` | Значения модели (**async** — `await`) |
32
+ | `getModelState(sessionId?)` | Состояние интерпретации |
33
+ | `evaluate(input, sessionId?)` | Scratch или конституента |
34
+ | `recalculateModel(sessionId?)` | Пересчёт производных |
35
+ | `restoreOrder(sessionId?)` | Упорядочить конституенты (топология + семантика) |
36
+
37
+ Служебные (stdio / MCP): `ping` / `pong`+`contractVersion`; `methods` / `list_methods`.
38
+
39
+ ### Library ↔ stdio ↔ MCP
40
+
41
+ Stdio использует **camelCase** имён методов (как library). MCP (`@rsconcept/rstool-mcp`) — **snake_case**. Params везде **плоские** (без обёртки `input`).
42
+
43
+ | Library / stdio | MCP tool |
44
+ | ------------------- | --------------------- |
45
+ | `ping` | `ping` |
46
+ | `methods` | `list_methods` |
47
+ | `ensureSession` | `ensure_session` |
48
+ | `createSession` | `create_session` |
49
+ | `getCurrentSession` | `get_current_session` |
50
+ | `setCurrentSession` | `set_current_session` |
51
+ | `applySchemaPatch` | `apply_schema_patch` |
52
+ | `getSessionState` | `get_session_state` |
53
+ | `listDiagnostics` | `list_diagnostics` |
54
+ | `analyzeExpression` | `analyze_expression` |
55
+ | `commitStep` | `commit_step` |
56
+ | `exportSession` | `export_session` |
57
+ | `exportPortal` | `export_portal` |
58
+ | `importData` | `import_data` |
59
+ | `setModelValues` | `set_model_values` |
60
+ | `getModelState` | `get_model_state` |
61
+ | `evaluate` | `evaluate` |
62
+ | `recalculateModel` | `recalculate_model` |
63
+ | `restoreOrder` | `restore_order` |
64
+
65
+ ## `applySchemaPatch`
32
66
 
33
67
  ```ts
34
68
  tool.applySchemaPatch({
@@ -38,23 +72,132 @@ tool.applySchemaPatch({
38
72
  });
39
73
  ```
40
74
 
41
- - `items` упорядочиваются автоматически: поставщики раньше потребителей.
42
- - `id` и `cstType` можно опускать — выводятся из префикса `alias` (`X/C/S/D/F/P/A/T/N`).
43
- - `mode`: `'atomic'` (default; откат всей пачки при первой ошибке) или `'best_effort'` (применяются корректные элементы).
44
- - `commitMessage` фиксирует ревизию после успешного patch.
75
+ **Вход**
76
+
77
+ | Поле | Смысл |
78
+ | ---------------- | ------------------------------------------------------------------------------------------------------------ |
79
+ | `items` | Массив патчей; у каждого обязателен `alias` |
80
+ | `initial?` | `alias` / `title` / `comment` — только если для patch создаётся новая сессия |
81
+ | `mode?` | `'atomic'` (default) — откат всей пачки при первой ошибке; `'best_effort'` — применяются корректные элементы |
82
+ | `commitMessage?` | После успешного patch фиксирует ревизию |
83
+
84
+ **Патч элемента:** `alias` обязателен; `id`, `cstType`, `definitionFormal`, `term`, `definitionText`, `convention` опциональны. `id` и `cstType` выводятся из префикса `alias` (`X/C/S/D/F/P/A/T/N`). Поля мержатся с существующей конституентой.
85
+
86
+ **Поведение:** `items` упорядочиваются автоматически (поставщики раньше потребителей).
87
+
88
+ **Результат:** `{ success, applied, failed, diagnostics, session, summary, revision? }`.
89
+
90
+ - `applied` — успешно применённые конституенты со `analysis`.
91
+ - `failed` — `{ draft, diagnostics }[]` (при `best_effort` или атомарном отказе).
92
+ - `summary` — компактный обзор сессии (см. ниже).
93
+ - `revision` — если был `commitMessage`.
94
+
95
+ Для крупных импортов с частичными ошибками формул предпочитай `mode: 'best_effort'`, затем правь оставшееся по `failed` / `listDiagnostics`.
96
+
97
+ ## `getSessionState`
98
+
99
+ - `getSessionState()` / `getSessionState('summary')` → `SessionSummary`:
100
+ - `sessionId`, `contractVersion`, `alias`, `title`, `comment`
101
+ - `itemCount`, `modelItemCount`, `diagnosticsCount`
102
+ - `items[]`: `{ id, alias, cstType, analysisSuccess }`
103
+ - `diagnostics[]` (активные)
104
+ - `lastRevision?`
105
+ - `getSessionState('full')` → полный `SessionState` (все конституенты с формулами, модель, ревизии). Для обзора агенту обычно хватает `summary`.
106
+
107
+ ## `listDiagnostics`
108
+
109
+ ```ts
110
+ tool.listDiagnostics();
111
+ tool.listDiagnostics({ kind: 'expression' });
112
+ tool.listDiagnostics({ kind: 'schema', constituentId: 3, severity: 'error' });
113
+ ```
114
+
115
+ Фильтры: `kind?: 'expression' | 'schema' | 'model'`, `constituentId?`, `severity?: 'error' | 'warning'`.
116
+
117
+ Элемент диагностики (агентский вид): `kind`, `code`, `name`, `severity`, `alias?`, `expression`, `from`, `to`, `params?`. Ключ для поиска исправления — `name` ([DIAGNOSTICS.md](../../docs/DIAGNOSTICS.md)).
118
+
119
+ ## `analyzeExpression`
120
+
121
+ ```ts
122
+ tool.analyzeExpression({
123
+ expression: 'Pr1(S1)',
124
+ cstType: CstType.TERM,
125
+ recordDiagnostics: false // default
126
+ });
127
+ ```
128
+
129
+ **Вход:** `expression`, `cstType` (обязательны); `recordDiagnostics?` — писать ли диагностики в сессию (default `false`).
130
+
131
+ **Результат:** `{ success, type, valueClass, diagnostics }`. Не сохраняет конституенту. Для черновика формулы — до `applySchemaPatch`.
132
+
133
+ ## `evaluate`
134
+
135
+ Ровно один вариант входа:
136
+
137
+ - конституента: `{ constituentId }`
138
+ - scratch: `{ expression, cstType }`
139
+
140
+ **Результат:** `{ success, value, status, iterations, cacheHits, diagnostics }`.
141
+
142
+ `status` — статус вычисления КМ (`HAS_DATA`, `EMPTY`, `AXIOM_FALSE`, `EVAL_FAIL`, … — [DOMAIN.md](../../docs/DOMAIN.md)).
143
+
144
+ ## `setModelValues` / `getModelState` / `recalculateModel`
145
+
146
+ ```ts
147
+ await tool.setModelValues({
148
+ set: [{ target: 1, value: { 0: 'a', 1: 'b' } }],
149
+ clear: [3]
150
+ });
151
+ ```
152
+
153
+ - `set[]`: `{ target, value, type? }` — `target` = id конституенты; `type` обычно не нужен.
154
+ - `clear[]`: id для сброса значений.
155
+ - Метод **async**. После изменения данных пересчитываются диагностики модели.
156
+ - `getModelState()` → `{ items: [{ id, type, value }] }`.
157
+ - `recalculateModel()` → `{ items: [{ id, alias, value, status }] }`.
158
+
159
+ Формы `value` для `X#` / `S#` — [MODEL-TESTING.md](../../docs/MODEL-TESTING.md).
160
+
161
+ ## `restoreOrder`
162
+
163
+ Упорядочивает конституенты сессии одним проходом Kahn:
164
+
165
+ - формальные зависимости — жёстко (поставщик раньше зависимого);
166
+ - семантические дети — сразу после родителя, если уже готовы;
167
+ - иначе — стабильно относительно приоритета типов/ядра.
168
+
169
+ ```ts
170
+ const { orderedAliases } = tool.restoreOrder();
171
+ // → { orderedIds: number[], orderedAliases: string[] }
172
+ ```
173
+
174
+ ## `importData` / `exportSession` / `exportPortal`
45
175
 
46
176
  ### `importData(payload, kind?)`
47
177
 
48
- `kind`: `'auto'` (default; определяет формат по содержимому), `'session'`, `'portal-details'` (ответ `GET /api/rsforms/:id/details`), `'portal-schema'` (Portal Load from JSON). После импорта новая сессия становится текущей.
178
+ `kind`: `'auto'` (default), `'session'`, `'portal-details'` (`GET /api/rsforms/:id/details`), `'portal-schema'` (Portal Load from JSON). После импорта новая сессия становится текущей. Возвращает `SessionHandle`.
49
179
 
50
- ### Протокол stdio
180
+ Portal JSON из `/details` **не** является `exportSession` — не передавай его с `kind: 'session'`.
51
181
 
52
- Плоские `params` (без `input`). Опционально: переменная окружения `RSTOOL_PERSISTENCE_DIR` — каталог для сохранения сессий между перезапусками обёртки (в library API — `new RSToolAgent({ persistenceDir })`).
182
+ ### `exportSession()`
53
183
 
54
- Служебные методы:
184
+ JSON-строка сессии для последующего `importData(..., 'session')`.
55
185
 
56
- - `ping` — liveness и `contractVersion`.
57
- - `methods` — список доступных stdio-методов.
186
+ ### `exportPortal({ kind, format? })`
187
+
188
+ - `kind`: `'schema'` | `'model'`
189
+ - `format?`: `'json'` (default, строка) | `'object'`
190
+ - Для загрузки в Portal UI: обычно `exportPortal({ kind: 'schema' })`.
191
+
192
+ ## `commitStep` / сессии
193
+
194
+ - `commitStep(message?)` → `{ revisionId, at, message? }`.
195
+ - `ensureSession` / `createSession` принимают optional `initial`: `alias`, `title`, `comment` (и прочие поля `SessionState` в library).
196
+ - `SessionHandle`: `{ sessionId, contractVersion }`.
197
+
198
+ ## Протокол stdio
199
+
200
+ Плоские `params`. Переменная окружения `RSTOOL_PERSISTENCE_DIR` — каталог персистентности.
58
201
 
59
202
  ```json
60
203
  {"id":"2","method":"applySchemaPatch","params":{"items":[{"alias":"X1"}]}}
@@ -62,8 +205,23 @@ tool.applySchemaPatch({
62
205
  {"id":"8","method":"evaluate","params":{"constituentId":3}}
63
206
  {"id":"9","method":"exportPortal","params":{"kind":"schema"}}
64
207
  {"id":"10","method":"importData","params":{"payload":"..."}}
208
+ {"id":"11","method":"getSessionState","params":{"detail":"summary"}}
65
209
  ```
66
210
 
67
- ## Документация
68
-
69
- Воркфлоу: `GUIDE.md`. Примеры: `EXAMPLES.md`. Язык: `docs/*.md`.
211
+ ## Help map: ошибка → документ
212
+
213
+ | Ситуация | Куда |
214
+ | -------------------------------------------------- | ------------------------------------------------------- |
215
+ | Код / `name` диагностики | [DIAGNOSTICS.md](../../docs/DIAGNOSTICS.md) |
216
+ | Синтаксис операторов, `D{}`/`I{}`/`R{}`, `F#`/`P#` | [SYNTAX.md](../../docs/SYNTAX.md) |
217
+ | Ступени, `AnalysisResult.type` | [TYPIFICATION.md](../../docs/TYPIFICATION.md) |
218
+ | `X#` vs `C#` vs `Z`, переносимость | [BASE-SELECTION.md](../../docs/BASE-SELECTION.md) |
219
+ | Проектирование, `F#` vs пары, ревью | [CONCEPTUAL-SCHEMA.md](../../docs/CONCEPTUAL-SCHEMA.md) |
220
+ | Поля и валидация конституенты | [CONSTITUENTA.md](../../docs/CONSTITUENTA.md) |
221
+ | Формы значений КМ, тесты | [MODEL-TESTING.md](../../docs/MODEL-TESTING.md) |
222
+ | Ссылка Portal, REST, curl | [PORTAL-API.md](../../docs/PORTAL-API.md) |
223
+ | Термины, статусы, «не путать» | [DOMAIN.md](../../docs/DOMAIN.md) |
224
+ | Воркфлоу агента | [GUIDE.md](GUIDE.md) |
225
+ | Сниппеты и антипаттерны | [EXAMPLES.md](EXAMPLES.md) |
226
+
227
+ Исходники кодов ошибок ЯРЭ: `@rsconcept/domain` (`RSErrorCode`, `error.ts`). Не дублируй грамматику в skill — правь domain и при необходимости этот help map.
@@ -1,7 +1,9 @@
1
1
  ---
2
2
  name: rstool-helper
3
3
  description: >-
4
- ЯРЭ (язык родоструктурной экспликации), формальные определения и проектирование концептуальных схем для агентов. Читай GUIDE и docs из node_modules.
4
+ ЯРЭ и концептуальные схемы через rstool: концептуализация, ревью/оценка КС,
5
+ правки и диагностики формул, проверка на КМ (evaluate), выбор X#/C#/Z,
6
+ импорт/экспорт Portal (/rsforms, /details). Читай GUIDE и docs из node_modules.
5
7
  ---
6
8
 
7
9
  # rstool-helper
@@ -16,15 +18,32 @@ description: >-
16
18
 
17
19
  Затем продолжай с каноническими файлами ниже.
18
20
 
19
- ## Канонические файлы (читать перед работой с rstool)
21
+ ## Канонические файлы
20
22
 
21
- - **Начни здесь** — воркфлоу и чеклисты: `node_modules/@rsconcept/rstool/skills/rstool-helper/GUIDE.md`
22
- - API, stdio, контракт: `node_modules/@rsconcept/rstool/skills/rstool-helper/REFERENCE.md`
23
- - Примеры, типичные ошибки: `node_modules/@rsconcept/rstool/skills/rstool-helper/EXAMPLES.md`
24
- - Язык и предметная область: `node_modules/@rsconcept/rstool/docs/*.md`
25
- - Процедура установки: `node_modules/@rsconcept/rstool/skills/INSTALL.md`
23
+ Префикс путей: `node_modules/@rsconcept/rstool/`.
26
24
 
27
- Перед началом задачи rstool всегда открывай **GUIDE.md**, затем по необходимости **REFERENCE.md** / **EXAMPLES.md** / нужные `docs/*.md`.
25
+ | Что | Путь |
26
+ | :-------------------------------------------------- | :---------------------------------- |
27
+ | **Начни здесь** — матрица задач, воркфлоу, чеклисты | `skills/rstool-helper/GUIDE.md` |
28
+ | API, stdio, MCP, контракт | `skills/rstool-helper/REFERENCE.md` |
29
+ | Примеры, антипаттерны | `skills/rstool-helper/EXAMPLES.md` |
30
+ | Язык и предметная область | `docs/*.md` |
31
+ | Установка skill | `skills/INSTALL.md` |
32
+
33
+ ## Задача → что читать
34
+
35
+ Не читай все `docs/` подряд. Открой **GUIDE.md** (раздел «Задача → чтение»), затем только нужные файлы:
36
+
37
+ | Задача | Минимум |
38
+ | :-------------------------------- | :----------------------------------------------------------------------------- |
39
+ | Собрать / развить КС из текста | GUIDE: концептуализация; `docs/CONCEPTUAL-SCHEMA.md`; `docs/BASE-SELECTION.md` |
40
+ | Оценить / отревьюить готовую КС | GUIDE: ревью; чеклист ревью; `docs/CONCEPTUAL-SCHEMA.md` |
41
+ | Починить формулу / диагностики | GUIDE: цикл диагностик; `docs/DIAGNOSTICS.md` |
42
+ | Импорт / правка по ссылке Portal | GUIDE: Portal; `docs/PORTAL-API.md` |
43
+ | Проверить смысл на данных | GUIDE: КМ; `docs/MODEL-TESTING.md` |
44
+ | Синтаксис / типизация / вызов API | `docs/SYNTAX.md` / `TYPIFICATION.md` / `REFERENCE.md` |
45
+
46
+ Перед работой всегда открой **GUIDE.md**; остальное — по строке таблицы.
28
47
 
29
48
  ## Не правь пакет rstool без запроса
30
49
 
@@ -41,4 +60,5 @@ REST API и curl — `node_modules/@rsconcept/rstool/docs/PORTAL-API.md`.
41
60
  ## Кратко
42
61
 
43
62
  - Пакет: `@rsconcept/rstool`; обёртка: `npx rstool-wrapper`
63
+ - MCP: `@rsconcept/rstool-mcp` (имена tools — `snake_case`, см. REFERENCE)
44
64
  - `@rsconcept/domain` устанавливается как зависимость (анализатор и коды ошибок)
@@ -90,6 +90,9 @@ function expressionForCstDiagnostic(cst: Constituenta, diagnostic: CstDiagnostic
90
90
  return cst.convention;
91
91
  case RSDiagnosticCode.schemaTypeMismatch:
92
92
  return cst.typification_manual;
93
+ case RSDiagnosticCode.schemaDependencyCycle:
94
+ case RSDiagnosticCode.schemaFormalDuplicate:
95
+ return cst.definition_formal;
93
96
  default:
94
97
  return cst.definition_formal;
95
98
  }
@@ -75,7 +75,7 @@ export function enrichSessionConstituents(session: SessionState): EnrichedConsti
75
75
  }
76
76
  }
77
77
 
78
- assignSchemaDiagnostics(schema.items, resolveAlias, normalizedDefinitions);
78
+ assignSchemaDiagnostics(schema.items, resolveAlias, normalizedDefinitions, schema.graph);
79
79
 
80
80
  return session.items.map(item => ({
81
81
  item,
@@ -1,6 +1,6 @@
1
- import { RSLangAnalyzer, type AnalysisFull, type ValueClass } from '@rsconcept/domain/rslang';
2
1
  import { getAnalysisFor } from '@rsconcept/domain/library/rsform-api';
3
- import { CstType, type RSForm } from '@rsconcept/domain/library/rsform';
2
+ import { type RSForm } from '@rsconcept/domain/library/rsform';
3
+ import { RSLangAnalyzer } from '@rsconcept/domain/rslang';
4
4
 
5
5
  import {
6
6
  type AnalysisResult,
@@ -9,6 +9,7 @@ import {
9
9
  type Diagnostic,
10
10
  type SessionState
11
11
  } from '../models';
12
+ import { buildRSFormFromSession } from './rsform-builder';
12
13
  import { toPublicAnalysis } from './types';
13
14
 
14
15
  export class SchemaAdapter {
@@ -16,9 +17,8 @@ export class SchemaAdapter {
16
17
  session: SessionState,
17
18
  draft: ConstituentaDraft
18
19
  ): { result: AnalysisResult; diagnostics: Diagnostic[] } {
19
- const analyzer = this.buildAnalyzer(session);
20
- const schema = this.toPseudoRSFormState(session, analyzer);
21
- const analysis = getAnalysisFor(draft.definitionFormal, draft.cstType, schema as unknown as RSForm, draft.alias);
20
+ const schema = this.toAnalysisSchema(session, draft);
21
+ const analysis = getAnalysisFor(draft.definitionFormal, draft.cstType, schema, draft.alias);
22
22
  const target = { constituentId: draft.id, alias: draft.alias };
23
23
  const result = toPublicAnalysis(
24
24
  {
@@ -70,18 +70,50 @@ export class SchemaAdapter {
70
70
  };
71
71
  }
72
72
 
73
- private buildAnalyzer(session: SessionState): RSLangAnalyzer {
74
- const analyzer = new RSLangAnalyzer();
75
- for (const item of session.items) {
76
- if (item.cstType === CstType.BASE) {
77
- analyzer.addBase(item.alias);
73
+ /**
74
+ * Schema view for analysis: session items with the draft expression merged in so the
75
+ * dependency graph (and cycle diagnostics) reflect the expression under check.
76
+ */
77
+ private toAnalysisSchema(session: SessionState, draft: ConstituentaDraft): RSForm {
78
+ const items = session.items.map(item => {
79
+ if (item.id !== draft.id) {
80
+ return item;
78
81
  }
79
- analyzer.setGlobal(
80
- item.alias,
81
- item.analysis.type as AnalysisFull['type'],
82
- item.analysis.valueClass as ValueClass | null
83
- );
82
+ return {
83
+ ...item,
84
+ alias: draft.alias,
85
+ cstType: draft.cstType,
86
+ definitionFormal: draft.definitionFormal,
87
+ term: draft.term ?? item.term,
88
+ definitionText: draft.definitionText ?? item.definitionText,
89
+ convention: draft.convention ?? item.convention,
90
+ // Drop cached type so mutual references surface as untyped → cycle.
91
+ analysis: {
92
+ ...item.analysis,
93
+ success: false,
94
+ type: null,
95
+ valueClass: null
96
+ }
97
+ };
98
+ });
99
+ if (!items.some(item => item.id === draft.id)) {
100
+ items.push({
101
+ id: draft.id,
102
+ alias: draft.alias,
103
+ cstType: draft.cstType,
104
+ definitionFormal: draft.definitionFormal,
105
+ term: draft.term ?? '',
106
+ definitionText: draft.definitionText ?? '',
107
+ convention: draft.convention ?? '',
108
+ analysis: {
109
+ success: false,
110
+ type: null,
111
+ valueClass: null,
112
+ diagnostics: []
113
+ }
114
+ });
84
115
  }
85
- return analyzer;
116
+
117
+ return buildRSFormFromSession({ ...session, items });
86
118
  }
87
119
  }
@@ -35,6 +35,14 @@ export interface ApplySchemaPatchInput {
35
35
  commitMessage?: string;
36
36
  }
37
37
 
38
+ /** Result of {@link RSToolAgent.restoreOrder}. */
39
+ export interface RestoreOrderResult {
40
+ /** Constituent ids in the restored declaration order. */
41
+ orderedIds: number[];
42
+ /** Aliases in the same order as {@link orderedIds}. */
43
+ orderedAliases: string[];
44
+ }
45
+
38
46
  /** Compact view of one constituent in a session summary. */
39
47
  export interface SessionSummaryItem {
40
48
  id: number;
@@ -2,6 +2,7 @@ export {
2
2
  type AgentConstituentaPatch,
3
3
  type ApplySchemaPatchInput,
4
4
  type ApplySchemaPatchResult,
5
+ type RestoreOrderResult,
5
6
  type SessionStateDetail,
6
7
  type SessionStateResult,
7
8
  type SessionSummary,
@@ -877,6 +877,27 @@ describe('RSToolAgent import and export', () => {
877
877
  expect(exported.items.map(item => item.alias)).toEqual(['D1', 'S1', 'C1', 'X1']);
878
878
  });
879
879
 
880
+ it('restoreOrder reorders by formal topology and kernel priority', () => {
881
+ const tool = new RSToolAgent();
882
+ const session = tool.createSession({ title: 'Restore', alias: 'RO' });
883
+ tool.applySchemaPatch(
884
+ {
885
+ items: [
886
+ { alias: 'D1', cstType: CstType.TERM, definitionFormal: 'Pr1(S1)\\X1' },
887
+ { alias: 'S1', cstType: CstType.STRUCTURED, definitionFormal: 'ℬ(X1×X1)' },
888
+ { alias: 'A2', cstType: CstType.AXIOM, definitionFormal: 'P1[S1]' },
889
+ { alias: 'P1', cstType: CstType.PREDICATE, definitionFormal: '[α∈ℬ(R1)] card(α)=0' },
890
+ { alias: 'X1', cstType: CstType.BASE, definitionFormal: '' }
891
+ ]
892
+ },
893
+ session.sessionId
894
+ );
895
+
896
+ const result = tool.restoreOrder(session.sessionId);
897
+ expect(result.orderedAliases.slice(0, 5)).toEqual(['X1', 'P1', 'S1', 'A2', 'D1']);
898
+ expect(fullState(tool, session.sessionId).items.map(item => item.alias)).toEqual(result.orderedAliases);
899
+ });
900
+
880
901
  it('round-trips portal schema export and import', () => {
881
902
  const tool = new RSToolAgent();
882
903
  const session = tool.createSession({ title: 'Round trip', alias: 'RT' });
@@ -1,3 +1,4 @@
1
+ import { restoreConstituentOrder } from '@rsconcept/domain/library/rsform-api';
1
2
  import {
2
3
  portalDetailsToDrafts,
3
4
  portalDetailsToSessionSeed,
@@ -13,6 +14,7 @@ import {
13
14
  type AgentConstituentaPatch,
14
15
  type ApplySchemaPatchInput,
15
16
  type ApplySchemaPatchResult,
17
+ type RestoreOrderResult,
16
18
  type SessionStateDetail,
17
19
  type SessionStateResult,
18
20
  type SessionSummary
@@ -303,6 +305,27 @@ export class RSToolAgent implements RSToolAgentContract {
303
305
  return result;
304
306
  }
305
307
 
308
+ /** @inheritdoc */
309
+ public restoreOrder(sessionId?: string): RestoreOrderResult {
310
+ const id = this.resolveSessionId(sessionId);
311
+ const envelope = this.sessions.get(id);
312
+ const orderable = envelope.state.items.map(item => ({
313
+ id: item.id,
314
+ alias: item.alias,
315
+ cst_type: item.cstType,
316
+ definition_formal: item.definitionFormal
317
+ }));
318
+ const ordered = restoreConstituentOrder(orderable);
319
+ const byId = new Map(envelope.state.items.map(item => [item.id, item]));
320
+ envelope.state.items = ordered.map(item => byId.get(item.id)!);
321
+ envelope.state.updatedAt = new Date().toISOString();
322
+ this.sessions.replaceState(id, envelope.state);
323
+ return {
324
+ orderedIds: ordered.map(item => item.id),
325
+ orderedAliases: ordered.map(item => item.alias)
326
+ };
327
+ }
328
+
306
329
  private addOrUpdateConstituenta(
307
330
  input: AddOrUpdateConstituentaInput,
308
331
  sessionId?: string
@@ -1,6 +1,7 @@
1
1
  import {
2
2
  type ApplySchemaPatchInput,
3
3
  type ApplySchemaPatchResult,
4
+ type RestoreOrderResult,
4
5
  type SessionStateDetail,
5
6
  type SessionStateResult
6
7
  } from './agent-workflow';
@@ -12,7 +13,7 @@ import { type RecalculateModelResult, type SessionModelState, type SetModelValue
12
13
  import { type SessionHandle, type SessionRevision, type SessionState } from './session';
13
14
 
14
15
  /** Agent-visible contract version; bump on breaking API changes. */
15
- export const CONTRACT_VERSION = '3.0.0';
16
+ export const CONTRACT_VERSION = '3.1.0';
16
17
 
17
18
  /** Options for constructing an {@link RSToolAgent}. */
18
19
  export interface RSToolAgentOptions {
@@ -85,4 +86,10 @@ export interface RSToolAgentContract {
85
86
 
86
87
  /** Recompute derived model values for all constituents. */
87
88
  recalculateModel(sessionId?: string): RecalculateModelResult;
89
+
90
+ /**
91
+ * Restore declaration order: formal topology, semantic clusters when ready,
92
+ * otherwise stable against type/kernel preferred ranks.
93
+ */
94
+ restoreOrder(sessionId?: string): RestoreOrderResult;
88
95
  }
@@ -33,7 +33,8 @@ export const STDIO_METHODS = [
33
33
  'setModelValues',
34
34
  'getModelState',
35
35
  'evaluate',
36
- 'recalculateModel'
36
+ 'recalculateModel',
37
+ 'restoreOrder'
37
38
  ] as const;
38
39
 
39
40
  function asObject(value: unknown): Record<string, unknown> {
@@ -171,6 +172,12 @@ export async function handleStdioRequest(tool: RSToolAgent, request: StdioReques
171
172
  ok: true,
172
173
  result: tool.recalculateModel(optionalSessionId(params))
173
174
  };
175
+ case 'restoreOrder':
176
+ return {
177
+ id: request.id,
178
+ ok: true,
179
+ result: tool.restoreOrder(optionalSessionId(params))
180
+ };
174
181
  default:
175
182
  return {
176
183
  id: request.id ?? null,