inistate-core 0.1.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.
package/LICENSE ADDED
@@ -0,0 +1,82 @@
1
+ INISTATE CORE — FREE-USE END USER LICENSE AGREEMENT
2
+ Version 1.0
3
+
4
+ Copyright (c) GNEY SOFTWARE PTE LTD. All rights reserved.
5
+
6
+ This End User License Agreement ("Agreement") is a legal agreement between you
7
+ (an individual or a single entity, "You") and GNEY SOFTWARE PTE LTD ("Licensor")
8
+ for the inistate-core software, including the compiled binaries, bundled data
9
+ files, and accompanying documentation (collectively, the "Software").
10
+
11
+ inistate-core is FREE TO USE but is NOT open-source software. It is distributed
12
+ as a closed, compiled artifact. The Software's source code is not provided, and
13
+ this Agreement grants no rights to its source code.
14
+
15
+ 1. GRANT OF LICENSE
16
+ Subject to Your compliance with this Agreement, Licensor grants You a
17
+ worldwide, royalty-free, non-exclusive, non-transferable license to install
18
+ and run the Software, in unlimited quantity and for an unlimited period,
19
+ for any purpose, including production and commercial use, on infrastructure
20
+ You operate or control. You may run the Software forever at no charge.
21
+
22
+ 2. RESTRICTIONS
23
+ You may NOT:
24
+ (a) reverse engineer, decompile, or disassemble the Software, or otherwise
25
+ attempt to derive its source code, except to the extent this restriction
26
+ is expressly prohibited by applicable law;
27
+ (b) modify, adapt, or create derivative works of the Software's engine;
28
+ (c) redistribute, sublicense, sell, rent, lease, or host the Software as a
29
+ service to third parties, except for unmodified redistribution of the
30
+ official published package for installation by end users under this
31
+ Agreement;
32
+ (d) remove or alter any proprietary notices, labels, or this Agreement;
33
+ (e) use the Software to build a product that competes with the Inistate
34
+ Platform.
35
+
36
+ 3. OPEN COMPONENTS
37
+ The Software embeds and is designed to interoperate with separately published
38
+ open-source components — the Inistate schema and the Inistate MCP server —
39
+ which are licensed under the MIT License in their own repositories. Nothing
40
+ in this Agreement restricts Your rights under those licenses with respect to
41
+ those components. This Agreement governs only the closed inistate-core engine
42
+ and the compiled artifact as a whole.
43
+
44
+ 4. NO PATENT LICENSE
45
+ No license, immunity, or other right is granted to You under any patent,
46
+ patent application, or other intellectual property of Licensor or its
47
+ affiliates, whether by implication, estoppel, or otherwise, except for the
48
+ limited right to run the Software as expressly set out in Section 1.
49
+
50
+ 5. RESERVATION OF RIGHTS
51
+ The Software is licensed, not sold. Licensor retains all right, title, and
52
+ interest in and to the Software, including all intellectual property rights.
53
+
54
+ 6. DISCLAIMER OF WARRANTY
55
+ THE SOFTWARE IS PROVIDED "AS IS" AND "AS AVAILABLE", WITHOUT WARRANTY OF ANY
56
+ KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
57
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE, AND
58
+ NON-INFRINGEMENT. YOU BEAR THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE
59
+ OF THE SOFTWARE.
60
+
61
+ 7. LIMITATION OF LIABILITY
62
+ TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, IN NO EVENT SHALL LICENSOR
63
+ BE LIABLE FOR ANY SPECIAL, INCIDENTAL, INDIRECT, OR CONSEQUENTIAL DAMAGES
64
+ WHATSOEVER, OR FOR ANY LOSS OF DATA, PROFITS, OR BUSINESS, ARISING OUT OF OR
65
+ IN ANY WAY RELATED TO THE USE OF OR INABILITY TO USE THE SOFTWARE, EVEN IF
66
+ ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
67
+
68
+ 8. DATA AND PRIVACY
69
+ The Software runs locally against persistence You control and performs no
70
+ network communication by default. Licensor does not collect Your records.
71
+
72
+ 9. TERMINATION
73
+ This Agreement is effective until terminated. It terminates automatically if
74
+ You breach any term. Upon termination You must cease all use of the Software.
75
+ Sections 4 through 7 survive termination.
76
+
77
+ 10. GOVERNING LAW
78
+ This Agreement is governed by the laws of Singapore, without regard to its
79
+ conflict-of-laws principles.
80
+
81
+ By installing or using the Software, You acknowledge that You have read this
82
+ Agreement and agree to be bound by its terms.
package/README.md ADDED
@@ -0,0 +1,283 @@
1
+ # inistate-core
2
+
3
+ **A free, local runtime for FACTSOps workflows — the deterministic floor of the Inistate stack.**
4
+
5
+ `inistate-core` runs governed state-machine workflows on persistence *you* control, driven over the [Model Context Protocol](https://modelcontextprotocol.io). One command, zero config, no account, no token, no network. Any MCP-compatible agent can drive it.
6
+
7
+ It enforces the **present state** of your records — only legal transitions, validated forms, atomic writes — and nothing more. It is deliberately free of governance, authorization, and history.
8
+
9
+ > **Core manages the _present state_ of your records. The Inistate Platform manages the _governed history_ of how that state changed** — together with the identity, authorization, and escalation a governed history implies.
10
+
11
+ That line is not a limitation list; it is physics. Enforcing the present state needs only a record's current value — any store can hold that, which is why Core runs directly on your own data. Recording an accountable, append-only, actor-attributed past needs a governed store a plain table doesn't have. So Core holds no history of any kind — and that is the point.
12
+
13
+ ---
14
+
15
+ ## Quickstart — win the first 30 seconds
16
+
17
+ ```bash
18
+ npx inistate-core
19
+ ```
20
+
21
+ That's a complete deployment: a zero-config SQLite store at `~/.inistate/core.db`, serving the Inistate MCP tool surface over stdio.
22
+
23
+ Wire it into any MCP client (Claude Desktop, Claude Code, Cursor, VS Code, …):
24
+
25
+ ```json
26
+ {
27
+ "mcpServers": {
28
+ "inistate": {
29
+ "command": "npx",
30
+ "args": ["-y", "inistate-core"]
31
+ }
32
+ }
33
+ }
34
+ ```
35
+
36
+ No token. No workspace. No setup wizard. Point your agent at it and design a workflow, create entries, and drive transitions — all on local storage.
37
+
38
+ ---
39
+
40
+ ## Editions
41
+
42
+ | | **Inistate Core** | **Inistate Core Plus** | **Inistate Platform** |
43
+ |---|---|---|---|
44
+ | Price | Free | **$9.99 one-time** | Paid (teams) |
45
+ | For | make one data source AI-native | make your whole stack AI-native | govern human-AI operations |
46
+ | Connectors | full connector support — **one backend type at a time** | **multiple backend types at once** | managed sync |
47
+ | Also | deterministic runtime, valid transitions, typed forms | all Core + cross-source workflow modules, presets | people, roles, audit, UI, confidence/intention gating |
48
+
49
+ > **Core makes one persistence layer AI-native. Core Plus connects many. Platform governs the organization.**
50
+
51
+ - **Free** — make one *kind* of data source AI-native (any number of files/tables of that kind, e.g. several Excel workbooks).
52
+ - **$9.99 once** — mix multiple *kinds* of source at once: Sheets, Notion, Airtable, SQLite, CSV, Excel, or your own adapter, side by side.
53
+ - **Platform** — make the whole organization governed.
54
+
55
+ Core is not crippled — it runs the full primitive on one backend type (and unlimited resources of that type). Core Plus is a one-time unlock for *mixing backend types*: set `INISTATE_CORE_PLUS_KEY` and map modules with `INISTATE_CORE_CONNECTIONS` (see [Connecting multiple tools](#connecting-multiple-tools--core-plus)). A single backend type never needs a license.
56
+
57
+ ---
58
+
59
+ ## What's inside the boundary
60
+
61
+ | In Core (free, local) | In the Inistate Platform (paid) |
62
+ |---|---|
63
+ | Schema interpretation | Human collaboration, web/mobile UI |
64
+ | **Present-state enforcement** (legal transitions) | Visual designer + app generation |
65
+ | Typed activity forms + validation | Auth / roles, files |
66
+ | Module design + validation | **Governed history & audit trail** |
67
+ | Persistence-agnostic storage | **Confidence / intention gating** |
68
+ | Descriptive `ai_hint` (*describe, never govern*) | Workspaces, the integrated AI Teammate |
69
+
70
+ ### The reliability invariants are the product
71
+
72
+ Core gives away the hard parts so you don't hand-roll them. Each is property-tested, not just example-tested:
73
+
74
+ - **Atomic transitions** — field writes and the state change commit together or not at all.
75
+ - **Illegal-transition rejection** — no accepted operation can move an entry into a state its flows don't permit. *(This is the single most important guarantee.)*
76
+ - **Idempotency** — replaying a submission with the same `idempotencyKey` applies the change at most once.
77
+ - **Optimistic concurrency** — concurrent writers: exactly one wins; the other gets `CONFLICT`, no lost update.
78
+ - **Crash durability** — a committed transition survives a process crash and restart (SQLite WAL).
79
+
80
+ ---
81
+
82
+ ## The tool surface
83
+
84
+ Core serves the identical Inistate MCP tools. The runtime set is always on; module-design tools appear after `switch_mode("configure")`.
85
+
86
+ **Served on Core (`Both`):** `list_modules`, `get_module_schema`, `get_module_canvas`, `list_entries`, `get_entry`, `get_form`, `submit_activity`, `submit_activities`, `design_workflow`, `validate_design`, `create_module`, `update_module`, `switch_mode`.
87
+
88
+ **Platform-only tools** (`login`, `list_workspaces`, `set_workspace`, `get_entry_history`, `upload_file`, `download_file`, `request_upload_url`, `confirm_upload`) are **not silently absent** — calling one returns a structured capability message so your agent can explain the gap rather than fabricate a result:
89
+
90
+ ```json
91
+ {
92
+ "error": "capability_unavailable",
93
+ "backend": "core",
94
+ "capability": "governed_history",
95
+ "message": "Governed history is not available on the local runtime. A local store keeps the current state of each record, not an accountable, append-only record of how it changed.",
96
+ "upgrade": "Connect to the Inistate Platform to enable governed history."
97
+ }
98
+ ```
99
+
100
+ `submit_activity` also accepts two reliability controls Core honours: `idempotencyKey` (safe retries) and `expectedVersion` (optimistic concurrency, read `version` from `get_entry`).
101
+
102
+ ---
103
+
104
+ ## Configuration
105
+
106
+ Everything has a working default — a bare `inistate-core` is a valid deployment.
107
+
108
+ | Setting | Env var | Default | Notes |
109
+ |---|---|---|---|
110
+ | Database | `INISTATE_CORE_DB` | `~/.inistate/core.db` | SQLite path, `memory`, or `<scheme>://…` for a registered adapter |
111
+ | Multi-connection | `INISTATE_CORE_CONNECTIONS` | — | JSON map module → target. Mixing multiple backend types — needs Core Plus |
112
+ | Core Plus license | `INISTATE_CORE_PLUS_KEY` | — | Unlocks mixing multiple backend types at once (one-time $9.99) |
113
+ | Custom adapter | `INISTATE_CORE_ADAPTER` | — | Module that registers your own persistence (see below) |
114
+ | Server mode | `INISTATE_MCP_MODE` | `runtime` | `runtime` or `configure` |
115
+ | Logging | `INISTATE_LOG` | on | Set to `0` to silence stderr JSON logs |
116
+ | Notion token | `INISTATE_NOTION_TOKEN` | — | Required for `notion://` targets |
117
+ | Airtable token | `INISTATE_AIRTABLE_TOKEN` | — | Required for `airtable://` targets |
118
+ | Google token | `INISTATE_GOOGLE_TOKEN` | — | OAuth2 access token, required for `sheets://` targets |
119
+
120
+ ### Storage schemes & connectors
121
+
122
+ Selected by the scheme of `INISTATE_CORE_DB`. Built-in backends are dependency-free; standard connectors run a governed workflow **on your existing data**.
123
+
124
+ | Scheme | `INISTATE_CORE_DB` example | Needs | Class / tier |
125
+ |---|---|---|---|
126
+ | `sqlite` *(default)* | `~/.inistate/core.db`, or any file path | — | Built-in — **full** present-state, durable (SQLite WAL) |
127
+ | `memory` | `memory` | — | Built-in — non-durable (tests/fallback) |
128
+ | `csv` | `csv://./tasks.csv` | — | Built-in (local file) — exposure tier (§5.5) |
129
+ | `excel` | `excel://./book.xlsx?sheet=Sheet1` | optional `exceljs` | Standard (local file) — exposure tier (§5.5) |
130
+ | `notion` | `notion://<databaseId>?module=Tasks&state=Status` | `INISTATE_NOTION_TOKEN` | Standard — exposure tier (§5.5) |
131
+ | `airtable` | `airtable://<baseId>/<table>?module=Tasks&state=Status` | `INISTATE_AIRTABLE_TOKEN` | Standard — exposure tier (§5.5) |
132
+ | `sheets` | `sheets://<spreadsheetId>/Sheet1?state=Status` | `INISTATE_GOOGLE_TOKEN` | Standard — exposure tier (§5.5) |
133
+
134
+ Postgres is on the roadmap. Any other `<scheme>://…` resolves through a custom adapter you register with `INISTATE_CORE_ADAPTER` (community/custom tier — see [Bring your own persistence](#bring-your-own-persistence)). All standard connectors are available in Core; see [Notion & Airtable](#notion--airtable-built-in-exposure-tier) and [`examples/connectors.md`](examples/connectors.md) for every connector's full config.
135
+
136
+ ### Connecting multiple tools — Core Plus
137
+
138
+ Core runs **one backend type at a time** — any number of resources of that kind (e.g. several Excel workbooks) is free. To connect **multiple kinds of backend/webservice at once** — routing different modules to different tools (e.g. `NEC` on Notion, `Tasks` in a CSV, `Leads` in Airtable) — unlock **Inistate Core Plus** (one-time $9.99): set `INISTATE_CORE_PLUS_KEY` and map modules with `INISTATE_CORE_CONNECTIONS`:
139
+
140
+ ```bash
141
+ INISTATE_NOTION_TOKEN=ntn_… \
142
+ INISTATE_CORE_PLUS_KEY=<your core-plus key> \
143
+ INISTATE_CORE_CONNECTIONS='{"NEC":"notion://<dbId>?state=Status","Tasks":"csv://./tasks.csv","Leads":"airtable://<base>/<table>"}' \
144
+ npx inistate-core
145
+ ```
146
+
147
+ Each module gets its own connection, all in one runtime. Multiple resources of a *single* backend type — several Notion databases, or two Excel workbooks — count as one connector and stay free; the gate only fires when you mix *different* kinds of backend. Without a license, a mixed-type config prints a clear upgrade message and refuses to start (single-type mode is unaffected).
148
+
149
+ ---
150
+
151
+ ## Bring your own persistence
152
+
153
+ The `StorageAdapter` interface is the **open seam**: SQLite (default) and an in-memory store ship built in, and you can plug in your own — a team Postgres, or an external substrate like Notion / Airtable / a spreadsheet — without forking. The engine owns correctness; your adapter owns storage. The one hard rule: `transaction(fn)` must be **all-or-nothing**, or the reliability invariants can't hold on your store.
154
+
155
+ **For the binary** — point `INISTATE_CORE_ADAPTER` at a module that registers an adapter against a URL scheme, then select it with `INISTATE_CORE_DB`:
156
+
157
+ ```bash
158
+ INISTATE_CORE_ADAPTER=./my-adapter.js \
159
+ INISTATE_CORE_DB=mystore://./data \
160
+ npx inistate-core
161
+ ```
162
+
163
+ Your module exports either `register(api)` or a default `{ scheme, create }`:
164
+
165
+ ```js
166
+ // my-adapter.js
167
+ export function register(api) {
168
+ api.registerAdapter({
169
+ scheme: "mystore",
170
+ description: "My durable store",
171
+ create: (target) => new MyAdapter(target),
172
+ });
173
+ }
174
+ ```
175
+
176
+ **Embedding Core** in your own Node process:
177
+
178
+ ```ts
179
+ import { createRuntime, loadConfig, registerAdapter } from "inistate-core";
180
+ import type { StorageAdapter } from "inistate-core/adapter";
181
+
182
+ registerAdapter({ scheme: "mystore", create: (t) => new MyAdapter(t) });
183
+ const runtime = createRuntime(loadConfig());
184
+ // runtime.backend → wire into createServer(); runtime.engine → drive directly
185
+ ```
186
+
187
+ Implement `StorageAdapter` from `inistate-core/adapter` — `applyQuery` and `makeDocumentId` are exported so your adapter inherits identical query/document-id semantics (and passes the shared conformance suite). A complete, copy-pasteable reference adapter that persists to a single JSON file with atomic commits is in [`examples/json-store-adapter.ts`](examples/json-store-adapter.ts).
188
+
189
+ ### Notion & Airtable (built-in, exposure tier)
190
+
191
+ Core ships reference adapters that run governed workflows **on top of your existing Notion databases or Airtable tables** — the friendliest on-ramp: keep your board, watch the engine block an illegal move on your own data. One Core instance can back **many modules**, each bound to its own table.
192
+
193
+ ```bash
194
+ # Notion — single database → one module (each page is an entry; a Status property holds the state)
195
+ INISTATE_NOTION_TOKEN=secret_xxx \
196
+ INISTATE_CORE_DB="notion://<databaseId>?module=Tasks&state=Status" \
197
+ npx inistate-core
198
+
199
+ # Notion — multiple databases → multiple modules
200
+ INISTATE_NOTION_TOKEN=secret_xxx \
201
+ INISTATE_CORE_DB="notion://?LeaveRequests=<db1>&Expenses=<db2>" \
202
+ npx inistate-core
203
+ # …or map them with INISTATE_NOTION_DATABASES='{"LeaveRequests":"db1","Expenses":"db2"}'
204
+
205
+ # Airtable — single table → one module
206
+ INISTATE_AIRTABLE_TOKEN=pat_xxx \
207
+ INISTATE_CORE_DB="airtable://<baseId>/<tableIdOrName>?module=Tasks&state=Status" \
208
+ npx inistate-core
209
+
210
+ # Airtable — many tables in one base → many modules
211
+ INISTATE_AIRTABLE_TOKEN=pat_xxx \
212
+ INISTATE_CORE_DB="airtable://<baseId>?Tasks=<tbl1>&Leads=<tbl2>" \
213
+ npx inistate-core
214
+ # …or map them with INISTATE_AIRTABLE_TABLES='{"Tasks":"tbl1","Leads":"tbl2"}'
215
+ ```
216
+
217
+ Create each Core module with the **same name** you mapped (`create_module({ name: "LeaveRequests", … })`); that module's entries then live in its bound table. They use **your own credentials** (opt-in, never the default). How they work, honestly: Core hydrates each substrate's rows into a per-module in-memory cache at startup, **enforces present-state against that cache** (so illegal transitions are rejected through Core), and **mirrors committed changes back to each table** with a best-effort, ordered write-back — failures are logged, never hidden.
218
+
219
+ **What's stored where.** Entry *records* live in the substrate (Notion/Airtable). The things a table can't express — your **module definitions** (states, activities, flows) and the **idempotency map** — persist in a small **SQLite meta sidecar** by default (`~/.inistate/<scheme>-<id>.db`, one per database/base), so a restart rehydrates records from the substrate and reloads the schema from SQLite without re-creating the module. Override the location with `?meta=<path>`, or use `?meta=memory` for an ephemeral meta store.
220
+
221
+ **Schema changes are additive.** Your module's `information` fields + the state column are a subset of the connecting table's columns. `create_module`/`update_module` reconcile that **add-only**: a new field **adds** a column to the substrate (a Notion property via `PATCH /databases`, or an Airtable field via the Metadata API — which needs a PAT with `schema.bases:write`), and missing select options are merged in. Core **never drops** a column — removing a field from the schema just stops mapping to it; the column stays in your table, untouched. Provisioning is best-effort (a failure, e.g. a missing Airtable scope, is logged, not fatal — enforcement still holds against the cache). Disable it entirely with `?provision=off` to leave your table structure alone.
222
+
223
+ Build your own substrate adapter by implementing a small async `SubstrateDriver` (`list` / `create` / `update` / `remove`, plus optional add-only `ensureColumns`) and reusing `ExternalSubstrateAdapter` — see [`src/storage/external/`](src/storage/external/).
224
+
225
+ > **Two honest guarantee tiers (§5.5).** Core's own store (SQLite/Postgres) gives a full present-state guarantee, including against out-of-band corruption within the transactional boundary. An external substrate (Notion/Airtable/Sheets) holds enforcement *through Core*, but out-of-band hand-edits are possible — so the guarantee softens to "no illegal move *through Core*". Label that tier; attribute the limit to the substrate.
226
+
227
+ ---
228
+
229
+ ## Scaffold a schema from an existing table
230
+
231
+ Already have a Notion database, Airtable table, or SQLite table? Generate a starter FACTSOps module schema from it — columns become typed fields, and a detected status column becomes states:
232
+
233
+ ```bash
234
+ inistate-core scaffold notion://<databaseId> # needs INISTATE_NOTION_TOKEN
235
+ inistate-core scaffold airtable://<baseId>/<table> # needs INISTATE_AIRTABLE_TOKEN
236
+ inistate-core scaffold ./core.db --table leave_requests # local SQLite, no creds
237
+
238
+ # pipe the schema straight to a file (guidance prints to stderr, schema to stdout)
239
+ inistate-core scaffold ./core.db --table leave_requests --name "Leave Requests" > module.json
240
+ ```
241
+
242
+ It prints a `ModuleSchema` to stdout and a validation summary + next steps to stderr. Override with `--name <Module>` and `--state <Column>`. Core deliberately leaves `activities`/`flows` empty — it can't infer the *governed transitions* a plain table doesn't express; defining those is the value Core adds. Finish the schema (e.g. with `design_workflow` for a pattern), `create_module`, then point Core at the same substrate and illegal moves are rejected.
243
+
244
+ The same thing is available to an agent over MCP as the **`scaffold_module`** tool (configure mode — `switch_mode` to `configure`), designed so the user and the agent design the schema **together**. One container (a SQLite database, an Airtable base, a Notion workspace) often holds several tables — so the tool discovers them first and you map the chosen ones, each as its own module:
245
+
246
+ 1. **Discover** — call with just `source` pointed at the container: a SQLite database path, `airtable://<baseId>` (no table), or a bare `notion://`. It returns `{ discovery: true, tables: [{ name, id?, columns, rows?, hasState, scaffold_source }] }` so the agent can show what's there and ask the user which one(s) to model.
247
+ 2. **Draft** — call again with a chosen table's `scaffold_source` as `source` (or a SQLite `table`), plus optional `name` / `state`. It returns `{ schema, validation, suggestions }` — columns become typed fields, a detected status column becomes states.
248
+ 3. **Refine** — confirm the inferred field types with the user, then define `activities`/`flows` (e.g. via `design_workflow`).
249
+ 4. **Create** — `validate_design → create_module`, then point Core at the same data.
250
+
251
+ Network targets read `INISTATE_NOTION_TOKEN` / `INISTATE_AIRTABLE_TOKEN` from the environment — tokens are never passed as tool arguments. Listing an Airtable base's tables uses the Metadata API, which needs the token's `schema.bases:read` scope; single-table scaffolding does not.
252
+
253
+ Programmatic equivalent: `import { introspectSqlite, scaffoldModule } from "inistate-core"` (or `introspectTarget` to dispatch by URI).
254
+
255
+ ## Upgrading to the Platform
256
+
257
+ ```bash
258
+ npx inistate-core connect
259
+ ```
260
+
261
+ Moving to the Inistate Platform is a **backend swap, not a migration** — the same schema, the same MCP tools, re-pointed from the local engine to the Platform API. You *gain* people, UI, governed history, authorization, and the Teammate. Your workflows, vocabulary, and agent integrations carry over unchanged. A team can run real production on local Core forever and never pay — that is intended.
262
+
263
+ ---
264
+
265
+ ## Development
266
+
267
+ ```bash
268
+ npm install # installs deps and builds
269
+ npm run build # tsc → build/
270
+ npm test # vitest: invariants, conformance, capability, boundary, durability, end-to-end MCP
271
+ npm run scan # forbidden-symbol boundary scan over the engine + storage
272
+ npm run check # build + scan + test (the full gate)
273
+ ```
274
+
275
+ The engine and storage adapters are kept clean of governance/UI/history concepts by a **forbidden-symbol scan** enforced in CI (`scan`). The shared **conformance suite** runs the same transition scenario against every storage backend (including a developer-style custom adapter) and asserts identical outcomes — the guard against semantic drift.
276
+
277
+ ---
278
+
279
+ ## License
280
+
281
+ `inistate-core` is **free to use** but **not open source**. It ships under a proprietary free-use EULA (see [LICENSE](LICENSE)): run it anywhere, forever, at no cost — including in production — but the engine source is closed and no patent rights are granted. The embedded Inistate schema and MCP server are separately published under MIT.
282
+
283
+ © GNEY SOFTWARE PTE LTD.