b3-prosaic-harness-postgres 0.2.1__tar.gz → 0.3.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.
- {b3_prosaic_harness_postgres-0.2.1/src/b3_prosaic_harness_postgres.egg-info → b3_prosaic_harness_postgres-0.3.0}/PKG-INFO +103 -7
- {b3_prosaic_harness_postgres-0.2.1 → b3_prosaic_harness_postgres-0.3.0}/README.md +100 -4
- {b3_prosaic_harness_postgres-0.2.1 → b3_prosaic_harness_postgres-0.3.0}/pyproject.toml +3 -3
- {b3_prosaic_harness_postgres-0.2.1 → b3_prosaic_harness_postgres-0.3.0/src/b3_prosaic_harness_postgres.egg-info}/PKG-INFO +103 -7
- {b3_prosaic_harness_postgres-0.2.1 → b3_prosaic_harness_postgres-0.3.0}/src/b3_prosaic_harness_postgres.egg-info/SOURCES.txt +10 -0
- {b3_prosaic_harness_postgres-0.2.1 → b3_prosaic_harness_postgres-0.3.0}/src/b3_prosaic_harness_postgres.egg-info/requires.txt +1 -1
- b3_prosaic_harness_postgres-0.3.0/src/prosaic_harness_postgres/__init__.py +7 -0
- b3_prosaic_harness_postgres-0.3.0/src/prosaic_harness_postgres/session_leases.py +10 -0
- b3_prosaic_harness_postgres-0.3.0/src/prosaic_harness_postgres/session_schema.py +185 -0
- b3_prosaic_harness_postgres-0.3.0/src/prosaic_harness_postgres/session_store.py +319 -0
- {b3_prosaic_harness_postgres-0.2.1 → b3_prosaic_harness_postgres-0.3.0}/tests/support/linux_wheel_smoke.py +13 -1
- {b3_prosaic_harness_postgres-0.2.1 → b3_prosaic_harness_postgres-0.3.0}/tests/support/local_cluster.py +5 -3
- b3_prosaic_harness_postgres-0.3.0/tests/support/session_documents.py +40 -0
- b3_prosaic_harness_postgres-0.3.0/tests/support/session_workers.py +94 -0
- {b3_prosaic_harness_postgres-0.2.1 → b3_prosaic_harness_postgres-0.3.0}/tests/test_packaging.py +21 -8
- b3_prosaic_harness_postgres-0.3.0/tests/test_session_fencing.py +137 -0
- b3_prosaic_harness_postgres-0.3.0/tests/test_session_recovery.py +110 -0
- b3_prosaic_harness_postgres-0.3.0/tests/test_session_schema.py +230 -0
- b3_prosaic_harness_postgres-0.3.0/tests/test_session_store.py +160 -0
- b3_prosaic_harness_postgres-0.3.0/tests/test_session_workers.py +87 -0
- b3_prosaic_harness_postgres-0.2.1/src/prosaic_harness_postgres/__init__.py +0 -6
- {b3_prosaic_harness_postgres-0.2.1 → b3_prosaic_harness_postgres-0.3.0}/LICENSE +0 -0
- {b3_prosaic_harness_postgres-0.2.1 → b3_prosaic_harness_postgres-0.3.0}/MANIFEST.in +0 -0
- {b3_prosaic_harness_postgres-0.2.1 → b3_prosaic_harness_postgres-0.3.0}/NOTICE +0 -0
- {b3_prosaic_harness_postgres-0.2.1 → b3_prosaic_harness_postgres-0.3.0}/setup.cfg +0 -0
- {b3_prosaic_harness_postgres-0.2.1 → b3_prosaic_harness_postgres-0.3.0}/src/b3_prosaic_harness_postgres.egg-info/dependency_links.txt +0 -0
- {b3_prosaic_harness_postgres-0.2.1 → b3_prosaic_harness_postgres-0.3.0}/src/b3_prosaic_harness_postgres.egg-info/entry_points.txt +0 -0
- {b3_prosaic_harness_postgres-0.2.1 → b3_prosaic_harness_postgres-0.3.0}/src/b3_prosaic_harness_postgres.egg-info/top_level.txt +0 -0
- {b3_prosaic_harness_postgres-0.2.1 → b3_prosaic_harness_postgres-0.3.0}/src/prosaic_harness_postgres/cli.py +0 -0
- {b3_prosaic_harness_postgres-0.2.1 → b3_prosaic_harness_postgres-0.3.0}/src/prosaic_harness_postgres/leases.py +0 -0
- {b3_prosaic_harness_postgres-0.2.1 → b3_prosaic_harness_postgres-0.3.0}/src/prosaic_harness_postgres/schema.py +0 -0
- {b3_prosaic_harness_postgres-0.2.1 → b3_prosaic_harness_postgres-0.3.0}/src/prosaic_harness_postgres/store.py +0 -0
- {b3_prosaic_harness_postgres-0.2.1 → b3_prosaic_harness_postgres-0.3.0}/src/prosaic_harness_postgres/transport.py +0 -0
- {b3_prosaic_harness_postgres-0.2.1 → b3_prosaic_harness_postgres-0.3.0}/tests/conftest.py +0 -0
- {b3_prosaic_harness_postgres-0.2.1 → b3_prosaic_harness_postgres-0.3.0}/tests/support/model_server.py +0 -0
- {b3_prosaic_harness_postgres-0.2.1 → b3_prosaic_harness_postgres-0.3.0}/tests/support/tcp_proxy.py +0 -0
- {b3_prosaic_harness_postgres-0.2.1 → b3_prosaic_harness_postgres-0.3.0}/tests/support/workers.py +0 -0
- {b3_prosaic_harness_postgres-0.2.1 → b3_prosaic_harness_postgres-0.3.0}/tests/test_cli.py +0 -0
- {b3_prosaic_harness_postgres-0.2.1 → b3_prosaic_harness_postgres-0.3.0}/tests/test_durability.py +0 -0
- {b3_prosaic_harness_postgres-0.2.1 → b3_prosaic_harness_postgres-0.3.0}/tests/test_engine.py +0 -0
- {b3_prosaic_harness_postgres-0.2.1 → b3_prosaic_harness_postgres-0.3.0}/tests/test_examples.py +0 -0
- {b3_prosaic_harness_postgres-0.2.1 → b3_prosaic_harness_postgres-0.3.0}/tests/test_leases.py +0 -0
- {b3_prosaic_harness_postgres-0.2.1 → b3_prosaic_harness_postgres-0.3.0}/tests/test_local_cluster.py +0 -0
- {b3_prosaic_harness_postgres-0.2.1 → b3_prosaic_harness_postgres-0.3.0}/tests/test_native_factory.py +0 -0
- {b3_prosaic_harness_postgres-0.2.1 → b3_prosaic_harness_postgres-0.3.0}/tests/test_recovery.py +0 -0
- {b3_prosaic_harness_postgres-0.2.1 → b3_prosaic_harness_postgres-0.3.0}/tests/test_registration.py +0 -0
- {b3_prosaic_harness_postgres-0.2.1 → b3_prosaic_harness_postgres-0.3.0}/tests/test_routing.py +0 -0
- {b3_prosaic_harness_postgres-0.2.1 → b3_prosaic_harness_postgres-0.3.0}/tests/test_schema.py +0 -0
- {b3_prosaic_harness_postgres-0.2.1 → b3_prosaic_harness_postgres-0.3.0}/tests/test_store.py +0 -0
- {b3_prosaic_harness_postgres-0.2.1 → b3_prosaic_harness_postgres-0.3.0}/tests/test_transport.py +0 -0
- {b3_prosaic_harness_postgres-0.2.1 → b3_prosaic_harness_postgres-0.3.0}/tests/test_workers.py +0 -0
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: b3-prosaic-harness-postgres
|
|
3
|
-
Version: 0.
|
|
4
|
-
Summary: Explicit PostgreSQL run storage for Prosaic Harness
|
|
3
|
+
Version: 0.3.0
|
|
4
|
+
Summary: Explicit PostgreSQL run and conversation storage for Prosaic Harness
|
|
5
5
|
License-Expression: Apache-2.0
|
|
6
6
|
Requires-Python: >=3.11
|
|
7
7
|
Description-Content-Type: text/markdown
|
|
8
8
|
License-File: LICENSE
|
|
9
9
|
License-File: NOTICE
|
|
10
|
-
Requires-Dist: b3-prosaic-harness<0.
|
|
10
|
+
Requires-Dist: b3-prosaic-harness<0.9,>=0.8
|
|
11
11
|
Requires-Dist: psycopg[binary]<4,>=3.2.3
|
|
12
12
|
Requires-Dist: psycopg-pool<4,>=3.2
|
|
13
13
|
Provides-Extra: test
|
|
@@ -24,11 +24,11 @@ Run on worker A, inspect/resume on worker B, without a shared writable run
|
|
|
24
24
|
directory. Core remains database-free; install this separate adapter only when
|
|
25
25
|
you need shared durable state. The API is synchronous; a private I/O thread
|
|
26
26
|
bounds network waits. Model execution holds no database connection/transaction.
|
|
27
|
-
The
|
|
28
|
-
`b3-prosaic-harness>=0.
|
|
27
|
+
The conversation candidate is `b3-prosaic-harness-postgres` **0.3.0**, selecting
|
|
28
|
+
`b3-prosaic-harness>=0.8,<0.9`. The adapter is maintained in this repository but
|
|
29
29
|
installed and versioned separately, keeping PostgreSQL drivers optional for
|
|
30
30
|
Harness users. Imports and CLI names remain unchanged. Final qualification and
|
|
31
|
-
publication of
|
|
31
|
+
publication of this conversation release train are pending. The historical
|
|
32
32
|
[Harness v0.6.1 release](https://github.com/B3Cognition/prosaic-harness/releases/tag/v0.6.1)
|
|
33
33
|
retains its original 0.6.1/0.1.1 package names and receipts.
|
|
34
34
|
|
|
@@ -49,7 +49,7 @@ prosaic-harness-postgres --help
|
|
|
49
49
|
|
|
50
50
|
ALWAYS migrate using a fresh environment or image; NEVER install legacy and b3
|
|
51
51
|
distributions together because they own the same import paths. See
|
|
52
|
-
[package migration](../../docs/package-migration.md). The Runtime 0.
|
|
52
|
+
[package migration](../../docs/package-migration.md). The Runtime 0.9 candidate
|
|
53
53
|
must be available before this installation can resolve.
|
|
54
54
|
|
|
55
55
|
Harness does **not** provision PostgreSQL, create databases/users, start a
|
|
@@ -220,3 +220,99 @@ session capacity atomically under the existing store mutex. A registering lease
|
|
|
220
220
|
can no longer be counted twice while maintenance is scheduled. The database
|
|
221
221
|
storage/fencing contract is unchanged. A unit scheduling-boundary regression and
|
|
222
222
|
an owned PostgreSQL 16/18 race regression cover the transition.
|
|
223
|
+
|
|
224
|
+
## Optional conversation sessions
|
|
225
|
+
|
|
226
|
+
The conversation candidate adds `PostgresSessionStore` to this same optional
|
|
227
|
+
distribution. It implements the public Harness `SessionStore` contract and uses
|
|
228
|
+
the independent, logged `prosaic_harness_conversations` schema, storage version 1.
|
|
229
|
+
Existing `PostgresRunStore` tables, grants and CLI `init`/`doctor` behavior remain
|
|
230
|
+
compatible; those CLI commands continue to address workflow storage.
|
|
231
|
+
|
|
232
|
+
Initialize conversation storage once with the schema owner's credentials:
|
|
233
|
+
|
|
234
|
+
```python
|
|
235
|
+
from prosaic_harness_postgres import PostgresSessionStore
|
|
236
|
+
|
|
237
|
+
with PostgresSessionStore.from_env(
|
|
238
|
+
'HARNESS_MIGRATION_DSN', namespace='my-chat'
|
|
239
|
+
) as owner:
|
|
240
|
+
owner.initialize()
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
Apply these grants as the owner, replacing `chat_runtime` with the existing
|
|
244
|
+
application role. Role, database and secret creation remain operator tasks.
|
|
245
|
+
|
|
246
|
+
```sql
|
|
247
|
+
GRANT USAGE ON SCHEMA prosaic_harness_conversations TO chat_runtime;
|
|
248
|
+
GRANT SELECT ON prosaic_harness_conversations.schema_version,
|
|
249
|
+
prosaic_harness_conversations.leases,
|
|
250
|
+
prosaic_harness_conversations.sessions,
|
|
251
|
+
prosaic_harness_conversations.receipts TO chat_runtime;
|
|
252
|
+
GRANT INSERT, UPDATE ON prosaic_harness_conversations.leases,
|
|
253
|
+
prosaic_harness_conversations.sessions TO chat_runtime;
|
|
254
|
+
GRANT INSERT ON prosaic_harness_conversations.receipts TO chat_runtime;
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
The runtime role needs no schema CREATE, schema-version UPDATE, receipt UPDATE
|
|
258
|
+
or DELETE. A role with only workflow grants can continue using `PostgresRunStore`
|
|
259
|
+
and cannot use conversation storage until it receives the separate grants.
|
|
260
|
+
Conversation readiness rejects forbidden effective grants on non-owner runtime
|
|
261
|
+
roles, including inherited and column-only grants. It also rejects schema-version
|
|
262
|
+
INSERT/DELETE and table DELETE/TRUNCATE privileges. Revoke accidental extra
|
|
263
|
+
permissions before accepting work.
|
|
264
|
+
|
|
265
|
+
Schema owners and roles that immediately inherit schema-owner privileges remain
|
|
266
|
+
administrative credentials: `initialize()` and operator `check_ready()` can
|
|
267
|
+
validate their installation. Those checks cannot make an owner a restricted
|
|
268
|
+
role. ALWAYS configure worker processes with separate non-owner runtime
|
|
269
|
+
credentials and verify their ownership/grants; NEVER use migration credentials
|
|
270
|
+
as the application's runtime DSN.
|
|
271
|
+
Readiness performs no migration or fallback:
|
|
272
|
+
|
|
273
|
+
```python
|
|
274
|
+
from prosaic_harness import Conversation, ConversationScope
|
|
275
|
+
from prosaic_harness_postgres import PostgresSessionStore
|
|
276
|
+
|
|
277
|
+
scope = ConversationScope('my-chat', 'production', 'my-service', tenant, actor)
|
|
278
|
+
with PostgresSessionStore.from_env(
|
|
279
|
+
'HARNESS_RUNTIME_DSN', namespace=scope.namespace
|
|
280
|
+
) as sessions:
|
|
281
|
+
sessions.check_ready()
|
|
282
|
+
chat = Conversation(approved_bindings, store=sessions, scope=scope)
|
|
283
|
+
created = chat.create()
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
Derive `tenant`, `actor` and any billing account from current authenticated host
|
|
287
|
+
state before each public operation. Scope ownership filters every session and
|
|
288
|
+
receipt read; leases, revisions and writes also bind to the exact scope and
|
|
289
|
+
address. A foreign owner sees `session_not_found`. The SQL role can still access
|
|
290
|
+
its granted tables directly: namespaces and scope hashes do not replace host
|
|
291
|
+
authorization or database row-level security.
|
|
292
|
+
|
|
293
|
+
Use the same sealed bindings and logical workspace/journal identities on every
|
|
294
|
+
worker. The default bounded pools and lease timings documented above apply.
|
|
295
|
+
Each operation checks the writable primary and WAL settings, acquires a short
|
|
296
|
+
transaction, then returns its connection. A conversation's model, tool and
|
|
297
|
+
linked workflow execution holds no transaction or checked-out connection.
|
|
298
|
+
Database generations fence late writes after ownership changes; they cannot
|
|
299
|
+
cancel an external effect already in progress.
|
|
300
|
+
|
|
301
|
+
Snapshots and immutable receipts use the common finite canonical UTF-8 session
|
|
302
|
+
codec, with at most 8 MiB each. Their addresses and receipts are checked against
|
|
303
|
+
allocated attempts, dispatch markers and the frozen scope/binding/input/context.
|
|
304
|
+
Exact receipt repeats are idempotent under a live lease; conflicts fail without
|
|
305
|
+
overwriting evidence. A lost COMMIT acknowledgement raises `store_unavailable`
|
|
306
|
+
with `outcome_unknown` and invalidates the lease. Recover by reading authoritative
|
|
307
|
+
state with fresh ownership; never blindly repeat inference or business effects.
|
|
308
|
+
|
|
309
|
+
The session policy bounds retained/reserved logical records. PostgreSQL indexes,
|
|
310
|
+
WAL, tool journals, replicas and backups add physical storage. The host must also
|
|
311
|
+
enforce retained-session quotas and own any explicit retention/deletion policy;
|
|
312
|
+
this adapter never automatically expires or deletes records.
|
|
313
|
+
|
|
314
|
+
The required adapter suite includes both the existing workflow backend and the
|
|
315
|
+
conversation backend. Run the owned PostgreSQL 16 and 18 fixtures shown above
|
|
316
|
+
with `--require-postgres`. Missing infrastructure or any skipped required case
|
|
317
|
+
fails qualification. Installed Linux AMD64 and ARM64 acceptance is required
|
|
318
|
+
before claiming the conversation candidate is ready for production.
|
|
@@ -4,11 +4,11 @@ Run on worker A, inspect/resume on worker B, without a shared writable run
|
|
|
4
4
|
directory. Core remains database-free; install this separate adapter only when
|
|
5
5
|
you need shared durable state. The API is synchronous; a private I/O thread
|
|
6
6
|
bounds network waits. Model execution holds no database connection/transaction.
|
|
7
|
-
The
|
|
8
|
-
`b3-prosaic-harness>=0.
|
|
7
|
+
The conversation candidate is `b3-prosaic-harness-postgres` **0.3.0**, selecting
|
|
8
|
+
`b3-prosaic-harness>=0.8,<0.9`. The adapter is maintained in this repository but
|
|
9
9
|
installed and versioned separately, keeping PostgreSQL drivers optional for
|
|
10
10
|
Harness users. Imports and CLI names remain unchanged. Final qualification and
|
|
11
|
-
publication of
|
|
11
|
+
publication of this conversation release train are pending. The historical
|
|
12
12
|
[Harness v0.6.1 release](https://github.com/B3Cognition/prosaic-harness/releases/tag/v0.6.1)
|
|
13
13
|
retains its original 0.6.1/0.1.1 package names and receipts.
|
|
14
14
|
|
|
@@ -29,7 +29,7 @@ prosaic-harness-postgres --help
|
|
|
29
29
|
|
|
30
30
|
ALWAYS migrate using a fresh environment or image; NEVER install legacy and b3
|
|
31
31
|
distributions together because they own the same import paths. See
|
|
32
|
-
[package migration](../../docs/package-migration.md). The Runtime 0.
|
|
32
|
+
[package migration](../../docs/package-migration.md). The Runtime 0.9 candidate
|
|
33
33
|
must be available before this installation can resolve.
|
|
34
34
|
|
|
35
35
|
Harness does **not** provision PostgreSQL, create databases/users, start a
|
|
@@ -200,3 +200,99 @@ session capacity atomically under the existing store mutex. A registering lease
|
|
|
200
200
|
can no longer be counted twice while maintenance is scheduled. The database
|
|
201
201
|
storage/fencing contract is unchanged. A unit scheduling-boundary regression and
|
|
202
202
|
an owned PostgreSQL 16/18 race regression cover the transition.
|
|
203
|
+
|
|
204
|
+
## Optional conversation sessions
|
|
205
|
+
|
|
206
|
+
The conversation candidate adds `PostgresSessionStore` to this same optional
|
|
207
|
+
distribution. It implements the public Harness `SessionStore` contract and uses
|
|
208
|
+
the independent, logged `prosaic_harness_conversations` schema, storage version 1.
|
|
209
|
+
Existing `PostgresRunStore` tables, grants and CLI `init`/`doctor` behavior remain
|
|
210
|
+
compatible; those CLI commands continue to address workflow storage.
|
|
211
|
+
|
|
212
|
+
Initialize conversation storage once with the schema owner's credentials:
|
|
213
|
+
|
|
214
|
+
```python
|
|
215
|
+
from prosaic_harness_postgres import PostgresSessionStore
|
|
216
|
+
|
|
217
|
+
with PostgresSessionStore.from_env(
|
|
218
|
+
'HARNESS_MIGRATION_DSN', namespace='my-chat'
|
|
219
|
+
) as owner:
|
|
220
|
+
owner.initialize()
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
Apply these grants as the owner, replacing `chat_runtime` with the existing
|
|
224
|
+
application role. Role, database and secret creation remain operator tasks.
|
|
225
|
+
|
|
226
|
+
```sql
|
|
227
|
+
GRANT USAGE ON SCHEMA prosaic_harness_conversations TO chat_runtime;
|
|
228
|
+
GRANT SELECT ON prosaic_harness_conversations.schema_version,
|
|
229
|
+
prosaic_harness_conversations.leases,
|
|
230
|
+
prosaic_harness_conversations.sessions,
|
|
231
|
+
prosaic_harness_conversations.receipts TO chat_runtime;
|
|
232
|
+
GRANT INSERT, UPDATE ON prosaic_harness_conversations.leases,
|
|
233
|
+
prosaic_harness_conversations.sessions TO chat_runtime;
|
|
234
|
+
GRANT INSERT ON prosaic_harness_conversations.receipts TO chat_runtime;
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
The runtime role needs no schema CREATE, schema-version UPDATE, receipt UPDATE
|
|
238
|
+
or DELETE. A role with only workflow grants can continue using `PostgresRunStore`
|
|
239
|
+
and cannot use conversation storage until it receives the separate grants.
|
|
240
|
+
Conversation readiness rejects forbidden effective grants on non-owner runtime
|
|
241
|
+
roles, including inherited and column-only grants. It also rejects schema-version
|
|
242
|
+
INSERT/DELETE and table DELETE/TRUNCATE privileges. Revoke accidental extra
|
|
243
|
+
permissions before accepting work.
|
|
244
|
+
|
|
245
|
+
Schema owners and roles that immediately inherit schema-owner privileges remain
|
|
246
|
+
administrative credentials: `initialize()` and operator `check_ready()` can
|
|
247
|
+
validate their installation. Those checks cannot make an owner a restricted
|
|
248
|
+
role. ALWAYS configure worker processes with separate non-owner runtime
|
|
249
|
+
credentials and verify their ownership/grants; NEVER use migration credentials
|
|
250
|
+
as the application's runtime DSN.
|
|
251
|
+
Readiness performs no migration or fallback:
|
|
252
|
+
|
|
253
|
+
```python
|
|
254
|
+
from prosaic_harness import Conversation, ConversationScope
|
|
255
|
+
from prosaic_harness_postgres import PostgresSessionStore
|
|
256
|
+
|
|
257
|
+
scope = ConversationScope('my-chat', 'production', 'my-service', tenant, actor)
|
|
258
|
+
with PostgresSessionStore.from_env(
|
|
259
|
+
'HARNESS_RUNTIME_DSN', namespace=scope.namespace
|
|
260
|
+
) as sessions:
|
|
261
|
+
sessions.check_ready()
|
|
262
|
+
chat = Conversation(approved_bindings, store=sessions, scope=scope)
|
|
263
|
+
created = chat.create()
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
Derive `tenant`, `actor` and any billing account from current authenticated host
|
|
267
|
+
state before each public operation. Scope ownership filters every session and
|
|
268
|
+
receipt read; leases, revisions and writes also bind to the exact scope and
|
|
269
|
+
address. A foreign owner sees `session_not_found`. The SQL role can still access
|
|
270
|
+
its granted tables directly: namespaces and scope hashes do not replace host
|
|
271
|
+
authorization or database row-level security.
|
|
272
|
+
|
|
273
|
+
Use the same sealed bindings and logical workspace/journal identities on every
|
|
274
|
+
worker. The default bounded pools and lease timings documented above apply.
|
|
275
|
+
Each operation checks the writable primary and WAL settings, acquires a short
|
|
276
|
+
transaction, then returns its connection. A conversation's model, tool and
|
|
277
|
+
linked workflow execution holds no transaction or checked-out connection.
|
|
278
|
+
Database generations fence late writes after ownership changes; they cannot
|
|
279
|
+
cancel an external effect already in progress.
|
|
280
|
+
|
|
281
|
+
Snapshots and immutable receipts use the common finite canonical UTF-8 session
|
|
282
|
+
codec, with at most 8 MiB each. Their addresses and receipts are checked against
|
|
283
|
+
allocated attempts, dispatch markers and the frozen scope/binding/input/context.
|
|
284
|
+
Exact receipt repeats are idempotent under a live lease; conflicts fail without
|
|
285
|
+
overwriting evidence. A lost COMMIT acknowledgement raises `store_unavailable`
|
|
286
|
+
with `outcome_unknown` and invalidates the lease. Recover by reading authoritative
|
|
287
|
+
state with fresh ownership; never blindly repeat inference or business effects.
|
|
288
|
+
|
|
289
|
+
The session policy bounds retained/reserved logical records. PostgreSQL indexes,
|
|
290
|
+
WAL, tool journals, replicas and backups add physical storage. The host must also
|
|
291
|
+
enforce retained-session quotas and own any explicit retention/deletion policy;
|
|
292
|
+
this adapter never automatically expires or deletes records.
|
|
293
|
+
|
|
294
|
+
The required adapter suite includes both the existing workflow backend and the
|
|
295
|
+
conversation backend. Run the owned PostgreSQL 16 and 18 fixtures shown above
|
|
296
|
+
with `--require-postgres`. Missing infrastructure or any skipped required case
|
|
297
|
+
fails qualification. Installed Linux AMD64 and ARM64 acceptance is required
|
|
298
|
+
before claiming the conversation candidate is ready for production.
|
|
@@ -4,13 +4,13 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "b3-prosaic-harness-postgres"
|
|
7
|
-
version = "0.
|
|
8
|
-
description = "Explicit PostgreSQL run storage for Prosaic Harness"
|
|
7
|
+
version = "0.3.0"
|
|
8
|
+
description = "Explicit PostgreSQL run and conversation storage for Prosaic Harness"
|
|
9
9
|
requires-python = ">=3.11"
|
|
10
10
|
readme = "README.md"
|
|
11
11
|
license = "Apache-2.0"
|
|
12
12
|
license-files = ["LICENSE", "NOTICE"]
|
|
13
|
-
dependencies = ["b3-prosaic-harness>=0.
|
|
13
|
+
dependencies = ["b3-prosaic-harness>=0.8,<0.9", "psycopg[binary]>=3.2.3,<4", "psycopg-pool>=3.2,<4"]
|
|
14
14
|
|
|
15
15
|
[project.optional-dependencies]
|
|
16
16
|
test = ["pytest>=8", "build>=1", "setuptools>=77", "wheel", "packaging"]
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: b3-prosaic-harness-postgres
|
|
3
|
-
Version: 0.
|
|
4
|
-
Summary: Explicit PostgreSQL run storage for Prosaic Harness
|
|
3
|
+
Version: 0.3.0
|
|
4
|
+
Summary: Explicit PostgreSQL run and conversation storage for Prosaic Harness
|
|
5
5
|
License-Expression: Apache-2.0
|
|
6
6
|
Requires-Python: >=3.11
|
|
7
7
|
Description-Content-Type: text/markdown
|
|
8
8
|
License-File: LICENSE
|
|
9
9
|
License-File: NOTICE
|
|
10
|
-
Requires-Dist: b3-prosaic-harness<0.
|
|
10
|
+
Requires-Dist: b3-prosaic-harness<0.9,>=0.8
|
|
11
11
|
Requires-Dist: psycopg[binary]<4,>=3.2.3
|
|
12
12
|
Requires-Dist: psycopg-pool<4,>=3.2
|
|
13
13
|
Provides-Extra: test
|
|
@@ -24,11 +24,11 @@ Run on worker A, inspect/resume on worker B, without a shared writable run
|
|
|
24
24
|
directory. Core remains database-free; install this separate adapter only when
|
|
25
25
|
you need shared durable state. The API is synchronous; a private I/O thread
|
|
26
26
|
bounds network waits. Model execution holds no database connection/transaction.
|
|
27
|
-
The
|
|
28
|
-
`b3-prosaic-harness>=0.
|
|
27
|
+
The conversation candidate is `b3-prosaic-harness-postgres` **0.3.0**, selecting
|
|
28
|
+
`b3-prosaic-harness>=0.8,<0.9`. The adapter is maintained in this repository but
|
|
29
29
|
installed and versioned separately, keeping PostgreSQL drivers optional for
|
|
30
30
|
Harness users. Imports and CLI names remain unchanged. Final qualification and
|
|
31
|
-
publication of
|
|
31
|
+
publication of this conversation release train are pending. The historical
|
|
32
32
|
[Harness v0.6.1 release](https://github.com/B3Cognition/prosaic-harness/releases/tag/v0.6.1)
|
|
33
33
|
retains its original 0.6.1/0.1.1 package names and receipts.
|
|
34
34
|
|
|
@@ -49,7 +49,7 @@ prosaic-harness-postgres --help
|
|
|
49
49
|
|
|
50
50
|
ALWAYS migrate using a fresh environment or image; NEVER install legacy and b3
|
|
51
51
|
distributions together because they own the same import paths. See
|
|
52
|
-
[package migration](../../docs/package-migration.md). The Runtime 0.
|
|
52
|
+
[package migration](../../docs/package-migration.md). The Runtime 0.9 candidate
|
|
53
53
|
must be available before this installation can resolve.
|
|
54
54
|
|
|
55
55
|
Harness does **not** provision PostgreSQL, create databases/users, start a
|
|
@@ -220,3 +220,99 @@ session capacity atomically under the existing store mutex. A registering lease
|
|
|
220
220
|
can no longer be counted twice while maintenance is scheduled. The database
|
|
221
221
|
storage/fencing contract is unchanged. A unit scheduling-boundary regression and
|
|
222
222
|
an owned PostgreSQL 16/18 race regression cover the transition.
|
|
223
|
+
|
|
224
|
+
## Optional conversation sessions
|
|
225
|
+
|
|
226
|
+
The conversation candidate adds `PostgresSessionStore` to this same optional
|
|
227
|
+
distribution. It implements the public Harness `SessionStore` contract and uses
|
|
228
|
+
the independent, logged `prosaic_harness_conversations` schema, storage version 1.
|
|
229
|
+
Existing `PostgresRunStore` tables, grants and CLI `init`/`doctor` behavior remain
|
|
230
|
+
compatible; those CLI commands continue to address workflow storage.
|
|
231
|
+
|
|
232
|
+
Initialize conversation storage once with the schema owner's credentials:
|
|
233
|
+
|
|
234
|
+
```python
|
|
235
|
+
from prosaic_harness_postgres import PostgresSessionStore
|
|
236
|
+
|
|
237
|
+
with PostgresSessionStore.from_env(
|
|
238
|
+
'HARNESS_MIGRATION_DSN', namespace='my-chat'
|
|
239
|
+
) as owner:
|
|
240
|
+
owner.initialize()
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
Apply these grants as the owner, replacing `chat_runtime` with the existing
|
|
244
|
+
application role. Role, database and secret creation remain operator tasks.
|
|
245
|
+
|
|
246
|
+
```sql
|
|
247
|
+
GRANT USAGE ON SCHEMA prosaic_harness_conversations TO chat_runtime;
|
|
248
|
+
GRANT SELECT ON prosaic_harness_conversations.schema_version,
|
|
249
|
+
prosaic_harness_conversations.leases,
|
|
250
|
+
prosaic_harness_conversations.sessions,
|
|
251
|
+
prosaic_harness_conversations.receipts TO chat_runtime;
|
|
252
|
+
GRANT INSERT, UPDATE ON prosaic_harness_conversations.leases,
|
|
253
|
+
prosaic_harness_conversations.sessions TO chat_runtime;
|
|
254
|
+
GRANT INSERT ON prosaic_harness_conversations.receipts TO chat_runtime;
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
The runtime role needs no schema CREATE, schema-version UPDATE, receipt UPDATE
|
|
258
|
+
or DELETE. A role with only workflow grants can continue using `PostgresRunStore`
|
|
259
|
+
and cannot use conversation storage until it receives the separate grants.
|
|
260
|
+
Conversation readiness rejects forbidden effective grants on non-owner runtime
|
|
261
|
+
roles, including inherited and column-only grants. It also rejects schema-version
|
|
262
|
+
INSERT/DELETE and table DELETE/TRUNCATE privileges. Revoke accidental extra
|
|
263
|
+
permissions before accepting work.
|
|
264
|
+
|
|
265
|
+
Schema owners and roles that immediately inherit schema-owner privileges remain
|
|
266
|
+
administrative credentials: `initialize()` and operator `check_ready()` can
|
|
267
|
+
validate their installation. Those checks cannot make an owner a restricted
|
|
268
|
+
role. ALWAYS configure worker processes with separate non-owner runtime
|
|
269
|
+
credentials and verify their ownership/grants; NEVER use migration credentials
|
|
270
|
+
as the application's runtime DSN.
|
|
271
|
+
Readiness performs no migration or fallback:
|
|
272
|
+
|
|
273
|
+
```python
|
|
274
|
+
from prosaic_harness import Conversation, ConversationScope
|
|
275
|
+
from prosaic_harness_postgres import PostgresSessionStore
|
|
276
|
+
|
|
277
|
+
scope = ConversationScope('my-chat', 'production', 'my-service', tenant, actor)
|
|
278
|
+
with PostgresSessionStore.from_env(
|
|
279
|
+
'HARNESS_RUNTIME_DSN', namespace=scope.namespace
|
|
280
|
+
) as sessions:
|
|
281
|
+
sessions.check_ready()
|
|
282
|
+
chat = Conversation(approved_bindings, store=sessions, scope=scope)
|
|
283
|
+
created = chat.create()
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
Derive `tenant`, `actor` and any billing account from current authenticated host
|
|
287
|
+
state before each public operation. Scope ownership filters every session and
|
|
288
|
+
receipt read; leases, revisions and writes also bind to the exact scope and
|
|
289
|
+
address. A foreign owner sees `session_not_found`. The SQL role can still access
|
|
290
|
+
its granted tables directly: namespaces and scope hashes do not replace host
|
|
291
|
+
authorization or database row-level security.
|
|
292
|
+
|
|
293
|
+
Use the same sealed bindings and logical workspace/journal identities on every
|
|
294
|
+
worker. The default bounded pools and lease timings documented above apply.
|
|
295
|
+
Each operation checks the writable primary and WAL settings, acquires a short
|
|
296
|
+
transaction, then returns its connection. A conversation's model, tool and
|
|
297
|
+
linked workflow execution holds no transaction or checked-out connection.
|
|
298
|
+
Database generations fence late writes after ownership changes; they cannot
|
|
299
|
+
cancel an external effect already in progress.
|
|
300
|
+
|
|
301
|
+
Snapshots and immutable receipts use the common finite canonical UTF-8 session
|
|
302
|
+
codec, with at most 8 MiB each. Their addresses and receipts are checked against
|
|
303
|
+
allocated attempts, dispatch markers and the frozen scope/binding/input/context.
|
|
304
|
+
Exact receipt repeats are idempotent under a live lease; conflicts fail without
|
|
305
|
+
overwriting evidence. A lost COMMIT acknowledgement raises `store_unavailable`
|
|
306
|
+
with `outcome_unknown` and invalidates the lease. Recover by reading authoritative
|
|
307
|
+
state with fresh ownership; never blindly repeat inference or business effects.
|
|
308
|
+
|
|
309
|
+
The session policy bounds retained/reserved logical records. PostgreSQL indexes,
|
|
310
|
+
WAL, tool journals, replicas and backups add physical storage. The host must also
|
|
311
|
+
enforce retained-session quotas and own any explicit retention/deletion policy;
|
|
312
|
+
this adapter never automatically expires or deletes records.
|
|
313
|
+
|
|
314
|
+
The required adapter suite includes both the existing workflow backend and the
|
|
315
|
+
conversation backend. Run the owned PostgreSQL 16 and 18 fixtures shown above
|
|
316
|
+
with `--require-postgres`. Missing infrastructure or any skipped required case
|
|
317
|
+
fails qualification. Installed Linux AMD64 and ARM64 acceptance is required
|
|
318
|
+
before claiming the conversation candidate is ready for production.
|
|
@@ -13,6 +13,9 @@ src/prosaic_harness_postgres/__init__.py
|
|
|
13
13
|
src/prosaic_harness_postgres/cli.py
|
|
14
14
|
src/prosaic_harness_postgres/leases.py
|
|
15
15
|
src/prosaic_harness_postgres/schema.py
|
|
16
|
+
src/prosaic_harness_postgres/session_leases.py
|
|
17
|
+
src/prosaic_harness_postgres/session_schema.py
|
|
18
|
+
src/prosaic_harness_postgres/session_store.py
|
|
16
19
|
src/prosaic_harness_postgres/store.py
|
|
17
20
|
src/prosaic_harness_postgres/transport.py
|
|
18
21
|
tests/conftest.py
|
|
@@ -28,11 +31,18 @@ tests/test_recovery.py
|
|
|
28
31
|
tests/test_registration.py
|
|
29
32
|
tests/test_routing.py
|
|
30
33
|
tests/test_schema.py
|
|
34
|
+
tests/test_session_fencing.py
|
|
35
|
+
tests/test_session_recovery.py
|
|
36
|
+
tests/test_session_schema.py
|
|
37
|
+
tests/test_session_store.py
|
|
38
|
+
tests/test_session_workers.py
|
|
31
39
|
tests/test_store.py
|
|
32
40
|
tests/test_transport.py
|
|
33
41
|
tests/test_workers.py
|
|
34
42
|
tests/support/linux_wheel_smoke.py
|
|
35
43
|
tests/support/local_cluster.py
|
|
36
44
|
tests/support/model_server.py
|
|
45
|
+
tests/support/session_documents.py
|
|
46
|
+
tests/support/session_workers.py
|
|
37
47
|
tests/support/tcp_proxy.py
|
|
38
48
|
tests/support/workers.py
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"""Scoped conversation leases use the existing bounded renewal lifecycle."""
|
|
2
|
+
from .leases import PostgresLease
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class PostgresSessionLease(PostgresLease):
|
|
6
|
+
def __init__(self, store, session_id, scope_content, scope_identity, started):
|
|
7
|
+
super().__init__(store, session_id, started)
|
|
8
|
+
self.session_id = session_id
|
|
9
|
+
self.scope_content = scope_content
|
|
10
|
+
self.scope_identity = scope_identity
|