@rsconcept/rstool 1.7.1 → 2.0.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 (126) hide show
  1. package/README.md +7 -7
  2. package/dist/index.js +1 -1
  3. package/dist/mappers/model-adapter.d.ts +6 -2
  4. package/dist/mappers/model-adapter.js +28 -3
  5. package/dist/mappers/model-adapter.js.map +1 -1
  6. package/dist/mappers/schema-adapter.d.ts +1 -2
  7. package/dist/mappers/schema-adapter.js +1 -1
  8. package/dist/mappers/types.d.ts +4 -5
  9. package/dist/models/index.js +1 -1
  10. package/dist/models/rstool-agent.js +1 -2
  11. package/dist/{models-DG2uTaV_.js → models-D06g8LId.js} +6 -5
  12. package/dist/{models-DG2uTaV_.js.map → models-D06g8LId.js.map} +1 -1
  13. package/dist/{rsform-builder-DKwZfJvN.js → rsform-builder-BlXEe5Ow.js} +2 -2
  14. package/dist/{rsform-builder-DKwZfJvN.js.map → rsform-builder-BlXEe5Ow.js.map} +1 -1
  15. package/dist/session/session-store.d.ts +3 -3
  16. package/dist/session/session-store.js +1 -2
  17. package/dist/wrapper/client.d.ts +15 -9
  18. package/dist/wrapper/client.js +79 -12
  19. package/dist/wrapper/client.js.map +1 -1
  20. package/dist/wrapper/stdio-wrapper.d.ts +1 -1
  21. package/dist/wrapper/stdio-wrapper.js +18 -10
  22. package/dist/wrapper/stdio-wrapper.js.map +1 -1
  23. package/docs/BRIEF.md +93 -0
  24. package/docs/CONCEPTUAL-SCHEMA.md +17 -2
  25. package/docs/CONSTRUCTIONS.md +384 -0
  26. package/docs/DIAGNOSTICS.md +9 -8
  27. package/docs/DOMAIN.md +12 -0
  28. package/docs/METHOD.md +208 -0
  29. package/docs/MODEL-TESTING.md +48 -0
  30. package/docs/PORTAL-API.md +38 -285
  31. package/docs/PORTAL-MODELS.md +24 -0
  32. package/docs/PORTAL-OSS.md +56 -0
  33. package/docs/PORTAL-PUBLISH.md +39 -0
  34. package/docs/PORTAL-READ.md +301 -0
  35. package/docs/README.md +8 -4
  36. package/docs/REQUIREMENTS.md +61 -0
  37. package/docs/SYNTAX.md +5 -4
  38. package/docs/TYPIFICATION.md +12 -3
  39. package/docs/brief.schema.json +173 -0
  40. package/examples/README.md +16 -2
  41. package/examples/access-control/NOTES.md +23 -0
  42. package/examples/access-control/anti/collapsed.json +439 -0
  43. package/examples/access-control/anti/weakened.json +439 -0
  44. package/examples/access-control/brief-v1.json +231 -0
  45. package/examples/access-control/brief.json +310 -0
  46. package/examples/access-control/build-rsform.ts +151 -0
  47. package/examples/access-control/requirements-v1.md +9 -0
  48. package/examples/access-control/requirements-v2.md +4 -0
  49. package/examples/access-control/rsform-session-v1.json +439 -0
  50. package/examples/access-control/rsform-session.json +523 -0
  51. package/examples/audit-diagnostics.ts +9 -1
  52. package/examples/briefs.test.ts +126 -0
  53. package/examples/clinic-booking/NOTES.md +24 -0
  54. package/examples/clinic-booking/anti/collapsed.json +615 -0
  55. package/examples/clinic-booking/anti/species-as-genus.json +568 -0
  56. package/examples/clinic-booking/brief-v1.json +332 -0
  57. package/examples/clinic-booking/brief.json +405 -0
  58. package/examples/clinic-booking/build-rsform.ts +174 -0
  59. package/examples/clinic-booking/requirements-v1.md +9 -0
  60. package/examples/clinic-booking/requirements-v2.md +4 -0
  61. package/examples/clinic-booking/rsform-session-v1.json +615 -0
  62. package/examples/clinic-booking/rsform-session.json +700 -0
  63. package/examples/diagnostics-utils.ts +26 -0
  64. package/examples/kinship/brief.json +337 -0
  65. package/examples/kinship/build-rsform.ts +7 -4
  66. package/examples/kinship/rsform-session.json +20 -63
  67. package/examples/kinship/rsmodel-session.json +14 -14
  68. package/examples/kinship-subordination/NOTES.md +49 -0
  69. package/examples/kinship-subordination/brief.json +161 -0
  70. package/examples/kinship-subordination/build-rsform.ts +100 -0
  71. package/examples/kinship-subordination/rsform-session.json +795 -0
  72. package/examples/library-loans/NOTES.md +46 -0
  73. package/examples/library-loans/anti/collapsed.json +481 -0
  74. package/examples/library-loans/anti/species-as-genus.json +334 -0
  75. package/examples/library-loans/anti/uncovered.json +406 -0
  76. package/examples/library-loans/anti/weakened.json +464 -0
  77. package/examples/library-loans/brief-v1.json +340 -0
  78. package/examples/library-loans/brief.json +502 -0
  79. package/examples/library-loans/build-rsform.ts +239 -0
  80. package/examples/library-loans/requirements-v1.md +11 -0
  81. package/examples/library-loans/requirements-v2.md +5 -0
  82. package/examples/library-loans/rsform-session-v1.json +481 -0
  83. package/examples/library-loans/rsform-session.json +652 -0
  84. package/examples/library-loans/source-markup.md +18 -0
  85. package/examples/movd/brief.json +220 -0
  86. package/examples/movd/build-rsform.ts +5 -2
  87. package/examples/movd/rsform-session.json +14 -46
  88. package/examples/subordination/NOTES.md +59 -0
  89. package/examples/subordination/anti/collapsed.json +556 -0
  90. package/examples/subordination/anti/single-relation.json +125 -0
  91. package/examples/subordination/anti/transitive.json +573 -0
  92. package/examples/subordination/anti/weakened.json +539 -0
  93. package/examples/subordination/brief.json +386 -0
  94. package/examples/subordination/build-rsform.ts +63 -0
  95. package/examples/subordination/constants.ts +115 -0
  96. package/examples/subordination/rsform-session.json +556 -0
  97. package/examples/subordination/source-markup.md +19 -0
  98. package/examples/subordination/source.md +40 -0
  99. package/package.json +29 -20
  100. package/skills/INSTALL.md +28 -29
  101. package/skills/README.md +25 -10
  102. package/skills/rs-core/SKILL.md +56 -0
  103. package/skills/rs-explain/SKILL.md +28 -0
  104. package/skills/rs-explicate/SKILL.md +71 -0
  105. package/skills/rs-interpret/SKILL.md +67 -0
  106. package/skills/rs-portal/SKILL.md +51 -0
  107. package/skills/rs-review/SKILL.md +79 -0
  108. package/skills/rs-scope/SKILL.md +67 -0
  109. package/skills/rs-synthesis/SKILL.md +52 -0
  110. package/skills/rstool/SKILL.md +70 -0
  111. package/skills/rstool-helper/GUIDE.md +18 -272
  112. package/skills/rstool-helper/SKILL.md +8 -55
  113. package/skills/{rstool-helper → rstool-runtime}/EXAMPLES.md +40 -12
  114. package/skills/{rstool-helper → rstool-runtime}/REFERENCE.md +28 -7
  115. package/skills/rstool-runtime/SKILL.md +96 -0
  116. package/skills/scripts/brief-check.test.ts +165 -0
  117. package/skills/scripts/brief-check.ts +826 -0
  118. package/skills/scripts/check-brief.ts +94 -0
  119. package/skills/scripts/rstool-skills.mjs +119 -0
  120. package/skills/scripts/skill-stubs.test.ts +48 -0
  121. package/src/mappers/model-adapter.ts +34 -1
  122. package/src/models/rstool-agent.test.ts +95 -0
  123. package/src/wrapper/client.test.ts +43 -4
  124. package/src/wrapper/client.ts +95 -16
  125. package/src/wrapper/stdio-handler.test.ts +43 -0
  126. package/src/wrapper/stdio-handler.ts +19 -5
