@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.
- package/README.md +14 -11
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/lib/core/ArgumentEngine.d.ts +23 -19
- package/dist/lib/core/ArgumentEngine.d.ts.map +1 -1
- package/dist/lib/core/ArgumentEngine.js +11 -11
- package/dist/lib/core/ArgumentEngine.js.map +1 -1
- package/dist/lib/core/ChangeCollector.d.ts +13 -14
- package/dist/lib/core/ChangeCollector.d.ts.map +1 -1
- package/dist/lib/core/ChangeCollector.js.map +1 -1
- package/dist/lib/core/ExpressionManager.d.ts +15 -13
- package/dist/lib/core/ExpressionManager.d.ts.map +1 -1
- package/dist/lib/core/ExpressionManager.js +52 -22
- package/dist/lib/core/ExpressionManager.js.map +1 -1
- package/dist/lib/core/PremiseManager.d.ts +21 -20
- package/dist/lib/core/PremiseManager.d.ts.map +1 -1
- package/dist/lib/core/PremiseManager.js +19 -24
- package/dist/lib/core/PremiseManager.js.map +1 -1
- package/dist/lib/core/VariableManager.d.ts +7 -8
- package/dist/lib/core/VariableManager.d.ts.map +1 -1
- package/dist/lib/core/VariableManager.js +4 -1
- package/dist/lib/core/VariableManager.js.map +1 -1
- package/dist/lib/core/diff.d.ts +1 -1
- package/dist/lib/core/diff.d.ts.map +1 -1
- package/dist/lib/core/diff.js +19 -11
- package/dist/lib/core/diff.js.map +1 -1
- package/dist/lib/schemata/propositional.d.ts.map +1 -1
- package/dist/lib/schemata/propositional.js +0 -1
- package/dist/lib/schemata/propositional.js.map +1 -1
- package/dist/lib/schemata/shared.d.ts +4 -0
- package/dist/lib/schemata/shared.d.ts.map +1 -1
- package/dist/lib/types/diff.d.ts +15 -15
- package/dist/lib/types/diff.d.ts.map +1 -1
- package/dist/lib/types/mutation.d.ts +7 -20
- package/dist/lib/types/mutation.d.ts.map +1 -1
- package/dist/lib/utils/position.d.ts +9 -3
- package/dist/lib/utils/position.d.ts.map +1 -1
- package/dist/lib/utils/position.js +8 -3
- package/dist/lib/utils/position.js.map +1 -1
- package/package.json +3 -2
- package/skills/proposit-core/SKILL.md +35 -0
- package/skills/proposit-core/docs/api-usage.md +442 -0
- package/skills/proposit-core/docs/architecture.md +256 -0
- package/skills/proposit-core/docs/cli.md +304 -0
- package/skills/proposit-core/docs/testing.md +281 -0
- 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 |
|