@polintpro/proposit-core 0.2.3 → 0.2.4

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 (48) hide show
  1. package/README.md +14 -11
  2. package/dist/index.d.ts +3 -1
  3. package/dist/index.d.ts.map +1 -1
  4. package/dist/index.js +1 -1
  5. package/dist/index.js.map +1 -1
  6. package/dist/lib/core/ArgumentEngine.d.ts +23 -19
  7. package/dist/lib/core/ArgumentEngine.d.ts.map +1 -1
  8. package/dist/lib/core/ArgumentEngine.js +11 -11
  9. package/dist/lib/core/ArgumentEngine.js.map +1 -1
  10. package/dist/lib/core/ChangeCollector.d.ts +13 -14
  11. package/dist/lib/core/ChangeCollector.d.ts.map +1 -1
  12. package/dist/lib/core/ChangeCollector.js.map +1 -1
  13. package/dist/lib/core/ExpressionManager.d.ts +15 -13
  14. package/dist/lib/core/ExpressionManager.d.ts.map +1 -1
  15. package/dist/lib/core/ExpressionManager.js +52 -22
  16. package/dist/lib/core/ExpressionManager.js.map +1 -1
  17. package/dist/lib/core/PremiseManager.d.ts +21 -20
  18. package/dist/lib/core/PremiseManager.d.ts.map +1 -1
  19. package/dist/lib/core/PremiseManager.js +19 -24
  20. package/dist/lib/core/PremiseManager.js.map +1 -1
  21. package/dist/lib/core/VariableManager.d.ts +7 -8
  22. package/dist/lib/core/VariableManager.d.ts.map +1 -1
  23. package/dist/lib/core/VariableManager.js +4 -1
  24. package/dist/lib/core/VariableManager.js.map +1 -1
  25. package/dist/lib/core/diff.d.ts +1 -1
  26. package/dist/lib/core/diff.d.ts.map +1 -1
  27. package/dist/lib/core/diff.js +19 -11
  28. package/dist/lib/core/diff.js.map +1 -1
  29. package/dist/lib/schemata/propositional.d.ts.map +1 -1
  30. package/dist/lib/schemata/propositional.js +0 -1
  31. package/dist/lib/schemata/propositional.js.map +1 -1
  32. package/dist/lib/schemata/shared.d.ts +4 -0
  33. package/dist/lib/schemata/shared.d.ts.map +1 -1
  34. package/dist/lib/types/diff.d.ts +15 -15
  35. package/dist/lib/types/diff.d.ts.map +1 -1
  36. package/dist/lib/types/mutation.d.ts +7 -20
  37. package/dist/lib/types/mutation.d.ts.map +1 -1
  38. package/dist/lib/utils/position.d.ts +9 -3
  39. package/dist/lib/utils/position.d.ts.map +1 -1
  40. package/dist/lib/utils/position.js +8 -3
  41. package/dist/lib/utils/position.js.map +1 -1
  42. package/package.json +3 -2
  43. package/skills/proposit-core/SKILL.md +35 -0
  44. package/skills/proposit-core/docs/api-usage.md +442 -0
  45. package/skills/proposit-core/docs/architecture.md +256 -0
  46. package/skills/proposit-core/docs/cli.md +304 -0
  47. package/skills/proposit-core/docs/testing.md +281 -0
  48. package/skills/proposit-core/docs/types-schemas.md +648 -0
