backpack-store 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.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Ella Inng
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,252 @@
1
+ Metadata-Version: 2.4
2
+ Name: backpack-store
3
+ Version: 0.1.0
4
+ Summary: Store and retrieve typed local records with identity and provenance.
5
+ Keywords: typed-storage,sqlite,records,provenance,local-first
6
+ Author: Ella Inng
7
+ License-Expression: MIT
8
+ License-File: LICENSE
9
+ Classifier: Development Status :: 3 - Alpha
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: Programming Language :: Python :: 3 :: Only
12
+ Classifier: Programming Language :: Python :: 3.11
13
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
14
+ Requires-Dist: hidden-moves>=0.1,<0.2 ; extra == 'host'
15
+ Requires-Dist: hidden-moves-mcp>=0.1,<0.2 ; extra == 'host'
16
+ Requires-Dist: hidden-moves>=0.1,<0.2 ; extra == 'moves'
17
+ Requires-Dist: pydantic>=2.13,<3 ; extra == 'pydantic'
18
+ Maintainer: Outside Labs
19
+ Requires-Python: >=3.11
20
+ Project-URL: Homepage, https://github.com/outside-labs/backpack
21
+ Project-URL: Repository, https://github.com/outside-labs/backpack
22
+ Project-URL: Issues, https://github.com/outside-labs/backpack/issues
23
+ Project-URL: Documentation, https://github.com/outside-labs/backpack#readme
24
+ Provides-Extra: host
25
+ Provides-Extra: moves
26
+ Provides-Extra: pydantic
27
+ Description-Content-Type: text/markdown
28
+
29
+ # Backpack
30
+
31
+ Backpack is an experimental programmable local store for typed records. The
32
+ SQLite API stores finite JSON with explicit codecs, local identity and provenance.
33
+ Records recover their Python types after closing and reopening the database.
34
+
35
+ - Product: Backpack
36
+ - Distribution: `backpack-store`
37
+ - Import: `backpack_store`
38
+ - Python: 3.11 or newer
39
+ - Runtime dependencies: none
40
+
41
+ ## Installation
42
+
43
+ ```sh
44
+ python -m pip install backpack-store==0.1.0
45
+ ```
46
+
47
+ Optional extras are `backpack-store[pydantic]`, `backpack-store[moves]` and
48
+ `backpack-store[host]`. The `backpack_store` import stays the same for every extra.
49
+
50
+ ## Explicit typed records
51
+
52
+ ```python
53
+ from dataclasses import dataclass
54
+ from backpack_store import CodecRegistry, DataclassCodec
55
+
56
+ @dataclass(frozen=True)
57
+ class Note:
58
+ title: str
59
+ body: str
60
+
61
+ registry = CodecRegistry()
62
+ registry.register(DataclassCodec(Note, type_key="notes.note", schema_version=1))
63
+ encoded = registry.encode(Note("Example", "Local text"))
64
+ restored = registry.decode(encoded.type_key, encoded.schema_version, encoded.payload)
65
+ assert isinstance(restored, Note)
66
+ ```
67
+
68
+ A stable dotted `type_key` and positive `schema_version` identify each registered
69
+ codec. Registrations are explicit and reject duplicate keys or Python types.
70
+ Dataclass codecs support concrete non-recursive dataclasses, nested dataclasses,
71
+ JSON scalar fields, literals, optional/unions, typed lists, tuples and string-keyed
72
+ dictionaries. They validate every field; unknown or missing fields fail. A custom
73
+ codec can support other trusted model types. Stored names never cause imports.
74
+
75
+ Record envelopes contain a separate local UUID, payload schema version, revision,
76
+ UTC creation/update timestamps, normalized exact tags and optional `Provenance`.
77
+ Timestamps use `YYYY-MM-DDTHH:MM:SS.ffffffZ`. Provenance names the source URL,
78
+ system, object ID and capture time; a source ID is not the local record UUID.
79
+ JSON payloads must be finite objects, at most 1 MiB and 64 nesting levels. Tags
80
+ are case-sensitive NFC strings, trimmed, deduplicated and sorted (up to 64 supplied
81
+ values of 128 characters). Unknown types/versions and invalid payloads raise typed
82
+ errors. Importing the package opens no files or database and performs no network I/O.
83
+
84
+ ## Durable local storage
85
+
86
+ ```python
87
+ from backpack_store import Backpack
88
+
89
+ with Backpack.open("notes.sqlite") as store:
90
+ store.register(DataclassCodec(Note, type_key="notes.note"))
91
+ written = store.put(Note("Example", "Local text"), tags=["work"])
92
+
93
+ with Backpack.open("notes.sqlite") as store:
94
+ store.register(DataclassCodec(Note, type_key="notes.note"))
95
+ note = store.get(written.id)
96
+ record = store.get_record(written.id) # envelope plus .value
97
+ page = store.find(Note, tags=["work"], limit=20)
98
+ updated = store.update(written.id, Note("Revised", "Local text"),
99
+ expected_revision=record.revision, tags=["work"])
100
+ assert updated.revision == 2
101
+ assert store.delete(written.id)
102
+ ```
103
+
104
+ Each `put` creates a new UUID and returns `WriteResult(id, revision)`. `update`
105
+ replaces payload, tags and provenance together, preserves the local ID/type key
106
+ and creation time, and requires the expected revision. A stale revision raises
107
+ `RevisionConflictError` without changing the record. Missing reads/updates raise
108
+ `NotFoundError`; deletion returns whether a row existed. Payload, metadata and
109
+ tag writes share one transaction. Invalid input and database failures leave no
110
+ partial record.
111
+
112
+ `find` requires an explicitly registered Python type and matches **all** requested
113
+ tags. It returns at most 1,000 records (default 50) in ascending local UUID order.
114
+ Pass `.next_cursor` with the same type/tags to continue. Cursors are query-scoped;
115
+ concurrent inserts, updates or deletions can change later pages, so pagination
116
+ does not promise a frozen snapshot. `get_raw` returns recoverable JSON/metadata
117
+ without decoding an unknown registered type or payload version.
118
+
119
+ The database schema has its own version, separate from payload versions. Unknown
120
+ or nonempty unversioned databases fail safely. Use the connection on its opening
121
+ thread; a context manager closes it even after caller failure. Lock waiting is
122
+ bounded by `timeout` (default 5 seconds, allowed 0–60). No path is chosen by
123
+ default. Parent directories must already exist. Importing never opens a database.
124
+
125
+ Run `python examples/persistence.py` for a complete offline file-backed Note and
126
+ synthetic ProjectItem round trip, including filtering and deletion.
127
+
128
+ ## Version evolution and export
129
+
130
+ Register the current codec, then explicit trusted functions for every one-version
131
+ step from an older payload to the current version:
132
+
133
+ ```python
134
+ store.register(DataclassCodec(NoteV2, type_key="notes.note", schema_version=2))
135
+ store.register_migration("notes.note", from_version=1, to_version=2,
136
+ migrate=lambda old: {"title": old["title"], "text": old["body"]})
137
+ ```
138
+
139
+ `NoteV2` is an application-defined dataclass with `title` and `text` fields. Old
140
+ reads upgrade in memory; `.payload` and `.schema_version` on the returned envelope
141
+ still describe the original stored representation. Only an explicit
142
+ revision-checked `update` writes the current representation. Missing migration
143
+ steps and newer unknown versions raise `UnknownVersionError`; failed/invalid
144
+ migration output raises `MigrationError`. Migration functions never come from a
145
+ stored module name. Database schema version 1 is independent; no destructive
146
+ migration or automatic database replacement is provided.
147
+
148
+ ```python
149
+ page = store.export_jsonl("records-1.jsonl", limit=100, max_bytes=4_194_304)
150
+ # If page.next_cursor is set, export to another new explicit destination:
151
+ # store.export_jsonl("records-2.jsonl", cursor=page.next_cursor, limit=100)
152
+ ```
153
+
154
+ Export writes UTF-8 JSONL containing raw record envelopes with `format_version: 1`,
155
+ type/payload versions and provenance. No codec or migration runs during export.
156
+ It stops at the record or byte budget (default 1,000 records / 4 MiB; maximum
157
+ 1,000 / 64 MiB). If a single record exceeds the byte budget, it fails before
158
+ creating a destination. Pages use ascending UUIDs and the same concurrent-write
159
+ limitations as `find`. A complete temporary file is published without overwriting
160
+ an existing path; the destination directory must exist and support hard links.
161
+ Export never rewrites the database.
162
+
163
+ ## Optional selected tools
164
+
165
+ The direct store remains independent of Hidden Moves and MCP. Optional `moves`
166
+ and `host` extras declare the capability-family dependencies. CI uses a reviewed
167
+ pinned family revision, runs standalone storage first, then installs the optional
168
+ wheels.
169
+
170
+ Installed entry-point discovery advertises `backpack`; load it explicitly and bind
171
+ an already-configured Backpack instance. The factory chooses no path and opens no
172
+ database. The application registers trusted models before selecting tools:
173
+
174
+ ```python
175
+ from backpack_store.host import build_catalog
176
+ from backpack_store.integration import READ_CAPABILITIES, WRITE_CAPABILITIES
177
+
178
+ read = build_catalog(store, READ_CAPABILITIES)
179
+ result = read.invoke("backpack.records.find", {"type_key": "notes.note", "limit": 10})
180
+ write = build_catalog(store, WRITE_CAPABILITIES, allow_writes=True)
181
+ ```
182
+
183
+ Read tools are `backpack.records.get` and `.find`; local write tools are `.put` and
184
+ `.delete`. A write profile requires explicit host intent and explicit selection.
185
+ Inputs contain local IDs, registered stable type keys and finite JSON payloads,
186
+ never Python classes, codec imports, SQL or database paths. `put` validates the
187
+ current registered model; `find` enforces the storage query limits. Concrete JSON
188
+ views preserve record identity/version/provenance without serializing arbitrary
189
+ Python objects. Effect annotations describe local reads/insertion/deletion;
190
+ application permissions and caller approval policy remain responsible for access.
191
+
192
+ `serve_store(store, names, allow_writes=False)` serves the same catalog over local
193
+ MCP stdio. The application owns the connection and cleanup. For a complete notes
194
+ application with a fixed trusted model and explicit path/profile:
195
+
196
+ ```sh
197
+ python examples/notes_stdio.py --database notes.sqlite --profile read
198
+ # Select a write-capable host only when local writes are intended:
199
+ python examples/notes_stdio.py --database notes.sqlite --profile write
200
+ ```
201
+
202
+ These are local tools. No public endpoint or remote access to the SQLite file is
203
+ provided. Run `python -m unittest discover -s integration_tests -v` in the optional
204
+ integration environment to check real stdio, selection and consumer equivalence.
205
+
206
+ ## Optional Pydantic model codec
207
+
208
+ Install the `pydantic` extra to use the separate Pydantic 2 extension (supported
209
+ range `>=2.13,<3`; tested with 2.13.5). Importing the core never imports Pydantic.
210
+ The extension uses the same store, explicit identity and migration registry:
211
+
212
+ ```python
213
+ from pydantic import BaseModel
214
+ from backpack_store.pydantic_codec import PydanticCodec
215
+
216
+ class ValidatedNote(BaseModel):
217
+ title: str
218
+ priority: int
219
+
220
+ store.register(PydanticCodec(ValidatedNote, type_key="notes.validated", schema_version=1))
221
+ written = store.put(ValidatedNote(title="Example", priority=2))
222
+ ```
223
+
224
+ Register the same trusted model after reopening. The codec accepts the exact
225
+ registered class, serializes aliases and JSON representations, then revalidates
226
+ strictly before writing. Decode also uses strict JSON validation with extra fields
227
+ forbidden. This catches mutated instances and `model_construct` bypasses instead
228
+ of trusting an already-created instance. Payloads still obey the core finite JSON
229
+ object limits. Object-shaped nested models and datetime JSON round trips are
230
+ covered; scalar/list `RootModel` classes are rejected. Private/computed fields are
231
+ not part of the durable representation.
232
+
233
+ This codec does not add native Pydantic type support to tool signatures. The
234
+ optional provider continues to use its concrete JSON views. Run
235
+ `python -m unittest discover -s pydantic_tests -v` in the extension environment.
236
+
237
+ ## Development
238
+
239
+ ```sh
240
+ uv build
241
+ python -m pip install dist/backpack_store-0.1.0-py3-none-any.whl
242
+ python -m unittest discover -s tests -v
243
+ ```
244
+
245
+ CI installs the wheel built from its source distribution and runs the behavior
246
+ suite on Python 3.11/3.13 across Linux, macOS and Windows. The original generated
247
+ scaffold is preserved in the repository's initial commit.
248
+
249
+ This 0.1.x project is experimental. Published GitHub releases with matching `v`
250
+ tags run `.github/workflows/release.yaml`, verify the installed wheel and source
251
+ distribution, and upload through PyPI trusted publishing in environment `pypi`.
252
+ The initial release tag is `v0.1.0`.
@@ -0,0 +1,224 @@
1
+ # Backpack
2
+
3
+ Backpack is an experimental programmable local store for typed records. The
4
+ SQLite API stores finite JSON with explicit codecs, local identity and provenance.
5
+ Records recover their Python types after closing and reopening the database.
6
+
7
+ - Product: Backpack
8
+ - Distribution: `backpack-store`
9
+ - Import: `backpack_store`
10
+ - Python: 3.11 or newer
11
+ - Runtime dependencies: none
12
+
13
+ ## Installation
14
+
15
+ ```sh
16
+ python -m pip install backpack-store==0.1.0
17
+ ```
18
+
19
+ Optional extras are `backpack-store[pydantic]`, `backpack-store[moves]` and
20
+ `backpack-store[host]`. The `backpack_store` import stays the same for every extra.
21
+
22
+ ## Explicit typed records
23
+
24
+ ```python
25
+ from dataclasses import dataclass
26
+ from backpack_store import CodecRegistry, DataclassCodec
27
+
28
+ @dataclass(frozen=True)
29
+ class Note:
30
+ title: str
31
+ body: str
32
+
33
+ registry = CodecRegistry()
34
+ registry.register(DataclassCodec(Note, type_key="notes.note", schema_version=1))
35
+ encoded = registry.encode(Note("Example", "Local text"))
36
+ restored = registry.decode(encoded.type_key, encoded.schema_version, encoded.payload)
37
+ assert isinstance(restored, Note)
38
+ ```
39
+
40
+ A stable dotted `type_key` and positive `schema_version` identify each registered
41
+ codec. Registrations are explicit and reject duplicate keys or Python types.
42
+ Dataclass codecs support concrete non-recursive dataclasses, nested dataclasses,
43
+ JSON scalar fields, literals, optional/unions, typed lists, tuples and string-keyed
44
+ dictionaries. They validate every field; unknown or missing fields fail. A custom
45
+ codec can support other trusted model types. Stored names never cause imports.
46
+
47
+ Record envelopes contain a separate local UUID, payload schema version, revision,
48
+ UTC creation/update timestamps, normalized exact tags and optional `Provenance`.
49
+ Timestamps use `YYYY-MM-DDTHH:MM:SS.ffffffZ`. Provenance names the source URL,
50
+ system, object ID and capture time; a source ID is not the local record UUID.
51
+ JSON payloads must be finite objects, at most 1 MiB and 64 nesting levels. Tags
52
+ are case-sensitive NFC strings, trimmed, deduplicated and sorted (up to 64 supplied
53
+ values of 128 characters). Unknown types/versions and invalid payloads raise typed
54
+ errors. Importing the package opens no files or database and performs no network I/O.
55
+
56
+ ## Durable local storage
57
+
58
+ ```python
59
+ from backpack_store import Backpack
60
+
61
+ with Backpack.open("notes.sqlite") as store:
62
+ store.register(DataclassCodec(Note, type_key="notes.note"))
63
+ written = store.put(Note("Example", "Local text"), tags=["work"])
64
+
65
+ with Backpack.open("notes.sqlite") as store:
66
+ store.register(DataclassCodec(Note, type_key="notes.note"))
67
+ note = store.get(written.id)
68
+ record = store.get_record(written.id) # envelope plus .value
69
+ page = store.find(Note, tags=["work"], limit=20)
70
+ updated = store.update(written.id, Note("Revised", "Local text"),
71
+ expected_revision=record.revision, tags=["work"])
72
+ assert updated.revision == 2
73
+ assert store.delete(written.id)
74
+ ```
75
+
76
+ Each `put` creates a new UUID and returns `WriteResult(id, revision)`. `update`
77
+ replaces payload, tags and provenance together, preserves the local ID/type key
78
+ and creation time, and requires the expected revision. A stale revision raises
79
+ `RevisionConflictError` without changing the record. Missing reads/updates raise
80
+ `NotFoundError`; deletion returns whether a row existed. Payload, metadata and
81
+ tag writes share one transaction. Invalid input and database failures leave no
82
+ partial record.
83
+
84
+ `find` requires an explicitly registered Python type and matches **all** requested
85
+ tags. It returns at most 1,000 records (default 50) in ascending local UUID order.
86
+ Pass `.next_cursor` with the same type/tags to continue. Cursors are query-scoped;
87
+ concurrent inserts, updates or deletions can change later pages, so pagination
88
+ does not promise a frozen snapshot. `get_raw` returns recoverable JSON/metadata
89
+ without decoding an unknown registered type or payload version.
90
+
91
+ The database schema has its own version, separate from payload versions. Unknown
92
+ or nonempty unversioned databases fail safely. Use the connection on its opening
93
+ thread; a context manager closes it even after caller failure. Lock waiting is
94
+ bounded by `timeout` (default 5 seconds, allowed 0–60). No path is chosen by
95
+ default. Parent directories must already exist. Importing never opens a database.
96
+
97
+ Run `python examples/persistence.py` for a complete offline file-backed Note and
98
+ synthetic ProjectItem round trip, including filtering and deletion.
99
+
100
+ ## Version evolution and export
101
+
102
+ Register the current codec, then explicit trusted functions for every one-version
103
+ step from an older payload to the current version:
104
+
105
+ ```python
106
+ store.register(DataclassCodec(NoteV2, type_key="notes.note", schema_version=2))
107
+ store.register_migration("notes.note", from_version=1, to_version=2,
108
+ migrate=lambda old: {"title": old["title"], "text": old["body"]})
109
+ ```
110
+
111
+ `NoteV2` is an application-defined dataclass with `title` and `text` fields. Old
112
+ reads upgrade in memory; `.payload` and `.schema_version` on the returned envelope
113
+ still describe the original stored representation. Only an explicit
114
+ revision-checked `update` writes the current representation. Missing migration
115
+ steps and newer unknown versions raise `UnknownVersionError`; failed/invalid
116
+ migration output raises `MigrationError`. Migration functions never come from a
117
+ stored module name. Database schema version 1 is independent; no destructive
118
+ migration or automatic database replacement is provided.
119
+
120
+ ```python
121
+ page = store.export_jsonl("records-1.jsonl", limit=100, max_bytes=4_194_304)
122
+ # If page.next_cursor is set, export to another new explicit destination:
123
+ # store.export_jsonl("records-2.jsonl", cursor=page.next_cursor, limit=100)
124
+ ```
125
+
126
+ Export writes UTF-8 JSONL containing raw record envelopes with `format_version: 1`,
127
+ type/payload versions and provenance. No codec or migration runs during export.
128
+ It stops at the record or byte budget (default 1,000 records / 4 MiB; maximum
129
+ 1,000 / 64 MiB). If a single record exceeds the byte budget, it fails before
130
+ creating a destination. Pages use ascending UUIDs and the same concurrent-write
131
+ limitations as `find`. A complete temporary file is published without overwriting
132
+ an existing path; the destination directory must exist and support hard links.
133
+ Export never rewrites the database.
134
+
135
+ ## Optional selected tools
136
+
137
+ The direct store remains independent of Hidden Moves and MCP. Optional `moves`
138
+ and `host` extras declare the capability-family dependencies. CI uses a reviewed
139
+ pinned family revision, runs standalone storage first, then installs the optional
140
+ wheels.
141
+
142
+ Installed entry-point discovery advertises `backpack`; load it explicitly and bind
143
+ an already-configured Backpack instance. The factory chooses no path and opens no
144
+ database. The application registers trusted models before selecting tools:
145
+
146
+ ```python
147
+ from backpack_store.host import build_catalog
148
+ from backpack_store.integration import READ_CAPABILITIES, WRITE_CAPABILITIES
149
+
150
+ read = build_catalog(store, READ_CAPABILITIES)
151
+ result = read.invoke("backpack.records.find", {"type_key": "notes.note", "limit": 10})
152
+ write = build_catalog(store, WRITE_CAPABILITIES, allow_writes=True)
153
+ ```
154
+
155
+ Read tools are `backpack.records.get` and `.find`; local write tools are `.put` and
156
+ `.delete`. A write profile requires explicit host intent and explicit selection.
157
+ Inputs contain local IDs, registered stable type keys and finite JSON payloads,
158
+ never Python classes, codec imports, SQL or database paths. `put` validates the
159
+ current registered model; `find` enforces the storage query limits. Concrete JSON
160
+ views preserve record identity/version/provenance without serializing arbitrary
161
+ Python objects. Effect annotations describe local reads/insertion/deletion;
162
+ application permissions and caller approval policy remain responsible for access.
163
+
164
+ `serve_store(store, names, allow_writes=False)` serves the same catalog over local
165
+ MCP stdio. The application owns the connection and cleanup. For a complete notes
166
+ application with a fixed trusted model and explicit path/profile:
167
+
168
+ ```sh
169
+ python examples/notes_stdio.py --database notes.sqlite --profile read
170
+ # Select a write-capable host only when local writes are intended:
171
+ python examples/notes_stdio.py --database notes.sqlite --profile write
172
+ ```
173
+
174
+ These are local tools. No public endpoint or remote access to the SQLite file is
175
+ provided. Run `python -m unittest discover -s integration_tests -v` in the optional
176
+ integration environment to check real stdio, selection and consumer equivalence.
177
+
178
+ ## Optional Pydantic model codec
179
+
180
+ Install the `pydantic` extra to use the separate Pydantic 2 extension (supported
181
+ range `>=2.13,<3`; tested with 2.13.5). Importing the core never imports Pydantic.
182
+ The extension uses the same store, explicit identity and migration registry:
183
+
184
+ ```python
185
+ from pydantic import BaseModel
186
+ from backpack_store.pydantic_codec import PydanticCodec
187
+
188
+ class ValidatedNote(BaseModel):
189
+ title: str
190
+ priority: int
191
+
192
+ store.register(PydanticCodec(ValidatedNote, type_key="notes.validated", schema_version=1))
193
+ written = store.put(ValidatedNote(title="Example", priority=2))
194
+ ```
195
+
196
+ Register the same trusted model after reopening. The codec accepts the exact
197
+ registered class, serializes aliases and JSON representations, then revalidates
198
+ strictly before writing. Decode also uses strict JSON validation with extra fields
199
+ forbidden. This catches mutated instances and `model_construct` bypasses instead
200
+ of trusting an already-created instance. Payloads still obey the core finite JSON
201
+ object limits. Object-shaped nested models and datetime JSON round trips are
202
+ covered; scalar/list `RootModel` classes are rejected. Private/computed fields are
203
+ not part of the durable representation.
204
+
205
+ This codec does not add native Pydantic type support to tool signatures. The
206
+ optional provider continues to use its concrete JSON views. Run
207
+ `python -m unittest discover -s pydantic_tests -v` in the extension environment.
208
+
209
+ ## Development
210
+
211
+ ```sh
212
+ uv build
213
+ python -m pip install dist/backpack_store-0.1.0-py3-none-any.whl
214
+ python -m unittest discover -s tests -v
215
+ ```
216
+
217
+ CI installs the wheel built from its source distribution and runs the behavior
218
+ suite on Python 3.11/3.13 across Linux, macOS and Windows. The original generated
219
+ scaffold is preserved in the repository's initial commit.
220
+
221
+ This 0.1.x project is experimental. Published GitHub releases with matching `v`
222
+ tags run `.github/workflows/release.yaml`, verify the installed wheel and source
223
+ distribution, and upload through PyPI trusted publishing in environment `pypi`.
224
+ The initial release tag is `v0.1.0`.
@@ -0,0 +1,53 @@
1
+ [project]
2
+ name = "backpack-store"
3
+ version = "0.1.0"
4
+ description = "Store and retrieve typed local records with identity and provenance."
5
+ readme = "README.md"
6
+ requires-python = ">=3.11"
7
+ license = "MIT"
8
+ license-files = ["LICENSE"]
9
+ keywords = [
10
+ "typed-storage",
11
+ "sqlite",
12
+ "records",
13
+ "provenance",
14
+ "local-first",
15
+ ]
16
+ classifiers = [
17
+ "Development Status :: 3 - Alpha",
18
+ "Intended Audience :: Developers",
19
+ "Programming Language :: Python :: 3 :: Only",
20
+ "Programming Language :: Python :: 3.11",
21
+ "Topic :: Software Development :: Libraries :: Python Modules",
22
+ ]
23
+ dependencies = []
24
+
25
+ [[project.authors]]
26
+ name = "Ella Inng"
27
+
28
+ [[project.maintainers]]
29
+ name = "Outside Labs"
30
+
31
+ [project.optional-dependencies]
32
+ pydantic = ["pydantic>=2.13,<3"]
33
+ moves = ["hidden-moves>=0.1,<0.2"]
34
+ host = [
35
+ "hidden-moves>=0.1,<0.2",
36
+ "hidden-moves-mcp>=0.1,<0.2",
37
+ ]
38
+
39
+ [project.entry-points."hidden_moves.moves"]
40
+ backpack = "backpack_store.integration:provide_moves"
41
+
42
+ [project.urls]
43
+ Homepage = "https://github.com/outside-labs/backpack"
44
+ Repository = "https://github.com/outside-labs/backpack"
45
+ Issues = "https://github.com/outside-labs/backpack/issues"
46
+ Documentation = "https://github.com/outside-labs/backpack#readme"
47
+
48
+ [build-system]
49
+ requires = ["uv_build>=0.12.19,<0.13.0"]
50
+ build-backend = "uv_build"
51
+
52
+ [tool.uv.build-backend]
53
+ module-name = "backpack_store"
@@ -0,0 +1,40 @@
1
+ [project]
2
+ name = "backpack-store"
3
+ version = "0.1.0"
4
+ description = "Store and retrieve typed local records with identity and provenance."
5
+ readme = "README.md"
6
+ requires-python = ">=3.11"
7
+ license = "MIT"
8
+ license-files = ["LICENSE"]
9
+ authors = [{ name = "Ella Inng" }]
10
+ maintainers = [{ name = "Outside Labs" }]
11
+ keywords = ["typed-storage", "sqlite", "records", "provenance", "local-first"]
12
+ classifiers = [
13
+ "Development Status :: 3 - Alpha",
14
+ "Intended Audience :: Developers",
15
+ "Programming Language :: Python :: 3 :: Only",
16
+ "Programming Language :: Python :: 3.11",
17
+ "Topic :: Software Development :: Libraries :: Python Modules",
18
+ ]
19
+ dependencies = []
20
+
21
+ [project.optional-dependencies]
22
+ pydantic = ["pydantic>=2.13,<3"]
23
+ moves = ["hidden-moves>=0.1,<0.2"]
24
+ host = ["hidden-moves>=0.1,<0.2", "hidden-moves-mcp>=0.1,<0.2"]
25
+
26
+ [project.entry-points."hidden_moves.moves"]
27
+ backpack = "backpack_store.integration:provide_moves"
28
+
29
+ [project.urls]
30
+ Homepage = "https://github.com/outside-labs/backpack"
31
+ Repository = "https://github.com/outside-labs/backpack"
32
+ Issues = "https://github.com/outside-labs/backpack/issues"
33
+ Documentation = "https://github.com/outside-labs/backpack#readme"
34
+
35
+ [build-system]
36
+ requires = ["uv_build>=0.12.19,<0.13.0"]
37
+ build-backend = "uv_build"
38
+
39
+ [tool.uv.build-backend]
40
+ module-name = "backpack_store"
@@ -0,0 +1,49 @@
1
+ """Experimental typed local records with explicitly registered codecs."""
2
+
3
+ from .codecs import Codec, CodecRegistry, DataclassCodec, EncodedValue
4
+ from .errors import (
5
+ BackpackError,
6
+ CodecError,
7
+ DatabaseVersionError,
8
+ InvalidCursorError,
9
+ MigrationError,
10
+ NotFoundError,
11
+ RegistrationError,
12
+ RevisionConflictError,
13
+ StorageError,
14
+ StoreClosedError,
15
+ UnknownTypeError,
16
+ UnknownVersionError,
17
+ ValidationError,
18
+ )
19
+ from .records import JSONObject, JSONValue, Provenance, RawRecord, Record
20
+ from .store import Backpack, ExportResult, FindPage, WriteResult
21
+
22
+ __all__ = [
23
+ "Backpack",
24
+ "ExportResult",
25
+ "MigrationError",
26
+ "FindPage",
27
+ "WriteResult",
28
+ "BackpackError",
29
+ "Codec",
30
+ "CodecError",
31
+ "CodecRegistry",
32
+ "DataclassCodec",
33
+ "DatabaseVersionError",
34
+ "EncodedValue",
35
+ "InvalidCursorError",
36
+ "JSONObject",
37
+ "JSONValue",
38
+ "NotFoundError",
39
+ "Provenance",
40
+ "RawRecord",
41
+ "Record",
42
+ "RegistrationError",
43
+ "RevisionConflictError",
44
+ "StorageError",
45
+ "StoreClosedError",
46
+ "UnknownTypeError",
47
+ "UnknownVersionError",
48
+ "ValidationError",
49
+ ]