pythia-mongo 0.1.0__tar.gz

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 (43) hide show
  1. pythia_mongo-0.1.0/LICENSE +21 -0
  2. pythia_mongo-0.1.0/PKG-INFO +395 -0
  3. pythia_mongo-0.1.0/README.md +367 -0
  4. pythia_mongo-0.1.0/pyproject.toml +43 -0
  5. pythia_mongo-0.1.0/setup.cfg +4 -0
  6. pythia_mongo-0.1.0/src/pythia_mongo/__init__.py +5 -0
  7. pythia_mongo-0.1.0/src/pythia_mongo/__main__.py +5 -0
  8. pythia_mongo-0.1.0/src/pythia_mongo/catalog.py +124 -0
  9. pythia_mongo-0.1.0/src/pythia_mongo/cli.py +8 -0
  10. pythia_mongo-0.1.0/src/pythia_mongo/commands/collection.json +16 -0
  11. pythia_mongo-0.1.0/src/pythia_mongo/commands/collections.json +29 -0
  12. pythia_mongo-0.1.0/src/pythia_mongo/commands/connection-status.json +8 -0
  13. pythia_mongo-0.1.0/src/pythia_mongo/commands/count.json +12 -0
  14. pythia_mongo-0.1.0/src/pythia_mongo/commands/duplicates.json +43 -0
  15. pythia_mongo-0.1.0/src/pythia_mongo/commands/indexes.json +12 -0
  16. pythia_mongo-0.1.0/src/pythia_mongo/commands/invalid.json +45 -0
  17. pythia_mongo-0.1.0/src/pythia_mongo/commands/pipeline-run.json +18 -0
  18. pythia_mongo-0.1.0/src/pythia_mongo/commands/probe-sample.json +28 -0
  19. pythia_mongo-0.1.0/src/pythia_mongo/commands/probe.json +37 -0
  20. pythia_mongo-0.1.0/src/pythia_mongo/commands/sample.json +23 -0
  21. pythia_mongo-0.1.0/src/pythia_mongo/commands/server-build.json +9 -0
  22. pythia_mongo-0.1.0/src/pythia_mongo/commands/server-hello.json +9 -0
  23. pythia_mongo-0.1.0/src/pythia_mongo/commands/view-run.json +24 -0
  24. pythia_mongo-0.1.0/src/pythia_mongo/engine.py +301 -0
  25. pythia_mongo-0.1.0/src/pythia_mongo/guard.py +58 -0
  26. pythia_mongo-0.1.0/src/pythia_mongo/read.py +346 -0
  27. pythia_mongo-0.1.0/src/pythia_mongo/rules.py +132 -0
  28. pythia_mongo-0.1.0/src/pythia_mongo/skills/pythia-mongo-apply/SKILL.md +101 -0
  29. pythia_mongo-0.1.0/src/pythia_mongo/skills/pythia-mongo-apply/reference/refusals.md +31 -0
  30. pythia_mongo-0.1.0/src/pythia_mongo/skills/pythia-mongo-autopilot/SKILL.md +62 -0
  31. pythia_mongo-0.1.0/src/pythia_mongo/skills/pythia-mongo-deploy/SKILL.md +66 -0
  32. pythia_mongo-0.1.0/src/pythia_mongo/skills/pythia-mongo-explore/SKILL.md +65 -0
  33. pythia_mongo-0.1.0/src/pythia_mongo/skills/pythia-mongo-impact/SKILL.md +60 -0
  34. pythia_mongo-0.1.0/src/pythia_mongo/skills/pythia-mongo-verify/SKILL.md +58 -0
  35. pythia_mongo-0.1.0/src/pythia_mongo/skills/using-pythia-mongo/SKILL.md +68 -0
  36. pythia_mongo-0.1.0/src/pythia_mongo/write.py +714 -0
  37. pythia_mongo-0.1.0/src/pythia_mongo.egg-info/PKG-INFO +395 -0
  38. pythia_mongo-0.1.0/src/pythia_mongo.egg-info/SOURCES.txt +41 -0
  39. pythia_mongo-0.1.0/src/pythia_mongo.egg-info/dependency_links.txt +1 -0
  40. pythia_mongo-0.1.0/src/pythia_mongo.egg-info/entry_points.txt +2 -0
  41. pythia_mongo-0.1.0/src/pythia_mongo.egg-info/requires.txt +2 -0
  42. pythia_mongo-0.1.0/src/pythia_mongo.egg-info/top_level.txt +1 -0
  43. pythia_mongo-0.1.0/tests/test_mongo.py +1235 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 thaild
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,395 @@
1
+ Metadata-Version: 2.4
2
+ Name: pythia-mongo
3
+ Version: 0.1.0
4
+ Summary: pythia for MongoDB: database development for AI agents over pythia-core - validators, views and indexes read live, impact with its blind spots named, and an approved write path.
5
+ License-Expression: MIT
6
+ Project-URL: Repository, https://github.com/thaildhe172591/pythia-mongo
7
+ Project-URL: Changelog, https://github.com/thaildhe172591/pythia-mongo/blob/main/CHANGELOG.md
8
+ Project-URL: Issues, https://github.com/thaildhe172591/pythia-mongo/issues
9
+ Keywords: mongodb,database,agent-skills
10
+ Classifier: Development Status :: 3 - Alpha
11
+ Classifier: Environment :: Console
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: Operating System :: OS Independent
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.9
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Programming Language :: Python :: 3.13
20
+ Classifier: Topic :: Database
21
+ Classifier: Topic :: Software Development
22
+ Requires-Python: >=3.9
23
+ Description-Content-Type: text/markdown
24
+ License-File: LICENSE
25
+ Requires-Dist: pythia-core<0.2,>=0.1.0
26
+ Requires-Dist: pymongo>=4.9
27
+ Dynamic: license-file
28
+
29
+ # pythia-mongo
30
+
31
+ **English** · [Tiếng Việt](https://github.com/thaildhe172591/pythia-mongo/blob/main/README.vi.md)
32
+
33
+ pythia for MongoDB: database development for AI agents, built on
34
+ [pythia-core](https://github.com/thaildhe172591/pythia-core). It aims to do
35
+ what [pythia](https://github.com/thaildhe172591/pythia) does on Oracle,
36
+ [pythia-postgres](https://github.com/thaildhe172591/pythia-postgres) on
37
+ PostgreSQL and [pythia-mssql](https://github.com/thaildhe172591/pythia-mssql)
38
+ on SQL Server: the agent learns from the live database, asks a person before
39
+ any write, and writes only through a gate that snapshots, verifies and
40
+ reports honestly.
41
+
42
+ **Alpha (0.1.0).** It reads everything pythia's Learn movement needs, writes through one gate — data rehearsed in a transaction;
43
+ validators, views and indexes predicted, written once, and read back — and
44
+ wires a project for an agent: the guard, the session brief, the approval
45
+ doors and the `pythia-mongo-*` skills.
46
+
47
+ | Works today |
48
+ |---|
49
+ | reads: `check`, `ls`, `src`, `cols`, `ddl`, `grep`, `deps`, `impact`, `errors`, `connections` |
50
+ | writes: `apply`, `deploy`, `approve`, `journal`, `history`, `policy`, `autopilot`, `lint` |
51
+ | the harness: `install`, `doctor`, `hook`, `mcp`, `skill lint` |
52
+
53
+ MongoDB 7.0 and 8.0 are tested; 7.0 is the floor.
54
+
55
+ ## Install
56
+
57
+ ```bash
58
+ pip install pythia-mongo
59
+ ```
60
+
61
+ That brings pythia-core, and [pymongo](https://pypi.org/project/pymongo/),
62
+ the only driver it uses. The development version is the main branch of
63
+ both:
64
+
65
+ ```bash
66
+ pip install "git+https://github.com/thaildhe172591/pythia-core" \
67
+ "git+https://github.com/thaildhe172591/pythia-mongo"
68
+ ```
69
+
70
+ ## Set up a project for an agent
71
+
72
+ ```bash
73
+ cd your-project
74
+ pythia-mongo install # once; a second run changes nothing
75
+ pythia-mongo doctor # is everything wired? exit 1 names what is not
76
+ ```
77
+
78
+ `install` writes, and leaves everything else as it was:
79
+
80
+ - `.pythia/mongo/connections.json`, mode 600, created once from the
81
+ template — the developer fills in the passwords; an agent never opens it;
82
+ - `.gitignore` lines for that file, this machine's approvals
83
+ (`.pythia/mongo/grants/`) and `.claude/settings.local.json`;
84
+ - `.claude/settings.local.json`: the hooks (the guard, the session brief,
85
+ the AskUserQuestion answer), spelled with this interpreter's absolute path
86
+ so they start whether or not a virtual environment is active, and deny
87
+ rules for the engine's files;
88
+ - the MCP approver `pythia_mongo` (tool `pythia_mongo_approve`), in this
89
+ machine's files only: Claude Code's local scope for the project
90
+ (`~/.claude.json`) and Codex's `~/.codex/config.toml`, each when that
91
+ client has run here;
92
+ - a `pythia-mongo` block in CLAUDE.md and AGENTS.md (other tools' blocks,
93
+ and the developer's own text, are left alone);
94
+ - the skills, in `.claude/skills` and `.agents/skills`:
95
+ `using-pythia-mongo` (the router), `pythia-mongo-explore`,
96
+ `pythia-mongo-impact`, `pythia-mongo-apply`, `pythia-mongo-deploy`,
97
+ `pythia-mongo-autopilot`, `pythia-mongo-verify`.
98
+
99
+ Restart the agent's session after installing, so the hooks load. Each
100
+ session then starts with the rules and a `STATE` line (connections, the
101
+ write policy). Before every tool call the guard decides:
102
+
103
+ | The agent runs | The guard |
104
+ |---|---|
105
+ | `mongosh` or the legacy `mongo` given a write (`insertOne`, `updateMany`, `deleteMany`, `replaceOne`, `drop`, `createIndex`, `createView`, `renameCollection`, `bulkWrite`, `runCommand`, `adminCommand`, `createUser`, a `$out` or `$merge` stage, `load()`), or a script (`-f`, `--file`, a `.js` or `.mjs` file, `< file`, a pipe into the shell); `mongoimport`, `mongorestore`, `mongofiles` — also behind `docker exec`, `ssh`, `sudo` or `bash -c` | denies: writes go through `pythia-mongo apply` |
106
+ | a MongoDB MCP server's tool named for a write (`insert-many`, `update-many`, `delete-many`, `drop-collection`, `create-index`, `rename-collection`, ...), or given a command document that writes (`{"drop": ...}`, `{"collMod": ...}`, a `$merge` or `$out` stage) | denies |
107
+ | reading `connections.json`; editing `policy.json`, the journal or a grant; setting `PYTHIA_MONGO_CI`; calling `grants.mint` from a one-liner | denies |
108
+ | a `pythia-mongo` read, a preview, `approve --card` | allows without a prompt |
109
+ | `apply --confirm <token>` the developer already approved on this connection | allows; apply re-verifies it |
110
+ | `mongosh` reads, `mongodump`, `mongoexport`, `approve`, `--yes`, `policy set`, anything compound | stays out: the client asks |
111
+
112
+ The guard reads a command for what it names, not what it means: it can
113
+ deny a harmless command (a `find` that mentions `.drop(` in a string, or a
114
+ `runCommand` that only reads) and miss a write spelled in a way it does not
115
+ list. It sees the commands Claude Code hands its tools, not Compass or a
116
+ driver inside the application. The gate behind it holds either way
117
+ (pythia-core's SECURITY.md).
118
+
119
+ pythia-postgres and pythia-mssql install beside it in one project, and
120
+ pythia for Oracle too: each keeps the others' hooks and instruction blocks,
121
+ and each guard and approval hook acts for its own tool only (pythia-core's
122
+ CI checks it with all four installed).
123
+
124
+ ## Connections
125
+
126
+ `.pythia/mongo/connections.json` in the project root, owner-readable only
127
+ (`chmod 600`), and out of git. Template:
128
+ [examples/connections.example.json](https://github.com/thaildhe172591/pythia-mongo/blob/main/examples/connections.example.json).
129
+
130
+ ```json
131
+ {
132
+ "default": "dev",
133
+ "dev": { "host": "db-dev", "port": 27017, "database": "app",
134
+ "user": "app_agent", "password": "...", "auth_source": "admin",
135
+ "tls": false }
136
+ }
137
+ ```
138
+
139
+ - `host` and `database` are required. `host` may list several members
140
+ (`"a:27017,b:27017"`).
141
+ - `auth_source` is the database the user lives in (default `admin`).
142
+ - `tls` (default `false`), `replica_set`, and `direct` (`true` talks to the
143
+ one member named, not the set it advertises — for a member reached
144
+ through a container's mapped port or a tunnel).
145
+ - A connection string (`"uri"`) is refused: it carries the password into
146
+ every place it is printed.
147
+ - The entry is picked by `--conn NAME`, then `PYTHIA_MONGO_CONNECTION`,
148
+ then the only entry, the folder directly under the project root, then
149
+ `"default"`. `PYTHIA_MONGO_CONFIG` points at another file.
150
+
151
+ pythia for Oracle keeps `.pythia/connections.json`, and the other engines
152
+ their own folders; pythia-mongo reads none of them.
153
+
154
+ ## Commands
155
+
156
+ ```bash
157
+ pythia-mongo check # server, replica set or not, database
158
+ pythia-mongo ls "t_order%" # collections and views by LIKE pattern
159
+ pythia-mongo src t_order # a validator, or a view's pipeline, as Extended JSON
160
+ pythia-mongo cols t_order # fields and their types, from a sample of documents
161
+ pythia-mongo ddl t_order # the commands that make it: create, createIndexes
162
+ pythia-mongo grep PAID # every validator and pipeline line holding a text
163
+ pythia-mongo deps v_order_lines # what a view reads
164
+ pythia-mongo impact t_order # what reads it: run before changing it
165
+ pythia-mongo errors [NAME] # documents a validator rejects, views that fail
166
+ ```
167
+
168
+ `--json` gives one envelope per command; `--limit`, `--max-lines` and
169
+ `--offset` window long output, and any truncation says how to fetch the
170
+ rest.
171
+
172
+ ### What MongoDB cannot see, and how pythia-mongo says so
173
+
174
+ - **There is no catalog of dependencies.** MongoDB records only views:
175
+ `impact` walks every view's `viewOn` and the collections its pipeline
176
+ reads (`$lookup`, `$graphLookup`, `$unionWith`, `$facet`, nested
177
+ pipelines too). The application code that reads and writes a collection
178
+ through a driver is invisible to the database, and so are views in other
179
+ databases, scheduled `$merge` / `$out` jobs and change streams. Each
180
+ blind spot is printed every time.
181
+ - **There is no column list.** `cols` reads up to 1000 sampled documents:
182
+ each field path (`address.city`, `tags[]` for an array's elements), its
183
+ types by how many documents hold them, how many hold it at all, and what
184
+ the `$jsonSchema` validator says (required, description). A field the
185
+ validator declares and no sampled document holds is listed as `0 of N`.
186
+ When the collection holds more than the sample, it says so.
187
+ - **`errors` counts, it does not compile.** For each collection with a
188
+ validator: how many documents break it as it stands, with the first of
189
+ them (`warning` when the validator only warns). Each view runs over its
190
+ first 100 documents, and a view that reads a collection that does not
191
+ exist is named. A collection without a validator has nothing to check,
192
+ and the output says `NOT VERIFIED`.
193
+ - **`ddl` is rebuilt, not dumped:** a JSON array of the command documents
194
+ that make the collection (`create` with its validator and options,
195
+ `createIndexes`) or the view. The live check drops each, runs them,
196
+ rebuilds and compares.
197
+ - **Reads never write, and the server is the second line.** MongoDB has no
198
+ read-only session. Every command a read sends is a file in
199
+ `src/pythia_mongo/commands/`, and the suite holds them to reads only; a
200
+ user holding only the `read` role is what the server enforces.
201
+
202
+ ### Writing: the gate
203
+
204
+ ```bash
205
+ pythia-mongo apply change.json # preview: rehearsed or predicted, nothing kept
206
+ pythia-mongo approve 3fa9c1 # the developer approves, at their own console
207
+ pythia-mongo apply change.json --confirm 3fa9c1 # writes it — only as approved
208
+ pythia-mongo journal restore <entry> # undo is a write too: preview, approve, confirm
209
+ pythia-mongo policy # which groups may be written, and is rollback real?
210
+ ```
211
+
212
+ The developer approves through the first door that works:
213
+
214
+ 1. **In Claude Code's chat.** The agent asks with AskUserQuestion, its text
215
+ exactly the output of `pythia-mongo approve --card <token>`, options
216
+ Approve / Reject. The answer hook records the approval only when the
217
+ question carried that card and the developer chose Approve themselves —
218
+ not an answer the agent wrote, not a dialog that closed unattended.
219
+ 2. **Through the MCP approver** (`pythia_mongo_approve`; Codex starts
220
+ here): the client shows the card, and only an explicit Approve counts.
221
+ 3. **At the developer's console:** `pythia-mongo approve <token>`.
222
+
223
+ `pythia-mongo doctor` warns about settings that would answer the chat door
224
+ for the developer (`dontAsk` mode, an allow rule for AskUserQuestion, a hook
225
+ matching every tool).
226
+
227
+ A file holds **one command document**, in Extended JSON — the document
228
+ MongoDB itself takes, not mongosh JavaScript:
229
+
230
+ ```json
231
+ {"update": "t_order",
232
+ "updates": [{"q": {"status": "DRAFT"}, "u": {"$inc": {"total": 1}}, "multi": true}]}
233
+ ```
234
+
235
+ `apply` sorts it into a group, and the policy decides: `source` — a view
236
+ (`create` with `viewOn`, `collMod` of `viewOn` and `pipeline`) or a
237
+ validator (`collMod` of `validator`, `validationLevel`,
238
+ `validationAction`) — needs the developer's approval; `structural` (a new
239
+ collection, `createIndexes`, `dropIndexes`, `drop`, other `collMod`
240
+ options) and `data_dml` (`insert`, `update`, `delete`) are denied until the
241
+ developer runs `pythia-mongo policy set <group> confirm` at a console.
242
+
243
+ **Data is rehearsed.** An insert, update or delete runs inside a
244
+ transaction and is aborted: the preview shows how many documents it
245
+ touched, and an UPDATE or DELETE is approved on its documents — the count, a
246
+ sample, and a fingerprint (`$toHashedIndexKey` over each document). If one
247
+ joins, leaves or changes before the confirm, the confirm is refused. A
248
+ document the validator rejects fails the rehearsal (MongoDB reports it in
249
+ the reply, not as an error, and pythia-mongo reads the reply).
250
+ Transactions need a replica set: on a standalone server data writes are
251
+ refused — a single-node replica set is enough for development.
252
+
253
+ **Everything else is predicted.** MongoDB runs a view, a validator, an
254
+ index or a dropped collection outside any transaction, so nothing can be
255
+ run and rolled back. pythia-core's predict tier takes over: nothing runs
256
+ before the approval, and the preview says "predicted — nothing ran".
257
+
258
+ | Command | The prediction | Read back after the write |
259
+ |---|---|---|
260
+ | a validator (`collMod`) | counts the documents the new validator would reject, with the first of them; a validator MongoDB would refuse has nothing to approve | the validator as stored, and the count again — a different count exits 3 |
261
+ | a view (`create`, `collMod`) | runs the pipeline over no documents (would MongoDB accept it?) and until its first 100 documents (does it run?); names what it reads that does not exist; runs every view built on it on top of the new pipeline | the view as stored, and every run again — a failure the prediction did not see exits 3 |
262
+ | a unique index | looks for the keys it would meet twice (its partial filter or sparse rule applied); duplicates mean nothing to approve | each index is there, with its key |
263
+ | an index, a drop | an index name that exists with another key is refused; a drop names every view that reads what it drops | the index is there, or gone |
264
+
265
+ After the approval core runs the command once. MongoDB applies each of
266
+ these commands whole or not at all, so a refusal wrote nothing and keeps
267
+ the approval; a command whose answer never came is reported `WRITTEN?`
268
+ (exit 3). What landed is read back, and anything unlike the prediction is
269
+ reported and exits 3 with the restore. A prediction runs for at most 30
270
+ seconds (`maxTimeMS`).
271
+
272
+ **Refused, with the reason:** mongosh JavaScript, several commands in one
273
+ file, a key written twice, JavaScript anywhere (`$where`, `$function`,
274
+ `$accumulator`, code), `$out` and `$merge` (a prediction runs a pipeline
275
+ before approval: it must never write), reads (`aggregate`, `find`),
276
+ `findAndModify`, an upsert, an update or delete of one document among many
277
+ (`"multi": true` or `"limit": 0`, or name it by `_id`), users and roles,
278
+ `renameCollection`, `dropDatabase`, `bypassDocumentValidation`, session and
279
+ transaction fields (`lsid`, `writeConcern`, ...), and `system.` collections.
280
+
281
+ ### A release: `deploy`, one transaction
282
+
283
+ ```bash
284
+ pythia-mongo deploy 01_fix.json 02_backfill.json # preview the release as one transaction
285
+ pythia-mongo deploy 01_fix.json 02_backfill.json --confirm 9c41e0b2aa # all of it, as approved, or none
286
+ ```
287
+
288
+ A release is data: one or more files, each one command document or a JSON
289
+ array of them, in the order they run. It is rehearsed in one transaction —
290
+ each command in order, its documents counted and fingerprinted at its own
291
+ point (an update of a document the release inserts sees it), then
292
+ everything is aborted. One card shows every command, and one approval (a
293
+ 10-digit token, through the same doors) covers the release; a single
294
+ command's token cannot be approved alone. The confirm checks that every
295
+ command is byte-identical to what was approved before anything runs,
296
+ re-runs the release in one transaction, and commits only if every command
297
+ still matches. A command that fails, or documents that moved, abort the
298
+ whole release. A commit that does not answer is reported `WRITTEN?` for the
299
+ whole release.
300
+
301
+ A validator, a view, an index or a new or dropped collection has no place in
302
+ a release: MongoDB runs them outside any transaction, so they are predicted,
303
+ not rehearsed, and could not land all or nothing. `deploy` refuses them
304
+ before anything is written; apply each on its own. Anything `apply` refuses
305
+ is refused here too.
306
+
307
+ ### Lint: before any apply
308
+
309
+ ```bash
310
+ pythia-mongo lint 01_fix.json 02_view.json # a change's files in one run
311
+ pythia-mongo lint --rules # every rule and its effective severity
312
+ ```
313
+
314
+ The rules read what MongoDB accepts and a reviewer would not:
315
+
316
+ | Rule | Default | Why |
317
+ |---|---|---|
318
+ | `mongo-validation-warn` | warn | `validationAction: "warn"` writes every document that breaks the validator anyway, and only logs it |
319
+ | `mongo-validation-off` | warn | `validationLevel: "off"` keeps the validator and enforces nothing |
320
+ | `mongo-validator-required` | info | a `$jsonSchema` with no `required` list accepts a document that holds none of its fields |
321
+ | `mongo-collection-validator` | warn | a new collection gets its validator in the same change (a `collMod` of it that sets one counts) |
322
+ | `mongo-unique-optional` | warn | a unique index counts every document without the field as `null`, and refuses the second — make it partial (`$exists`) or sparse |
323
+ | `mongo-ttl` | warn | a TTL index deletes documents on the server's schedule, unasked and unjournaled |
324
+ | `mongo-update-replacement` | warn | an update whose `u` holds no operator replaces each document, removing every field it does not name |
325
+
326
+ What MongoDB refuses itself is left to the rehearsal or the prediction. The
327
+ team sets severities and naming patterns (a view's name, `VIEW`; a
328
+ validator's collection, `VALIDATOR`) in `.pythia/mongo/lint.json`:
329
+
330
+ ```json
331
+ {"severity": {"mongo-validation-warn": "error", "naming": "error"},
332
+ "naming": {"VIEW": "^v_[a-z0-9_]+$"}}
333
+ ```
334
+
335
+ Every preview shows the findings, a command's errors are on its card, and
336
+ autopilot never lands a lint error.
337
+
338
+ ### Autopilot: one lease instead of every approval
339
+
340
+ ```bash
341
+ pythia-mongo autopilot request --groups source --hours 2 --max-writes 30
342
+ pythia-mongo autopilot report # every write under the lease, with its restore
343
+ pythia-mongo autopilot off # anyone may revoke
344
+ ```
345
+
346
+ When the developer wants the agent to work without approving each write,
347
+ the agent requests a lease. Its card names the connection, the groups, the
348
+ time, the number of writes and whether destructive commands are included,
349
+ and nothing is delegated until the developer approves it through a door (or
350
+ runs `pythia-mongo autopilot on` at a console). While it lasts, `apply` and
351
+ `deploy` land what it covers without `--confirm` — still snapshotted,
352
+ rehearsed or predicted, read back and journaled. It never covers another
353
+ connection, one marked `"protected": true` (or an `"env"` of prod /
354
+ production / prd / live), a group or destructive command the card did not
355
+ name, or more than 1000 documents; a write that breaks something, lands
356
+ unlike its prediction or is `WRITTEN?`, or five failures in a row pause it.
357
+
358
+ ### Least privilege
359
+
360
+ The read commands need the `read` role on the database. Writing needs
361
+ `readWrite` (data, indexes, new collections and views) and `dbAdmin`
362
+ (`collMod` of a validator or a view); the preview warns when the user holds
363
+ far more (`root`, `dbOwner`, any `AnyDatabase` role). The live check runs
364
+ every read as a `read` user, and every write as one holding `readWrite` and
365
+ `dbAdmin`.
366
+
367
+ ## Every command is a file
368
+
369
+ Like the SQL engines' query files, every command pythia-mongo sends lives in
370
+ `src/pythia_mongo/commands/` as JSON: what it is for, its binds, what it
371
+ returns, and the command document. A bind is written `{"$bind": "name"}`
372
+ where its value goes, so text from a person or an agent always lands as a
373
+ value — never as an operator, a stage or a command of its own. The suite
374
+ checks that every file declares exactly the binds it uses, holds no
375
+ JavaScript (`$where`, `$function`, `$accumulator`), and holds no write,
376
+ `$out` or `$merge`: every command file reads. The only write pythia-mongo
377
+ ever sends is the developer's own command document — in a rehearsal's
378
+ transaction, or once after the approval.
379
+
380
+ ## Tests
381
+
382
+ ```bash
383
+ python tests/test_mongo.py # no database, no pymongo — mutation-checked
384
+ python tests/live_check.py # a disposable server only (see its docstring)
385
+ ```
386
+
387
+ CI runs the suite on Ubuntu, Windows and macOS, Python 3.9 and 3.13, and
388
+ the live check — every read, every write path, a release, a lint finding
389
+ in a real preview and a write under a lease — against MongoDB 7.0 and 8.0,
390
+ each a single-node replica set with authentication. It tests against the
391
+ pythia-core branch of the same name when one exists.
392
+
393
+ ## License
394
+
395
+ MIT