@@ -1 +1 @@
1
- {"version":3,"file":"client.js","names":[],"sources":["../../src/wrapper/client.ts"],"sourcesContent":["import { spawn, type ChildProcessByStdio } from 'node:child_process';\nimport readline from 'node:readline';\nimport { type Readable, type Writable } from 'node:stream';\n\n/** One JSON line emitted by the stdio wrapper (request response or ready event). */\nexport interface WrapperResponse<T = unknown> {\n id: string | number | null;\n ok: boolean;\n result?: T;\n error?: {\n code: string;\n message: string;\n details?: unknown;\n };\n}\n\n/** Options for spawning the `rstool-wrapper` child process. */\nexport interface RSToolWrapperClientOptions {\n /** Executable to spawn. Default: `npm`. */\n command?: string;\n /** Arguments passed to `command`. Default: `['run', 'wrapper']`. */\n args?: string[];\n /** Working directory for the child process. Default: `process.cwd()`. */\n cwd?: string;\n /** Whether to run the command in a shell. Default: `true`. */\n shell?: boolean;\n}\n\n/**\n * JSON-RPC client for the `rstool-wrapper` stdio process.\n *\n * Sends one JSON request per line on stdin and reads one JSON response per line from stdout.\n */\nexport class RSToolWrapperClient {\n private process: ChildProcessByStdio<Writable, Readable, null>;\n private input: readline.Interface;\n private pending = new Map<string, (value: WrapperResponse) => void>();\n private requestCounter = 1;\n\n /**\n * @param options - Spawn configuration; defaults run `npm run wrapper` in the current directory.\n */\n public constructor(options: RSToolWrapperClientOptions = {}) {\n this.process = spawn(options.command ?? 'npm', options.args ?? ['run', 'wrapper'], {\n cwd: options.cwd ?? process.cwd(),\n shell: options.shell ?? true,\n stdio: ['pipe', 'pipe', 'inherit']\n });\n this.input = readline.createInterface({\n input: this.process.stdout,\n crlfDelay: Infinity\n });\n this.input.on('line', line => this.handleLine(line));\n }\n\n /** Block until the wrapper emits its initial `{ ready: true }` event. */\n public async waitUntilReady(): Promise<void> {\n for (;;) {\n const line = await this.readOneEvent();\n let response: WrapperResponse<{ ready: boolean }> | null = null;\n try {\n response = JSON.parse(line) as WrapperResponse<{ ready: boolean }>;\n } catch {\n continue;\n }\n if (response.ok && response.result?.ready) {\n return;\n }\n }\n }\n\n /**\n * Invoke a wrapper method and return its `result` field.\n *\n * @param method - Stdio method name (matches {@link RSToolAgentContract} operations).\n * @param params - Method parameters object.\n * @throws When the wrapper responds with `ok: false`.\n */\n public async call<T>(method: string, params: unknown = {}): Promise<T> {\n const id = String(this.requestCounter++);\n const payload = JSON.stringify({ id, method, params });\n const responsePromise = new Promise<WrapperResponse>(resolve => {\n this.pending.set(id, resolve);\n });\n this.process.stdin.write(`${payload}\\n`);\n const response = await responsePromise;\n if (!response.ok) {\n throw new Error(`${response.error?.code ?? 'UNKNOWN'}: ${response.error?.message ?? 'Request failed'}`);\n }\n return response.result as T;\n }\n\n /** Close stdin and terminate the wrapper process. */\n public async close(): Promise<void> {\n this.input.close();\n this.process.stdin.end();\n if (!this.process.killed) {\n this.process.kill();\n }\n }\n\n private async readOneEvent(): Promise<string> {\n return new Promise((resolve, reject) => {\n const onLine = (line: string) => {\n cleanup();\n resolve(line);\n };\n const onExit = () => {\n cleanup();\n reject(new Error('Wrapper exited before ready'));\n };\n const cleanup = () => {\n this.input.off('line', onLine);\n this.process.off('exit', onExit);\n };\n this.input.on('line', onLine);\n this.process.on('exit', onExit);\n });\n }\n\n private handleLine(line: string): void {\n let parsed: WrapperResponse;\n try {\n parsed = JSON.parse(line) as WrapperResponse;\n } catch {\n return;\n }\n if (parsed.id === null || parsed.id === undefined) {\n return;\n }\n const id = String(parsed.id);\n const resolver = this.pending.get(id);\n if (!resolver) {\n return;\n }\n this.pending.delete(id);\n resolver(parsed);\n }\n}\n"],"mappings":";;;;;;;;AAiCA,IAAa,sBAAb,MAAiC;CAC/B;CACA;CACA,0BAAkB,IAAI,IAA8C;CACpE,iBAAyB;;;;CAKzB,YAAmB,UAAsC,CAAC,GAAG;EAC3D,KAAK,UAAU,MAAM,QAAQ,WAAW,OAAO,QAAQ,QAAQ,CAAC,OAAO,SAAS,GAAG;GACjF,KAAK,QAAQ,OAAO,QAAQ,IAAI;GAChC,OAAO,QAAQ,SAAS;GACxB,OAAO;IAAC;IAAQ;IAAQ;GAAS;EACnC,CAAC;EACD,KAAK,QAAQ,SAAS,gBAAgB;GACpC,OAAO,KAAK,QAAQ;GACpB,WAAW;EACb,CAAC;EACD,KAAK,MAAM,GAAG,SAAQ,SAAQ,KAAK,WAAW,IAAI,CAAC;CACrD;;CAGA,MAAa,iBAAgC;EAC3C,SAAS;GACP,MAAM,OAAO,MAAM,KAAK,aAAa;GACrC,IAAI,WAAuD;GAC3D,IAAI;IACF,WAAW,KAAK,MAAM,IAAI;GAC5B,QAAQ;IACN;GACF;GACA,IAAI,SAAS,MAAM,SAAS,QAAQ,OAClC;EAEJ;CACF;;;;;;;;CASA,MAAa,KAAQ,QAAgB,SAAkB,CAAC,GAAe;EACrE,MAAM,KAAK,OAAO,KAAK,gBAAgB;EACvC,MAAM,UAAU,KAAK,UAAU;GAAE;GAAI;GAAQ;EAAO,CAAC;EACrD,MAAM,kBAAkB,IAAI,SAAyB,YAAW;GAC9D,KAAK,QAAQ,IAAI,IAAI,OAAO;EAC9B,CAAC;EACD,KAAK,QAAQ,MAAM,MAAM,GAAG,QAAQ,GAAG;EACvC,MAAM,WAAW,MAAM;EACvB,IAAI,CAAC,SAAS,IACZ,MAAM,IAAI,MAAM,GAAG,SAAS,OAAO,QAAQ,UAAU,IAAI,SAAS,OAAO,WAAW,kBAAkB;EAExG,OAAO,SAAS;CAClB;;CAGA,MAAa,QAAuB;EAClC,KAAK,MAAM,MAAM;EACjB,KAAK,QAAQ,MAAM,IAAI;EACvB,IAAI,CAAC,KAAK,QAAQ,QAChB,KAAK,QAAQ,KAAK;CAEtB;CAEA,MAAc,eAAgC;EAC5C,OAAO,IAAI,SAAS,SAAS,WAAW;GACtC,MAAM,UAAU,SAAiB;IAC/B,QAAQ;IACR,QAAQ,IAAI;GACd;GACA,MAAM,eAAe;IACnB,QAAQ;IACR,uBAAO,IAAI,MAAM,6BAA6B,CAAC;GACjD;GACA,MAAM,gBAAgB;IACpB,KAAK,MAAM,IAAI,QAAQ,MAAM;IAC7B,KAAK,QAAQ,IAAI,QAAQ,MAAM;GACjC;GACA,KAAK,MAAM,GAAG,QAAQ,MAAM;GAC5B,KAAK,QAAQ,GAAG,QAAQ,MAAM;EAChC,CAAC;CACH;CAEA,WAAmB,MAAoB;EACrC,IAAI;EACJ,IAAI;GACF,SAAS,KAAK,MAAM,IAAI;EAC1B,QAAQ;GACN;EACF;EACA,IAAI,OAAO,OAAO,QAAQ,OAAO,OAAO,QACtC;EAEF,MAAM,KAAK,OAAO,OAAO,EAAE;EAC3B,MAAM,WAAW,KAAK,QAAQ,IAAI,EAAE;EACpC,IAAI,CAAC,UACH;EAEF,KAAK,QAAQ,OAAO,EAAE;EACtB,SAAS,MAAM;CACjB;AACF"}
1
+ {"version":3,"file":"client.js","names":[],"sources":["../../src/wrapper/client.ts"],"sourcesContent":["import { spawn, type ChildProcessByStdio } from 'node:child_process';\nimport { existsSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport readline from 'node:readline';\nimport { type Readable, type Writable } from 'node:stream';\n\n/** One JSON line emitted by the stdio wrapper (request response or ready event). */\nexport interface WrapperResponse<T = unknown> {\n id: string | number | null;\n ok: boolean;\n result?: T;\n error?: {\n code: string;\n message: string;\n details?: unknown;\n };\n}\n\n/** Options for spawning the `rstool-wrapper` child process. */\nexport interface RSToolWrapperClientOptions {\n /** Executable to spawn. Default: `npm`. Passed as argv, not a shell string. */\n command?: string;\n /** Arguments passed to `command`. Default: `['run', 'wrapper']`. */\n args?: string[];\n /** Working directory for the child process. Default: `process.cwd()`. */\n cwd?: string;\n}\n\n/**\n * Rewrite Windows npm/npx shims to `node <npm-cli.js>` so spawn() never needs a shell.\n * Node 20+ refuses to exec `.cmd` files without `shell: true` (CVE-2024-27980).\n */\nfunction resolveSpawn(command: string, args: string[]): { command: string; args: string[] } {\n if (process.platform !== 'win32') {\n return { command, args };\n }\n const base = (command.replaceAll('\\\\', '/').split('/').pop() ?? command).toLowerCase();\n if (base === 'npm' || base === 'npm.cmd') {\n return { command: process.execPath, args: [bundledNpmBin('npm-cli.js'), ...args] };\n }\n if (base === 'npx' || base === 'npx.cmd') {\n return { command: process.execPath, args: [bundledNpmBin('npx-cli.js'), ...args] };\n }\n return { command, args };\n}\n\nfunction bundledNpmBin(bin: 'npm-cli.js' | 'npx-cli.js'): string {\n const cli = join(dirname(process.execPath), 'node_modules', 'npm', 'bin', bin);\n if (!existsSync(cli)) {\n throw new Error(`Cannot resolve ${bin} next to Node at ${process.execPath}`);\n }\n return cli;\n}\n\n/**\n * JSON-RPC client for the `rstool-wrapper` stdio process.\n *\n * Sends one JSON request per line on stdin and reads one JSON response per line from stdout.\n */\nexport class RSToolWrapperClient {\n private process: ChildProcessByStdio<Writable, Readable, null>;\n private input: readline.Interface;\n private pending = new Map<string, PendingCall>();\n private requestCounter = 1;\n /** Set once the child cannot start or has exited; every later request fails with this error. */\n private failure: Error | null = null;\n private closing = false;\n\n /**\n * @param options - Spawn configuration; defaults run `npm run wrapper` in the current directory.\n */\n public constructor(options: RSToolWrapperClientOptions = {}) {\n const resolved = resolveSpawn(options.command ?? 'npm', options.args ?? ['run', 'wrapper']);\n this.process = spawn(resolved.command, resolved.args, {\n cwd: options.cwd ?? process.cwd(),\n windowsHide: true,\n stdio: ['pipe', 'pipe', 'inherit']\n });\n this.input = readline.createInterface({\n input: this.process.stdout,\n crlfDelay: Infinity\n });\n this.input.on('line', line => this.handleLine(line));\n\n // Without these listeners a failed spawn (ENOENT, EACCES) or a broken stdin pipe\n // surfaces as an uncaught exception instead of a rejected promise.\n this.process.on('error', error => this.fail(error));\n this.process.stdin.on('error', error => this.fail(error));\n this.process.on('exit', (code, signal) => {\n if (this.closing) {\n this.fail(new Error('Wrapper closed'));\n return;\n }\n this.fail(new Error(`Wrapper exited before responding (code ${code ?? 'null'}, signal ${signal ?? 'null'})`));\n });\n }\n\n /**\n * Block until the wrapper emits its initial `{ ready: true }` event.\n *\n * @throws When the wrapper process cannot be started or exits before becoming ready.\n */\n public async waitUntilReady(): Promise<void> {\n for (;;) {\n const line = await this.readOneEvent();\n let response: WrapperResponse<{ ready: boolean }> | null = null;\n try {\n response = JSON.parse(line) as WrapperResponse<{ ready: boolean }>;\n } catch {\n continue;\n }\n if (response.ok && response.result?.ready) {\n return;\n }\n }\n }\n\n /**\n * Invoke a wrapper method and return its `result` field.\n *\n * @param method - Stdio method name (matches {@link RSToolAgentContract} operations).\n * @param params - Method parameters object.\n * @throws When the wrapper responds with `ok: false`, cannot be started, or exits before responding.\n */\n public async call<T>(method: string, params: unknown = {}): Promise<T> {\n if (this.failure) {\n throw this.failure;\n }\n const id = String(this.requestCounter++);\n const payload = JSON.stringify({ id, method, params });\n const responsePromise = new Promise<WrapperResponse>((resolve, reject) => {\n this.pending.set(id, { resolve, reject });\n });\n this.process.stdin.write(`${payload}\\n`);\n const response = await responsePromise;\n if (!response.ok) {\n throw new Error(`${response.error?.code ?? 'UNKNOWN'}: ${response.error?.message ?? 'Request failed'}`);\n }\n return response.result as T;\n }\n\n /** Close stdin and terminate the wrapper process. */\n public async close(): Promise<void> {\n this.closing = true;\n this.input.close();\n if (!this.process.stdin.destroyed) {\n this.process.stdin.end();\n }\n if (this.process.exitCode === null && !this.process.killed) {\n this.process.kill();\n }\n }\n\n private async readOneEvent(): Promise<string> {\n if (this.failure) {\n throw this.failure;\n }\n return new Promise((resolve, reject) => {\n const onLine = (line: string) => {\n cleanup();\n resolve(line);\n };\n const onExit = () => {\n cleanup();\n reject(this.failure ?? new Error('Wrapper exited before ready'));\n };\n const onError = (error: Error) => {\n cleanup();\n reject(error);\n };\n const cleanup = () => {\n this.input.off('line', onLine);\n this.process.off('exit', onExit);\n this.process.off('error', onError);\n };\n this.input.on('line', onLine);\n this.process.on('exit', onExit);\n this.process.on('error', onError);\n });\n }\n\n /** Records the terminal failure and rejects every outstanding request. */\n private fail(error: Error): void {\n if (this.failure) {\n return;\n }\n this.failure = error;\n const outstanding = [...this.pending.values()];\n this.pending.clear();\n for (const entry of outstanding) {\n entry.reject(error);\n }\n }\n\n private handleLine(line: string): void {\n let parsed: WrapperResponse;\n try {\n parsed = JSON.parse(line) as WrapperResponse;\n } catch {\n return;\n }\n if (parsed.id === null || parsed.id === undefined) {\n return;\n }\n const id = String(parsed.id);\n const entry = this.pending.get(id);\n if (!entry) {\n return;\n }\n this.pending.delete(id);\n entry.resolve(parsed);\n }\n}\n\ninterface PendingCall {\n resolve: (value: WrapperResponse) => void;\n reject: (reason: Error) => void;\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- export { };
1
+ export {}
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { t as RSToolAgent } from "../models-DG2uTaV_.js";
2
+ import { t as RSToolAgent } from "../models-D06g8LId.js";
3
3
  import readline from "node:readline";
4
4
  //#region src/wrapper/stdio-handler.ts
5
5
  const STDIO_METHODS = [
@@ -39,6 +39,18 @@ function omitSessionId(input) {
39
39
  const { sessionId: _sessionId, ...rest } = input;
40
40
  return rest;
41
41
  }
42
+ /** Flat `constituentId` / `kind` / `severity` params; legacy nested `filters` object is still accepted. */
43
+ function diagnosticsFilters(input) {
44
+ const source = {
45
+ ...asObject(input.filters),
46
+ ...omitSessionId(input)
47
+ };
48
+ const filters = {};
49
+ if (typeof source.constituentId === "number") filters.constituentId = source.constituentId;
50
+ if (source.kind === "expression" || source.kind === "schema" || source.kind === "model") filters.kind = source.kind;
51
+ if (source.severity === "error" || source.severity === "warning") filters.severity = source.severity;
52
+ return Object.keys(filters).length > 0 ? filters : void 0;
53
+ }
42
54
  async function handleStdioRequest(tool, request) {
43
55
  try {
44
56
  const params = asObject(request.params);
@@ -86,15 +98,11 @@ async function handleStdioRequest(tool, request) {
86
98
  ok: true,
87
99
  result: tool.getSessionState(params.detail ?? "summary", optionalSessionId(params))
88
100
  };
89
- case "listDiagnostics": {
90
- const constituentId = params.constituentId;
91
- const filters = typeof constituentId === "number" ? { constituentId } : params.filters;
92
- return {
93
- id: request.id,
94
- ok: true,
95
- result: tool.listDiagnostics(filters, optionalSessionId(params))
96
- };
97
- }
101
+ case "listDiagnostics": return {
102
+ id: request.id,
103
+ ok: true,
104
+ result: tool.listDiagnostics(diagnosticsFilters(params), optionalSessionId(params))
105
+ };
98
106
  case "analyzeExpression": return {
99
107
  id: request.id,
100
108
  ok: true,
@@ -1 +1 @@
1
- {"version":3,"file":"stdio-wrapper.js","names":[],"sources":["../../src/wrapper/stdio-handler.ts","../../src/wrapper/stdio-wrapper.ts"],"sourcesContent":["import type { RSToolAgent } from '../models/rstool-agent';\n\nexport interface StdioRequest {\n id: string | number;\n method: string;\n params?: unknown;\n}\n\nexport interface StdioResponse {\n id: string | number | null;\n ok: boolean;\n result?: unknown;\n error?: {\n code: string;\n message: string;\n details?: unknown;\n };\n}\n\nexport const STDIO_METHODS = [\n 'ensureSession',\n 'createSession',\n 'getCurrentSession',\n 'setCurrentSession',\n 'applySchemaPatch',\n 'getSessionState',\n 'listDiagnostics',\n 'analyzeExpression',\n 'commitStep',\n 'exportSession',\n 'exportPortal',\n 'importData',\n 'setModelValues',\n 'getModelState',\n 'evaluate',\n 'recalculateModel',\n 'restoreOrder',\n 'synthesize'\n] as const;\n\nfunction asObject(value: unknown): Record<string, unknown> {\n if (!value || typeof value !== 'object' || Array.isArray(value)) {\n return {};\n }\n return value as Record<string, unknown>;\n}\n\nfunction requiredString(input: Record<string, unknown>, key: string): string {\n const value = input[key];\n if (typeof value !== 'string' || value.length === 0) {\n throw new Error(`Missing or invalid \"${key}\"`);\n }\n return value;\n}\n\nfunction optionalSessionId(input: Record<string, unknown>): string | undefined {\n const value = input.sessionId;\n return typeof value === 'string' && value.length > 0 ? value : undefined;\n}\n\nfunction omitSessionId(input: Record<string, unknown>): Record<string, unknown> {\n const { sessionId: _sessionId, ...rest } = input;\n return rest;\n}\n\nexport async function handleStdioRequest(tool: RSToolAgent, request: StdioRequest): Promise<StdioResponse> {\n try {\n const params = asObject(request.params);\n switch (request.method) {\n case 'ping':\n return { id: request.id, ok: true, result: { pong: true, contractVersion: tool.contractVersion } };\n case 'methods':\n return { id: request.id, ok: true, result: STDIO_METHODS };\n case 'ensureSession':\n return {\n id: request.id,\n ok: true,\n result: tool.ensureSession(params.initial as never)\n };\n case 'createSession':\n return {\n id: request.id,\n ok: true,\n result: tool.createSession(params.initial as never)\n };\n case 'getCurrentSession':\n return {\n id: request.id,\n ok: true,\n result: tool.getCurrentSession()\n };\n case 'setCurrentSession':\n return {\n id: request.id,\n ok: true,\n result: tool.setCurrentSession(requiredString(params, 'sessionId'))\n };\n case 'applySchemaPatch':\n return {\n id: request.id,\n ok: true,\n result: tool.applySchemaPatch(omitSessionId(params) as never, optionalSessionId(params))\n };\n case 'getSessionState':\n return {\n id: request.id,\n ok: true,\n result: tool.getSessionState(\n (params.detail as 'summary' | 'full' | undefined) ?? 'summary',\n optionalSessionId(params)\n )\n };\n case 'listDiagnostics': {\n const constituentId = params.constituentId;\n const filters = typeof constituentId === 'number' ? { constituentId } : (params.filters as never);\n return {\n id: request.id,\n ok: true,\n result: tool.listDiagnostics(filters, optionalSessionId(params))\n };\n }\n case 'analyzeExpression':\n return {\n id: request.id,\n ok: true,\n result: tool.analyzeExpression(omitSessionId(params) as never, optionalSessionId(params))\n };\n case 'commitStep':\n return {\n id: request.id,\n ok: true,\n result: tool.commitStep(params.message as string | undefined, optionalSessionId(params))\n };\n case 'exportSession':\n return {\n id: request.id,\n ok: true,\n result: tool.exportSession(optionalSessionId(params))\n };\n case 'exportPortal':\n return {\n id: request.id,\n ok: true,\n result: tool.exportPortal(omitSessionId(params) as never, optionalSessionId(params))\n };\n case 'importData':\n return {\n id: request.id,\n ok: true,\n result: tool.importData(params.payload as string | object, params.kind as never)\n };\n case 'setModelValues':\n return {\n id: request.id,\n ok: true,\n result: await tool.setModelValues(omitSessionId(params) as never, optionalSessionId(params))\n };\n case 'getModelState':\n return {\n id: request.id,\n ok: true,\n result: tool.getModelState(optionalSessionId(params))\n };\n case 'evaluate':\n return {\n id: request.id,\n ok: true,\n result: tool.evaluate(omitSessionId(params) as never, optionalSessionId(params))\n };\n case 'recalculateModel':\n return {\n id: request.id,\n ok: true,\n result: tool.recalculateModel(optionalSessionId(params))\n };\n case 'restoreOrder':\n return {\n id: request.id,\n ok: true,\n result: tool.restoreOrder(optionalSessionId(params))\n };\n case 'synthesize':\n return {\n id: request.id,\n ok: true,\n result: tool.synthesize(omitSessionId(params) as never, optionalSessionId(params))\n };\n default:\n return {\n id: request.id ?? null,\n ok: false,\n error: {\n code: 'METHOD_NOT_FOUND',\n message: `Unknown method: ${request.method}`\n }\n };\n }\n } catch (error) {\n return {\n id: request.id ?? null,\n ok: false,\n error: {\n code: 'INTERNAL_ERROR',\n message: error instanceof Error ? error.message : 'Unknown error',\n details: error\n }\n };\n }\n}\n","#!/usr/bin/env node\nimport readline from 'node:readline';\n\nimport { RSToolAgent } from '../models/rstool-agent';\nimport { handleStdioRequest, type StdioRequest, type StdioResponse } from './stdio-handler';\n\nconst persistenceDir = process.env.RSTOOL_PERSISTENCE_DIR;\nconst tool = new RSToolAgent(persistenceDir ? { persistenceDir } : {});\n\nfunction writeResponse(response: StdioResponse): void {\n if (!process.stdout.writable || process.stdout.destroyed || process.stdout.writableEnded) {\n return;\n }\n try {\n process.stdout.write(`${JSON.stringify(response)}\\n`);\n } catch {\n // The client might have already closed stdout (EPIPE). Safe to ignore.\n }\n}\n\nconst input = readline.createInterface({\n input: process.stdin,\n crlfDelay: Infinity\n});\n\nwriteResponse({\n id: null,\n ok: true,\n result: {\n ready: true,\n wrapper: 'rstool-stdio',\n contractVersion: tool.contractVersion\n }\n});\n\ninput.on('line', line => {\n if (!line.trim()) {\n return;\n }\n try {\n const request = JSON.parse(line) as StdioRequest;\n if (!('id' in request) || !('method' in request)) {\n throw new Error('Request must include \"id\" and \"method\"');\n }\n void handleStdioRequest(tool, request).then(writeResponse);\n } catch (error) {\n writeResponse({\n id: null,\n ok: false,\n error: {\n code: 'BAD_REQUEST',\n message: error instanceof Error ? error.message : 'Invalid JSON'\n }\n });\n }\n});\n"],"mappings":"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"}
1
+ {"version":3,"file":"stdio-wrapper.js","names":[],"sources":["../../src/wrapper/stdio-handler.ts","../../src/wrapper/stdio-wrapper.ts"],"sourcesContent":["import type { ListDiagnosticsFilters } from '../models/diagnostic';\nimport type { RSToolAgent } from '../models/rstool-agent';\n\nexport interface StdioRequest {\n id: string | number;\n method: string;\n params?: unknown;\n}\n\nexport interface StdioResponse {\n id: string | number | null;\n ok: boolean;\n result?: unknown;\n error?: {\n code: string;\n message: string;\n details?: unknown;\n };\n}\n\nexport const STDIO_METHODS = [\n 'ensureSession',\n 'createSession',\n 'getCurrentSession',\n 'setCurrentSession',\n 'applySchemaPatch',\n 'getSessionState',\n 'listDiagnostics',\n 'analyzeExpression',\n 'commitStep',\n 'exportSession',\n 'exportPortal',\n 'importData',\n 'setModelValues',\n 'getModelState',\n 'evaluate',\n 'recalculateModel',\n 'restoreOrder',\n 'synthesize'\n] as const;\n\nfunction asObject(value: unknown): Record<string, unknown> {\n if (!value || typeof value !== 'object' || Array.isArray(value)) {\n return {};\n }\n return value as Record<string, unknown>;\n}\n\nfunction requiredString(input: Record<string, unknown>, key: string): string {\n const value = input[key];\n if (typeof value !== 'string' || value.length === 0) {\n throw new Error(`Missing or invalid \"${key}\"`);\n }\n return value;\n}\n\nfunction optionalSessionId(input: Record<string, unknown>): string | undefined {\n const value = input.sessionId;\n return typeof value === 'string' && value.length > 0 ? value : undefined;\n}\n\nfunction omitSessionId(input: Record<string, unknown>): Record<string, unknown> {\n const { sessionId: _sessionId, ...rest } = input;\n return rest;\n}\n\n/** Flat `constituentId` / `kind` / `severity` params; legacy nested `filters` object is still accepted. */\nfunction diagnosticsFilters(input: Record<string, unknown>): ListDiagnosticsFilters | undefined {\n const source = { ...asObject(input.filters), ...omitSessionId(input) };\n const filters: ListDiagnosticsFilters = {};\n if (typeof source.constituentId === 'number') {\n filters.constituentId = source.constituentId;\n }\n if (source.kind === 'expression' || source.kind === 'schema' || source.kind === 'model') {\n filters.kind = source.kind;\n }\n if (source.severity === 'error' || source.severity === 'warning') {\n filters.severity = source.severity;\n }\n return Object.keys(filters).length > 0 ? filters : undefined;\n}\n\nexport async function handleStdioRequest(tool: RSToolAgent, request: StdioRequest): Promise<StdioResponse> {\n try {\n const params = asObject(request.params);\n switch (request.method) {\n case 'ping':\n return { id: request.id, ok: true, result: { pong: true, contractVersion: tool.contractVersion } };\n case 'methods':\n return { id: request.id, ok: true, result: STDIO_METHODS };\n case 'ensureSession':\n return {\n id: request.id,\n ok: true,\n result: tool.ensureSession(params.initial as never)\n };\n case 'createSession':\n return {\n id: request.id,\n ok: true,\n result: tool.createSession(params.initial as never)\n };\n case 'getCurrentSession':\n return {\n id: request.id,\n ok: true,\n result: tool.getCurrentSession()\n };\n case 'setCurrentSession':\n return {\n id: request.id,\n ok: true,\n result: tool.setCurrentSession(requiredString(params, 'sessionId'))\n };\n case 'applySchemaPatch':\n return {\n id: request.id,\n ok: true,\n result: tool.applySchemaPatch(omitSessionId(params) as never, optionalSessionId(params))\n };\n case 'getSessionState':\n return {\n id: request.id,\n ok: true,\n result: tool.getSessionState(\n (params.detail as 'summary' | 'full' | undefined) ?? 'summary',\n optionalSessionId(params)\n )\n };\n case 'listDiagnostics':\n return {\n id: request.id,\n ok: true,\n result: tool.listDiagnostics(diagnosticsFilters(params), optionalSessionId(params))\n };\n case 'analyzeExpression':\n return {\n id: request.id,\n ok: true,\n result: tool.analyzeExpression(omitSessionId(params) as never, optionalSessionId(params))\n };\n case 'commitStep':\n return {\n id: request.id,\n ok: true,\n result: tool.commitStep(params.message as string | undefined, optionalSessionId(params))\n };\n case 'exportSession':\n return {\n id: request.id,\n ok: true,\n result: tool.exportSession(optionalSessionId(params))\n };\n case 'exportPortal':\n return {\n id: request.id,\n ok: true,\n result: tool.exportPortal(omitSessionId(params) as never, optionalSessionId(params))\n };\n case 'importData':\n return {\n id: request.id,\n ok: true,\n result: tool.importData(params.payload as string | object, params.kind as never)\n };\n case 'setModelValues':\n return {\n id: request.id,\n ok: true,\n result: await tool.setModelValues(omitSessionId(params) as never, optionalSessionId(params))\n };\n case 'getModelState':\n return {\n id: request.id,\n ok: true,\n result: tool.getModelState(optionalSessionId(params))\n };\n case 'evaluate':\n return {\n id: request.id,\n ok: true,\n result: tool.evaluate(omitSessionId(params) as never, optionalSessionId(params))\n };\n case 'recalculateModel':\n return {\n id: request.id,\n ok: true,\n result: tool.recalculateModel(optionalSessionId(params))\n };\n case 'restoreOrder':\n return {\n id: request.id,\n ok: true,\n result: tool.restoreOrder(optionalSessionId(params))\n };\n case 'synthesize':\n return {\n id: request.id,\n ok: true,\n result: tool.synthesize(omitSessionId(params) as never, optionalSessionId(params))\n };\n default:\n return {\n id: request.id ?? null,\n ok: false,\n error: {\n code: 'METHOD_NOT_FOUND',\n message: `Unknown method: ${request.method}`\n }\n };\n }\n } catch (error) {\n return {\n id: request.id ?? null,\n ok: false,\n error: {\n code: 'INTERNAL_ERROR',\n message: error instanceof Error ? error.message : 'Unknown error',\n details: error\n }\n };\n }\n}\n","#!/usr/bin/env node\nimport readline from 'node:readline';\n\nimport { RSToolAgent } from '../models/rstool-agent';\nimport { handleStdioRequest, type StdioRequest, type StdioResponse } from './stdio-handler';\n\nconst persistenceDir = process.env.RSTOOL_PERSISTENCE_DIR;\nconst tool = new RSToolAgent(persistenceDir ? { persistenceDir } : {});\n\nfunction writeResponse(response: StdioResponse): void {\n if (!process.stdout.writable || process.stdout.destroyed || process.stdout.writableEnded) {\n return;\n }\n try {\n process.stdout.write(`${JSON.stringify(response)}\\n`);\n } catch {\n // The client might have already closed stdout (EPIPE). Safe to ignore.\n }\n}\n\nconst input = readline.createInterface({\n input: process.stdin,\n crlfDelay: Infinity\n});\n\nwriteResponse({\n id: null,\n ok: true,\n result: {\n ready: true,\n wrapper: 'rstool-stdio',\n contractVersion: tool.contractVersion\n }\n});\n\ninput.on('line', line => {\n if (!line.trim()) {\n return;\n }\n try {\n const request = JSON.parse(line) as StdioRequest;\n if (!('id' in request) || !('method' in request)) {\n throw new Error('Request must include \"id\" and \"method\"');\n }\n void handleStdioRequest(tool, request).then(writeResponse);\n } catch (error) {\n writeResponse({\n id: null,\n ok: false,\n error: {\n code: 'BAD_REQUEST',\n message: error instanceof Error ? error.message : 'Invalid JSON'\n }\n });\n }\n});\n"],"mappings":"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"}
package/docs/BRIEF.md ADDED
@@ -0,0 +1,93 @@
1
+ # Бриф схемы
2
+
3
+ Бриф — постановка задачи концептуализации, журнал решений и исполнимые сценарии в одном JSON. Метод, который за ним стоит, — [METHOD.md](METHOD.md). JSON-схема — [brief.schema.json](brief.schema.json).
4
+
5
+ Пока бриф хранится файлом `brief.json` в папке задачи, рядом с сессией (не в пакете rstool). Проверка схемы по брифу — скрипт `skills/scripts/check-brief.ts` (см. «Проверка»). В rstool 4.0 бриф переедет в сессию, а на Portal — в блок «Задание» паспорта схемы.
6
+
7
+ ## Поля
8
+
9
+ | Поле | Этап | Что записать |
10
+ | :--------------- | :----- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
11
+ | `status` | 1 | `draft` — составляется; `confirmed` — пользователь подтвердил; `assumed` — спросить нельзя, ответы в `assumptions` |
12
+ | `approach` | 1 | `research` — экспликация; `education` — обучение; `project` — основа для ПО / ОСС |
13
+ | `object`, `task` | 1 | объект исследования; задача и точка зрения — по одной-две фразы |
14
+ | `source` | 1 | документ и разделы, ссылка на схему Portal |
15
+ | `restrictions` | 1 | что исключено (`excludes`) и что из-за этого невыразимо (`inexpressible`) |
16
+ | `expressiveness` | 1–3 | требования к выразительным средствам: `concept`, `ref` в источнике; `via` (этап 2) — как выразится через ядро; `constituent` (этап 3) — имя конституенты или `null` |
17
+ | `distinctions` | 1, 4 | пары `between` (id требований `E#` или имена конституент) и `witness` — сценарий, где они различаются |
18
+ | `invariants` | 1, 3–4 | правило `text`, аксиома `constituent`, сценарии `positive` и `negative` |
19
+ | `decisions` | 2–3 | журнал: `topic`, `alternatives`, `choice`, `rationale`; `supersedes` — какое решение отменено |
20
+ | `scenarios` | 1–4 | маленькие интерпретации с ожиданиями (ниже) |
21
+ | `questions` | 1–2 | вопросы раундов 1–2: `text`, `proposed`, `consequence`, `answer` |
22
+ | `assumptions` | 1–2 | то, что не спросили или не смогли спросить |
23
+ | `openQuestions` | любой | что осталось нерешённым, в том числе то, что ЯРЭ не выражает |
24
+
25
+ Идентификаторы: `L#` ограничения, `E#` требования, `V#` различения, `I#` инварианты, `DC#` решения, `SC#` сценарии. Идентификатор не переиспользуется после удаления.
26
+
27
+ Ссылки на конституенты — по имени (`D3`). При перенумерации (`restoreOrder`, синтез) обнови ссылки. Брифы аргументов синтеза в результат не переносятся: у синтезированной схемы свой бриф по итоговым именам (`skills/rs-synthesis`). Конвенции и текстовые определения не ссылаются на `DC#`: бриф не уходит вместе со схемой.
28
+
29
+ ## Сценарии
30
+
31
+ ```json
32
+ {
33
+ "id": "SC2",
34
+ "title": "Двойная выдача e1",
35
+ "base": "SC1",
36
+ "values": {
37
+ "S1": [
38
+ ["ann", "e1", 3],
39
+ ["bob", "e1", 4]
40
+ ]
41
+ },
42
+ "expect": [
43
+ { "of": "A1", "holds": false },
44
+ { "of": "D2", "contains": ["e2"], "excludes": ["e1"] },
45
+ { "of": "F1", "args": ["ann"], "value": ["e1"] },
46
+ { "expr": "card(D1)", "value": 1 }
47
+ ]
48
+ }
49
+ ```
50
+
51
+ - `values` — значения неопределяемых понятий **подписями**: `X#` / `C#` — массив подписей элементов; `S#` — по типизации: множество — массив, кортеж — массив компонент, элементы `Z` — числа. Индексы и `TUPLE_ID` не нужны.
52
+ - Понятия, не перечисленные в `values`, пусты. `base` наследует значения другого сценария; перечисленные здесь заменяют унаследованные целиком.
53
+ - `expect` — ожидания:
54
+ - `of` — конституента: `D#` (значение), `A#` / `T#` (`holds`), `F#` / `P#` с `args` (аргументы подписями);
55
+ - `expr` — выражение ЯРЭ над схемой (`card(D1)`, `D2⊆D1` с `holds`);
56
+ - `value` — точное значение; `contains` / `excludes` — элементы, которые должны входить / не входить; `holds` — истинность.
57
+ - Порядок элементов в множествах не важен.
58
+
59
+ ## Проверка
60
+
61
+ Из корня пакета rstool (в workspace Portal — `rsconcept/rstool`, в npm-проекте — `node_modules/@rsconcept/rstool`):
62
+
63
+ ```bash
64
+ npx tsx skills/scripts/check-brief.ts --session <abs>/session.json --brief <abs>/brief.json [--base <abs>/v1-session.json] [--json]
65
+ ```
66
+
67
+ В workspace Portal: `pnpm --filter @rsconcept/rstool exec tsx skills/scripts/check-brief.ts …`. Сессия — файл `exportSession`. Сценарии выполняются на копии сессии; сама сессия не меняется.
68
+
69
+ `--review` — режим ревью чужой схемы: блокеры — находки отчёта, следующий шаг — `rs-review`, а не правка.
70
+
71
+ Скрипт печатает этап, покрытие, матрицу приёмки «сценарий × ожидание → факт», блокеры, предупреждения и следующий шаг. Код выхода `0` — блокеров нет, `1` — есть.
72
+
73
+ | Проверка | Блокер когда |
74
+ | :-------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------- |
75
+ | статус брифа | `draft` |
76
+ | требование без `via` | всегда (этап 2 не пройден) |
77
+ | требование без конституенты | всегда; причина — не хватает исходного понятия, понятие не выведено или нужен синтез |
78
+ | инвариант без аксиомы | всегда |
79
+ | инвариант без позитива / негатива | `project`; иначе предупреждение |
80
+ | сценарий не прошёл | всегда |
81
+ | различение не подтверждено сценарием | `research`; иначе предупреждение |
82
+ | пустая конвенция `X#` / `C#` / `S#` | всегда |
83
+ | диагностики с `severity: error` | всегда |
84
+ | «сироты» — вне путей к требованиям и аксиомам | `project`; иначе предупреждение |
85
+ | с `--base`: аксиома или конституента брифа удалена, производное стало неопределяемым, формула стала тривиальной | если имени нет в журнале решений |
86
+ | требование выражено `S#` без решения | предупреждение |
87
+ | описание схемы пусто; конвенции или текстовые определения ссылаются на `DC#` | предупреждение: бриф не уходит ни в Portal, ни в результат синтеза — решения пишутся словами |
88
+
89
+ Чистый вывод не доказывает смысл схемы: он показывает только то, что записано в брифе. Полнота брифа — ответственность этапов 1–2.
90
+
91
+ ## Лёгкий бриф
92
+
93
+ Для точечной правки (одна формула, одно понятие) достаточно: `status`, `approach`, `object`, `task`, одно требование с `via` и `constituent` и один сценарий. Объяснение формулы и вопросы по синтаксису обходятся без брифа.
@@ -73,7 +73,21 @@ card(S1) = card(Pr1,2(S1))
73
73
  ```
74
74
 
75
75
  Это заменяет длинные `∀d1,d2∈S1 ...`.
76
- Оставляй `∀` / `∃`, только если выражение нельзя задать через множества, проекции, фильтры, `card`.
76
+ Оставляй `∀` / `∃`, только если выражение нельзя задать через множества, проекции, фильтры, сравнение с `∅` или `card`.
77
+
78
+ ## Пустота и мощность
79
+
80
+ (Не)пустота множества — сравнение с пустым множеством, не арифметика мощности.
81
+
82
+ | Нужно | Пиши | Не пиши |
83
+ | :----------------------- | :---------------------- | :------------------------------ |
84
+ | непусто | `S ≠ ∅` | `card(S) > 0`, `card(S) ≥ 1` |
85
+ | пусто | `S = ∅` | `card(S) = 0` |
86
+ | пересечение непусто | `A ∩ B ≠ ∅` | `card(A ∩ B) > 0` |
87
+ | ровно один элемент | `card(S) = 1` | — |
88
+ | сравнение численностей | `card(S1) = card(S2)` | — |
89
+
90
+ `∅` в `=` / `≠` подстраивается под ступень сравниваемого множества. Не используй `∅` как нейтральный элемент операций (`X1 ∪ ∅` → `invalidEmptySetUsage`).
77
91
 
78
92
  ## Производные понятия
79
93
 
@@ -82,6 +96,7 @@ card(S1) = card(Pr1,2(S1))
82
96
  - `Prk(…)` / `Pri,j(…)`;
83
97
  - `Fi*k[{…}](…)`;
84
98
  - `D{ξ∈… | F[ξ] = …}`;
99
+ - `S ≠ ∅` / `A ∩ B ≠ ∅`;
85
100
  - `card(F[ξ]) = 1`.
86
101
 
87
102
  Перед `D{x∈… | ∃d1∈… ∃d2∈…}` проверь, нельзя ли взять `Pr` от уже введенного отношения.
@@ -94,7 +109,7 @@ card(S1) = card(Pr1,2(S1))
94
109
  D1 ::= I{(ξ,σ) | ξ:∈S1; σ:=Expr(ξ)}
95
110
  ```
96
111
 
97
- а затем читать значение через `Pr2(Fi1[{α}](D1))` или `pr2(d)` при `d∈D1`.
112
+ а затем читать значение через `Pr2(Fi1[{α}](D1))` или `pr2(d)` при `d∈D1`. Как объяснить эту цепочку человеку — [CONSTRUCTIONS.md](CONSTRUCTIONS.md), раздел «Типичная цепочка».
98
113
 
99
114
  Если для каждого допустимого `ξ` значение `σ` **однозначно задаётся** выражением, это не отношение, а **терм-функция**:
100
115