@@ -0,0 +1,256 @@
1
+ # Architecture & Internals
2
+
3
+ Reference for proposit-core internal design. Covers class structure, data representations, invariants, and implementation details.
4
+
5
+ ---
6
+
7
+ ## Class Hierarchy
8
+
9
+ ```
10
+ ArgumentEngine
11
+ ├─ VariableManager (shared, owned by engine)
12
+ └─ PremiseManager (one per premise, receives shared VariableManager)
13
+ └─ ExpressionManager (expression tree)
14
+ ```
15
+
16
+ | Class | File | Exported? |
17
+ | ------------------- | ----------------------------------- | ---------------- |
18
+ | `ArgumentEngine` | `src/lib/core/ArgumentEngine.ts` | Yes (public API) |
19
+ | `PremiseManager` | `src/lib/core/PremiseManager.ts` | Yes (public API) |
20
+ | `ExpressionManager` | `src/lib/core/ExpressionManager.ts` | No (internal) |
21
+ | `VariableManager` | `src/lib/core/VariableManager.ts` | No (internal) |
22
+ | `ChangeCollector` | `src/lib/core/ChangeCollector.ts` | No (internal) |
23
+
24
+ - `ArgumentEngine` owns a single `VariableManager` instance and passes it by reference to every `PremiseManager` it creates. All premises share the same variable registry.
25
+ - Each `PremiseManager` owns one `ExpressionManager` for its expression tree.
26
+ - Constructor: `new ArgumentEngine(argument, options?)` where `options?: { checksumConfig?: TCoreChecksumConfig }`.
27
+
28
+ ---
29
+
30
+ ## Expression Tree Representation
31
+
32
+ Three internal maps in `ExpressionManager`:
33
+
34
+ ```typescript
35
+ expressions: Map<string, TExpressionInput>
36
+ // Main store. Entities lack checksums; checksums attached lazily by getters.
37
+
38
+ childExpressionIdsByParentId: Map<string | null, Set<string>>
39
+ // Fast child lookup. The null key holds root expression IDs.
40
+
41
+ childPositionsByParentId: Map<string | null, Set<number>>
42
+ // Tracks which positions are occupied under each parent.
43
+ ```
44
+
45
+ Expressions are **immutable value objects**. To move one, delete and re-insert or use internal `reparent()`.
46
+
47
+ ### Expression Types (Discriminated Union)
48
+
49
+ `TPropositionalExpression` narrows on `type`:
50
+
51
+ | `type` | Extra fields | Notes |
52
+ | ------------ | ------------------------------------ | --------------------------------------- |
53
+ | `"variable"` | `variableId: string` | Leaf node referencing a variable |
54
+ | `"operator"` | `operator: TCoreLogicalOperatorType` | `and`, `or`, `not`, `implies`, `iff` |
55
+ | `"formula"` | (none) | Transparent unary wrapper (parentheses) |
56
+
57
+ ### Input Types (Distributive Omit)
58
+
59
+ These preserve discriminated-union narrowing via conditional type distribution:
60
+
61
+ - `TExpressionInput` -- `Omit<TPropositionalExpression, "checksum">` (storage type)
62
+ - `TExpressionWithoutPosition` -- `Omit<TPropositionalExpression, "position" | "checksum">` (for `appendExpression`, `addExpressionRelative`)
63
+ - `TExpressionUpdate` -- `{ position?, variableId?, operator? }` (for `updateExpression`)
64
+
65
+ ---
66
+
67
+ ## Midpoint-Based Positions
68
+
69
+ File: `src/lib/utils/position.ts`
70
+
71
+ ```typescript
72
+ POSITION_MIN = 0
73
+ POSITION_MAX = Number.MAX_SAFE_INTEGER // 2^53 - 1
74
+ POSITION_INITIAL = Math.floor(POSITION_MAX / 2)
75
+
76
+ midpoint(a, b) = a + (b - a) / 2 // overflow-safe
77
+ ```
78
+
79
+ | Scenario | Position |
80
+ | ------------------------------ | ----------------------------------------- |
81
+ | First child (no siblings) | `POSITION_INITIAL` |
82
+ | Append (after last sibling) | `midpoint(last.position, POSITION_MAX)` |
83
+ | Prepend (before first sibling) | `midpoint(POSITION_MIN, first.position)` |
84
+ | Between two siblings | `midpoint(left.position, right.position)` |
85
+
86
+ Approximately 52 bisections at the same insertion point before losing IEEE 754 double precision.
87
+
88
+ ### Intent-Based Insertion API
89
+
90
+ - `appendExpression(parentId, expression)` -- appends as last child, position computed automatically.
91
+ - `addExpressionRelative(siblingId, "before" | "after", expression)` -- inserts relative to an existing sibling.
92
+ - `addExpression(expression)` -- low-level escape hatch with explicit position.
93
+
94
+ All three exist on both `ExpressionManager` and `PremiseManager`.
95
+
96
+ ---
97
+
98
+ ## Root-Only Operators
99
+
100
+ `implies` and `iff` must always have `parentId: null`. They cannot be nested inside another expression. Enforced in both `addExpression` and `insertExpression`. Attempting to add them with a non-null `parentId` throws.
101
+
102
+ ---
103
+
104
+ ## Formula Nodes
105
+
106
+ The `formula` expression type is a **transparent unary wrapper** -- equivalent to parentheses around its single child.
107
+
108
+ - Must have exactly one child.
109
+ - Collapse rules apply identically to `formula` and `operator` nodes.
110
+ - During evaluation, `formula` nodes propagate their child's value transparently.
111
+
112
+ ---
113
+
114
+ ## Operator Collapse
115
+
116
+ Triggered by `removeExpression(id, deleteSubtree)`. After removal, `collapseIfNeeded(parentId)` runs on the parent:
117
+
118
+ | Children remaining | Action |
119
+ | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------ |
120
+ | 0 | Delete the operator/formula. Recurse to grandparent. |
121
+ | 1 | Delete the operator/formula. Promote surviving child (inherits `parentId` + `position`). No recursion (grandparent child count unchanged). |
122
+ | 2+ | No action. |
123
+
124
+ ### removeExpression Behavior
125
+
126
+ The `deleteSubtree` parameter (required boolean) controls behavior:
127
+
128
+ - **`deleteSubtree: true`** -- Removes expression and all descendants. Then runs `collapseIfNeeded(parentId)`.
129
+ - **`deleteSubtree: false`** -- Removes expression and promotes its single child into its slot (inheriting `parentId` and `position`). Throws if >1 child. Validates that root-only operators (`implies`/`iff`) are not promoted into non-root positions. No collapse runs after promotion. Leaf removal with `deleteSubtree: false` runs collapse on the parent (same as `true`).
130
+
131
+ ---
132
+
133
+ ## insertExpression Mutation Order
134
+
135
+ `reparent(rightNodeId, ...)` runs **before** `reparent(leftNodeId, ...)`.
136
+
137
+ This handles the case where the right node is a descendant of the left node's subtree -- it must be detached first to avoid corrupting the tree.
138
+
139
+ ---
140
+
141
+ ## Premise Types
142
+
143
+ Derived dynamically from the root expression -- not stored on disk.
144
+
145
+ | Method | Returns `true` when | Used for |
146
+ | ---------------- | -------------------------------------------------------------------------- | ---------------------------------- |
147
+ | `isInference()` | Root is `implies` or `iff` | Supporting/conclusion chain |
148
+ | `isConstraint()` | Root is anything else (`variable`, `not`, `and`, `or`) or premise is empty | Restricting admissible assignments |
149
+
150
+ ---
151
+
152
+ ## Derived Supporting Premises
153
+
154
+ Supporting premises are **not explicitly managed**. The methods `addSupportingPremise()` and `removeSupportingPremise()` were removed.
155
+
156
+ **Rule:** Any inference premise that is not the conclusion is automatically considered supporting.
157
+
158
+ `TCoreArgumentRoleState` contains only `{ conclusionPremiseId?: string }`. `listSupportingPremises()` derives the list dynamically on each call from the current set of premises.
159
+
160
+ ---
161
+
162
+ ## Auto-Conclusion Assignment
163
+
164
+ When the first premise is added to an `ArgumentEngine` (via `createPremise` or `createPremiseWithId`) and no conclusion is currently set, that premise is automatically designated as the conclusion.
165
+
166
+ - Auto-assignment is reflected in the mutation changeset.
167
+ - `setConclusionPremise()` overrides auto-assignment.
168
+ - Removing or clearing the conclusion re-enables auto-assignment for the next premise created.
169
+
170
+ ---
171
+
172
+ ## Mutation Changesets
173
+
174
+ Every mutating method on `PremiseManager` and `ArgumentEngine` returns `TCoreMutationResult<T>`:
175
+
176
+ ```typescript
177
+ interface TCoreMutationResult<T> {
178
+ result: T // direct answer (e.g. removed expression, new role state)
179
+ changes: TCoreChangeset // all side effects
180
+ }
181
+ ```
182
+
183
+ ### TCoreChangeset Structure
184
+
185
+ ```typescript
186
+ interface TCoreChangeset {
187
+ expressions?: TCoreEntityChanges<TPropositionalExpression>
188
+ variables?: TCoreEntityChanges<TPropositionalVariable>
189
+ premises?: TCoreEntityChanges<TCorePremise>
190
+ roles?: TCoreArgumentRoleState // present only when roles changed
191
+ argument?: TCoreArgument // present only when argument metadata changed
192
+ }
193
+
194
+ interface TCoreEntityChanges<T> {
195
+ added: T[]
196
+ modified: T[]
197
+ removed: T[]
198
+ }
199
+ ```
200
+
201
+ ### Internal Flow
202
+
203
+ 1. `ChangeCollector` (not exported) accumulates entity changes during a mutation.
204
+ 2. `toChangeset()` produces `TCoreRawChangeset` (uses input types without checksums: `TExpressionInput`, `TVariableInput`).
205
+ 3. `attachChangesetChecksums()` converts to `TCoreChangeset` with checksums attached before returning to callers.
206
+
207
+ ---
208
+
209
+ ## Checksum System
210
+
211
+ Per-entity checksums provide lightweight change detection without deep comparison.
212
+
213
+ ### How It Works
214
+
215
+ - All entity types carry a required `checksum: string` field in their schemas.
216
+ - Internally, managers store entities **without checksums** using input types (`TExpressionInput`, `TVariableInput`).
217
+ - Checksums are **attached lazily** by getters (`getExpression()`, `getVariables()`, `getArgument()`, `toData()`) and in changeset outputs.
218
+ - `PremiseManager.checksum()` and `ArgumentEngine.checksum()` use dirty flags for lazy recomputation.
219
+
220
+ ### Checksum Utilities
221
+
222
+ File: `src/lib/core/checksum.ts`
223
+
224
+ | Function | Description |
225
+ | -------------------------------------------- | ---------------------------------------------------- |
226
+ | `computeHash(input: string): string` | FNV-1a 32-bit hash. Returns 8-char hex string. |
227
+ | `canonicalSerialize(value: unknown): string` | Deterministic JSON with sorted keys at all levels. |
228
+ | `entityChecksum(entity, fields): string` | Picks specified fields (sorted), serializes, hashes. |
229
+
230
+ ### Configuration
231
+
232
+ File: `src/lib/consts.ts`
233
+
234
+ - `DEFAULT_CHECKSUM_CONFIG` -- defines which fields are hashed per entity type.
235
+ - `createChecksumConfig(additional)` -- merges additional fields into defaults (union, not replace).
236
+ - `ArgumentEngine` constructor accepts `options?: { checksumConfig?: TCoreChecksumConfig }`.
237
+
238
+ `TCoreChecksumConfig` fields (all `Set<string>`, all optional):
239
+
240
+ - `expressionFields` -- default: `id`, `type`, `parentId`, `position`, `argumentId`, `argumentVersion`, `variableId`, `operator`
241
+ - `variableFields` -- default: `id`, `symbol`, `argumentId`, `argumentVersion`
242
+ - `premiseFields` -- default: `id`, `rootExpressionId`
243
+ - `argumentFields` -- default: `id`, `version`
244
+ - `roleFields` -- default: `conclusionPremiseId`
245
+
246
+ ---
247
+
248
+ ## ESM Import Requirements
249
+
250
+ The project uses `moduleResolution: "bundler"` in `tsconfig.json` but `src/cli.ts` is compiled and run directly by Node.js ESM.
251
+
252
+ **Rules:**
253
+
254
+ - All relative imports in `src/cli/` and `src/lib/` must end in `.js`.
255
+ - Directory imports must use the explicit index path: `schemata/index.js` not `schemata/`.
256
+ - Critical: `src/lib/utils/` (directory) and `src/lib/utils.ts` (file) both compile to `dist/lib/`, and Node.js ESM resolves the directory first if no extension is given.
@@ -0,0 +1,304 @@
1
+ # proposit-core CLI Reference
2
+
3
+ ## Running the CLI
4
+
5
+ ```bash
6
+ # Local development (builds first, then runs)
7
+ pnpm run build
8
+ pnpm cli -- --help
9
+
10
+ # Installed globally
11
+ proposit-core --help
12
+ ```
13
+
14
+ Always build before running the CLI locally. The entry point is `src/cli.ts`, compiled to `dist/cli.js`.
15
+
16
+ ## Routing
17
+
18
+ **Source:** `src/cli.ts`, `src/cli/router.ts`
19
+
20
+ `argv[2]` is inspected by `isNamedCommand()` to decide the routing path:
21
+
22
+ **Named commands** (handled by top-level Commander program):
23
+
24
+ - `help`, `--help`, `-h`
25
+ - `version`, `--version`, `-V`
26
+ - `arguments`
27
+ - `diff`
28
+
29
+ **Version-scoped commands** (everything else):
30
+
31
+ - `argv[2]` is treated as `<argument_id>`
32
+ - `argv[3]` is the version selector
33
+ - After `resolveVersion()`, a sub-Commander program is built with the remaining args
34
+ - Registered sub-commands: `show`, `render`, `roles`, `variables`, `premises`, `expressions`, `analysis`
35
+
36
+ If `argv[2]` is undefined, `isNamedCommand()` returns `true` (falls through to Commander's help).
37
+
38
+ ## Version Selectors
39
+
40
+ **Source:** `src/cli/router.ts`
41
+
42
+ `resolveVersion(argumentId, versionArg)` accepts:
43
+
44
+ | Selector | Behavior |
45
+ | ---------------------------------- | ---------------------------------------------------------------------------------- |
46
+ | `"latest"` | Max version number found in the argument directory |
47
+ | `"last-published"` | Highest published version (iterates from highest to lowest; exits 1 if none found) |
48
+ | Integer string (e.g. `"0"`, `"3"`) | Exact version number (must be non-negative integer; must exist on disk) |
49
+
50
+ Exits with error if no versions exist for the argument, or if the specified version is not found.
51
+
52
+ ## State Storage Layout
53
+
54
+ **Source:** `src/cli/config.ts`
55
+
56
+ Root directory: `$PROPOSIT_HOME` (default: `~/.proposit-core`)
57
+
58
+ ```
59
+ $PROPOSIT_HOME/
60
+ arguments/
61
+ <argument-id>/
62
+ meta.json # { id, title, description }
63
+ <version>/ # 0, 1, 2, ...
64
+ meta.json # { version, createdAt, published, publishedAt? }
65
+ variables.json # TCorePropositionalVariable[]
66
+ roles.json # { conclusionPremiseId? }
67
+ premises/
68
+ <premise-id>/
69
+ meta.json # { id, title? }
70
+ data.json # { rootExpressionId?, variables: id[], expressions[] }
71
+ <analysis>.json # { argumentId, argumentVersion, assignments, rejectedExpressionIds }
72
+ ```
73
+
74
+ **Reserved filenames** (excluded from analysis file listing): `meta.json`, `variables.json`, `roles.json`.
75
+
76
+ Analysis files default to `analysis.json`. The `listAnalysisFiles` function uses `Value.Check` to silently skip corrupt or non-analysis JSON files in the version directory.
77
+
78
+ **Path helpers** (`src/cli/config.ts`):
79
+
80
+ - `getStateDir()` -- root state directory
81
+ - `getArgumentsDir()` -- `<state>/arguments/`
82
+ - `getArgumentDir(argumentId)` -- `<state>/arguments/<id>/`
83
+ - `getVersionDir(argumentId, version)` -- `<state>/arguments/<id>/<version>/`
84
+ - `getPremisesDir(argumentId, version)` -- `<state>/arguments/<id>/<version>/premises/`
85
+ - `getPremiseDir(argumentId, version, premiseId)` -- `<state>/arguments/<id>/<version>/premises/<premiseId>/`
86
+
87
+ ## Engine Hydration
88
+
89
+ **Source:** `src/cli/engine.ts`
90
+
91
+ ### `hydrateEngine(argumentId, version)`
92
+
93
+ Builds a fully-hydrated `ArgumentEngine` from on-disk state:
94
+
95
+ 1. **Parallel read:** argument meta, version meta, variables, roles, and premise IDs (via `Promise.all`)
96
+ 2. **Construct argument:** merge `argMeta` and `versionMeta` into `Omit<TCoreArgument, "checksum">`
97
+ 3. **Create engine:** `new ArgumentEngine(argument)`
98
+ 4. **Register variables:** iterate all variables, call `engine.addVariable()` for each (with `argumentVersion` set)
99
+ 5. **For each premise:** read meta + data in parallel, then `engine.createPremiseWithId(premiseId, extras)`
100
+ 6. **Add expressions in BFS order:** roots first (`parentId === null`), then children of already-added nodes in subsequent passes until all are placed
101
+ 7. **Set conclusion role last:** `engine.setConclusionPremise(roles.conclusionPremiseId)` if defined. Supporting premises are derived automatically from expression type.
102
+
103
+ ### `persistEngine(engine)`
104
+
105
+ The inverse of hydration -- writes all engine state back to disk:
106
+
107
+ 1. Extracts argument meta and version meta from `engine.getArgument()`
108
+ 2. Writes argument meta, version meta, variables, and roles
109
+ 3. For each premise: writes meta and data (expressions + variables)
110
+
111
+ ## Publish Semantics
112
+
113
+ **Source:** `src/cli/commands/arguments.ts`
114
+
115
+ `arguments publish <id>`:
116
+
117
+ 1. Reads the latest version meta; exits if already published
118
+ 2. Marks the current latest version `published: true, publishedAt: new Date()`
119
+ 3. Copies the entire version directory to `version + 1`
120
+ 4. Writes a fresh unpublished meta for the new version (removes `publishedAt`)
121
+
122
+ **Publish guard:** All mutating CLI commands (roles, variables, premises, expressions) call `assertNotPublished(argumentId, version)` before making changes. If the version is already published, it exits with code 1.
123
+
124
+ ## Command Reference
125
+
126
+ ### Top-Level Commands
127
+
128
+ | Command | Subcommands |
129
+ | ----------- | -------------------------------------------------- |
130
+ | `version` | Prints the package version |
131
+ | `arguments` | `create`, `import`, `list`, `delete`, `publish` |
132
+ | `diff` | `<args...>` (version or cross-argument comparison) |
133
+
134
+ #### `arguments create <title> <description>`
135
+
136
+ Creates a new argument with a generated UUID. Initializes version 0 with empty variables, roles, and premises directory. Prints the argument ID.
137
+
138
+ #### `arguments import <yaml_file>`
139
+
140
+ Imports an argument from a YAML file. Parses the YAML, builds an `ArgumentEngine` via `importArgumentFromYaml()`, then persists it to disk. Prints the argument ID.
141
+
142
+ #### `arguments list [--json]`
143
+
144
+ Lists all arguments sorted newest-first by `createdAt`. Plain output shows `id | title (created date)`. JSON output includes `id`, `title`, `description`, `latestVersion`, `latestCreatedAt`, `latestPublished`.
145
+
146
+ #### `arguments delete <id> [--confirm] [--all]`
147
+
148
+ Deletes an argument. Without `--all`, deletes only the latest version (unless only one version exists, in which case the entire argument is deleted). With `--all`, deletes all versions and the argument directory. Without `--confirm`, prompts for interactive confirmation.
149
+
150
+ #### `arguments publish <id>`
151
+
152
+ Publishes the latest version and creates a new draft. See [Publish Semantics](#publish-semantics).
153
+
154
+ #### `diff <args...> [--json]`
155
+
156
+ Compares two argument versions. Accepts either:
157
+
158
+ - 3 args: `<id> <verA> <verB>` -- same argument, two versions
159
+ - 4 args: `<idA> <verA> <idB> <verB>` -- cross-argument comparison
160
+
161
+ Without `--json`, renders a human-readable diff. With `--json`, outputs the raw `TCoreArgumentDiff` object.
162
+
163
+ ### Version-Scoped Commands
164
+
165
+ Invoked as `proposit-core <argument_id> <version> <command> ...`
166
+
167
+ #### `show [--json]`
168
+
169
+ Shows metadata for the argument version. Plain output lists id, title, description, version, created date, published status. JSON merges argument meta and version meta.
170
+
171
+ #### `render`
172
+
173
+ Renders all premises as logical expression strings. The conclusion premise is listed first, marked with `*`. Empty premises show `(empty)`.
174
+
175
+ #### `roles`
176
+
177
+ | Subcommand | Description |
178
+ | ----------------------------- | ------------------------------------------------------------------ |
179
+ | `show [--json]` | Shows the conclusion premise ID and derived supporting premise IDs |
180
+ | `set-conclusion <premise_id>` | Sets the designated conclusion premise (premise must exist) |
181
+ | `clear-conclusion` | Clears the designated conclusion premise |
182
+
183
+ #### `variables`
184
+
185
+ | Subcommand | Description |
186
+ | ------------------------------------ | --------------------------------------------------------------------------------------------------------- |
187
+ | `create <symbol> [--id <id>]` | Registers a new variable with the given symbol. Optionally specify an explicit ID. Prints the variable ID |
188
+ | `list [--json]` | Lists all argument-level variables |
189
+ | `show <id> [--json]` | Shows a single variable |
190
+ | `update <id> [--symbol <new>]` | Updates a variable's symbol |
191
+ | `delete <id>` | Removes a variable (cascade-deletes all referencing expressions across all premises) |
192
+ | `list-unused [--json]` | Lists variables not referenced by any expression |
193
+ | `delete-unused [--confirm] [--json]` | Deletes all unreferenced variables (prompts unless `--confirm`) |
194
+
195
+ #### `premises`
196
+
197
+ | Subcommand | Description |
198
+ | --------------------------------------------- | ------------------------------------------------------------------------------- |
199
+ | `create [--title <title>]` | Creates a new empty premise. Prints the premise ID |
200
+ | `list [--json]` | Lists all premises with type (inference/constraint), display string, and title |
201
+ | `show <id> [--json]` | Shows premise metadata, type, root expression, variable count, expression count |
202
+ | `update <id> [--title <new>] [--clear-title]` | Updates premise metadata. `--title` and `--clear-title` are mutually exclusive |
203
+ | `delete <id> [--confirm]` | Deletes a premise. Clears the conclusion role if this was the conclusion |
204
+ | `render <id>` | Renders the premise as a logical expression string |
205
+
206
+ #### `expressions`
207
+
208
+ | Subcommand | Description |
209
+ | -------------------------------------- | ---------------------------------------------- |
210
+ | `create <premise_id> [flags]` | Adds an expression to a premise |
211
+ | `insert <premise_id> [flags]` | Inserts an expression, wrapping existing nodes |
212
+ | `delete <premise_id> <expr_id>` | Removes an expression and its subtree |
213
+ | `list <premise_id> [--json]` | Lists all expressions in a premise |
214
+ | `show <premise_id> <expr_id> [--json]` | Shows a single expression |
215
+
216
+ **`create` flags:**
217
+
218
+ - `--type <type>` (required): `variable`, `operator`, or `formula`
219
+ - `--id <id>`: explicit expression ID (default: generated UUID)
220
+ - `--parent-id <parent_id>`: parent expression ID (omit for root)
221
+ - `--position <n>`: explicit position among siblings
222
+ - `--before <sibling_id>`: insert before this sibling (mutually exclusive with `--after` and `--position`)
223
+ - `--after <sibling_id>`: insert after this sibling (mutually exclusive with `--before` and `--position`)
224
+ - `--variable-id <id>`: required for `type=variable`
225
+ - `--operator <op>`: required for `type=operator` (`not`, `and`, `or`, `implies`, `iff`)
226
+
227
+ When neither `--position`, `--before`, nor `--after` is specified, the expression is appended as the last child via `appendExpression`.
228
+
229
+ **`insert` flags:**
230
+
231
+ - `--type <type>` (required): `variable`, `operator`, or `formula`
232
+ - `--id <id>`: explicit expression ID
233
+ - `--parent-id <parent_id>`: parent expression ID
234
+ - `--position <n>`: position among siblings
235
+ - `--variable-id <id>`: required for `type=variable`
236
+ - `--operator <op>`: required for `type=operator`
237
+ - `--left-node-id <id>`: left node to wrap
238
+ - `--right-node-id <id>`: right node to wrap
239
+
240
+ At least one of `--left-node-id` or `--right-node-id` is required.
241
+
242
+ #### `analysis`
243
+
244
+ | Subcommand | Description |
245
+ | -------------------------------------------- | --------------------------------------------------------------------------------------------------------- |
246
+ | `create [filename] [--default <value>]` | Creates a new analysis file with all variables. Default assignment value: `unset` (or `true`/`false`) |
247
+ | `list [--json]` | Lists analysis files in the version directory |
248
+ | `show [--file <f>] [--json]` | Shows variable assignments and rejected expressions |
249
+ | `set <symbol> <value> [--file <f>]` | Sets a single variable assignment (`true`, `false`, or `unset`) |
250
+ | `reset [--file <f>] [--value <v>]` | Resets all assignments to one value (default: `unset`) |
251
+ | `reject <expr_id> [--file <f>]` | Rejects an expression (evaluates to `false`, children skipped) |
252
+ | `accept <expr_id> [--file <f>]` | Accepts an expression (restores normal evaluation) |
253
+ | `validate-assignments [--file <f>] [--json]` | Validates analysis file against the argument version (checks symbols, IDs, rejected expression existence) |
254
+ | `delete [--file <f>] [--confirm]` | Deletes an analysis file |
255
+ | `evaluate [--file <f>] [--json] [flags]` | Evaluates the argument using the analysis file's assignments |
256
+ | `check-validity [--json] [flags]` | Runs truth-table validity checking |
257
+ | `validate-argument [--json]` | Validates argument structure for evaluability |
258
+ | `refs [--json]` | Shows variables referenced across all premises (symbol, premise IDs) |
259
+ | `export [--json]` | Exports the full argument engine state snapshot |
260
+
261
+ **`evaluate` flags:**
262
+
263
+ - `--strict-unknown-assignment-keys`: reject extra assignment keys
264
+ - `--no-expression-values`: omit per-expression truth values from output
265
+ - `--no-diagnostics`: omit inference diagnostics from output
266
+ - `--no-validate-first`: skip evaluability validation before evaluating
267
+ - `--skip-analysis-file-validation`: skip analysis file validation (symbol/ID mismatch checks)
268
+
269
+ **`check-validity` flags:**
270
+
271
+ - `--mode <mode>`: `first-counterexample` (default) or `exhaustive`
272
+ - `--max-variables <n>`: maximum number of variables to allow
273
+ - `--max-assignments-checked <n>`: maximum assignments to enumerate
274
+ - `--include-counterexample-evaluations`: include full evaluation payloads for counterexamples
275
+ - `--no-validate-first`: skip evaluability validation
276
+
277
+ All analysis subcommands default to `analysis.json` when `--file` is not specified.
278
+
279
+ ## Storage Utilities
280
+
281
+ **Source:** `src/cli/storage/`
282
+
283
+ | File | Key Functions |
284
+ | -------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
285
+ | `arguments.ts` | `readArgumentMeta`, `writeArgumentMeta`, `readVersionMeta`, `writeVersionMeta`, `listArgumentIds`, `listVersionNumbers`, `latestVersionNumber`, `deleteVersionDir`, `deleteArgumentDir`, `copyVersionDir` |
286
+ | `variables.ts` | `readVariables`, `writeVariables` |
287
+ | `roles.ts` | `readRoles`, `writeRoles` |
288
+ | `premises.ts` | `readPremiseMeta`, `writePremiseMeta`, `readPremiseData`, `writePremiseData`, `listPremiseIds`, `deletePremiseDir`, `premiseExists` |
289
+ | `analysis.ts` | `readAnalysis`, `writeAnalysis`, `listAnalysisFiles`, `deleteAnalysisFile`, `analysisFileExists`, `resolveAnalysisFilename` |
290
+
291
+ Most disk reads use `Value.Parse(Schema, raw)` from `typebox/value`, which throws on invalid data. Some reads (e.g. `readVersionMeta`) use `Value.Decode` instead. `listAnalysisFiles` uses `Value.Check` to silently skip corrupt files.
292
+
293
+ Local CLI schemata (in `src/cli/schemata.ts`) use optional `checksum` fields for backward compatibility with older data files that may not include checksums.
294
+
295
+ ## Output Helpers
296
+
297
+ **Source:** `src/cli/output.ts`
298
+
299
+ | Function | Behavior |
300
+ | ----------------------------- | -------------------------------------------------------------------------------------------------------------------------- |
301
+ | `printJson(value)` | `JSON.stringify(value, null, 2)` + newline to stdout |
302
+ | `printLine(text)` | Text + newline to stdout |
303
+ | `errorExit(message, code=1)` | Message + newline to stderr, then `process.exit(code)`. Return type is `never` |
304
+ | `requireConfirmation(prompt)` | Reads from `/dev/tty` (falls back to stdin). Expects the user to type `"confirm"`. Calls `errorExit("Aborted.")` otherwise |