Skip to content
Draft
Show file tree
Hide file tree
Changes from 140 commits
Commits
Show all changes
241 commits
Select commit Hold shift + click to select a range
603cdda
test(postgres): define driver replacement port contract
seonghobae Sep 2, 2026
7ff3036
feat(postgres): add driver-neutral database port
seonghobae Sep 2, 2026
2139d04
test(postgres): cover connection lifecycle capabilities
seonghobae Sep 2, 2026
001cf7d
feat(postgres): preserve autocommit and closed-state contracts
seonghobae Sep 2, 2026
3fe4442
test(postgres): require affected-row contract
seonghobae Sep 2, 2026
33607e3
feat(postgres): preserve affected-row semantics
seonghobae Sep 2, 2026
c9d5ee0
test(postgres): define psycopg adapter parity contract
seonghobae Sep 2, 2026
7719259
feat(postgres): adapt current psycopg behavior behind driver port
seonghobae Sep 2, 2026
c71df81
fix(postgres): align driver timeout contract with libpq seconds
seonghobae Sep 2, 2026
4392121
test(postgres): keep driver port timeout contract exact
seonghobae Sep 2, 2026
b97973d
test(postgres): define commercial driver candidate acceptance
seonghobae Sep 2, 2026
f22db09
test(postgres): fail closed on mutable candidate evidence
seonghobae Sep 2, 2026
ec2e56c
feat(postgres): gate commercial driver candidates fail closed
seonghobae Sep 2, 2026
2ca0f9a
test(postgres): reject mutable candidate evidence
seonghobae Sep 2, 2026
e390ded
fix(postgres): revalidate driver candidate evidence
seonghobae Sep 2, 2026
ec26ce5
test(postgres): bound driver row fetches
seonghobae Sep 2, 2026
0bbcc4a
fix(postgres): enforce finite fetch budgets
seonghobae Sep 2, 2026
b457207
test(postgres): require conninfo error classification
seonghobae Sep 2, 2026
a5db16c
test(postgres): classify conninfo grammar errors
seonghobae Sep 2, 2026
cbfd335
feat(postgres): classify invalid conninfo through port
seonghobae Sep 2, 2026
bb77391
feat(postgres): isolate conninfo error type
seonghobae Sep 2, 2026
06746dc
fix(postgres): require conninfo error parity
seonghobae Sep 2, 2026
e748dad
test(postgres): require full type and parameter parity
seonghobae Sep 2, 2026
adfd1e8
fix(postgres): require full driver parity evidence
seonghobae Sep 2, 2026
6524fdf
test(postgres): reject broad ProgrammingError conninfo classification
seonghobae Sep 2, 2026
6c29ff1
fix(postgres): narrow conninfo error classification
seonghobae Sep 2, 2026
4e9486b
test(postgres): require canonical tuple result rows
seonghobae Sep 2, 2026
8dc90b2
fix(postgres): normalize driver result rows to tuples
seonghobae Sep 2, 2026
2886f90
test(postgres): reject null rows inside result pages
seonghobae Sep 2, 2026
20e00e5
fix(postgres): fail closed on malformed result pages
seonghobae Sep 2, 2026
b51c97e
test(postgres): bind port to tuple row semantics
seonghobae Sep 2, 2026
9e1bfa7
fix(postgres): declare canonical tuple result contract
seonghobae Sep 2, 2026
5bb1a1c
test(postgres): require checkpoint store driver-port injection
seonghobae Sep 2, 2026
43713a1
feat(postgres): route checkpoint persistence through driver port
seonghobae Sep 2, 2026
07962cb
test(postgres): require readiness driver-port injection
seonghobae Sep 2, 2026
1f1c6ae
feat(postgres): route readiness through driver port
seonghobae Sep 2, 2026
6f46200
fix(postgres): preserve health connection failure boundary
seonghobae Sep 2, 2026
d7fcd4f
test(postgres): require config stores driver-port injection
seonghobae Sep 2, 2026
7f31ef5
feat(postgres): route config stores through driver port
seonghobae Sep 2, 2026
9b30ce6
test(postgres): require config setup failure cleanup
seonghobae Sep 2, 2026
41efac7
fix(postgres): preserve config setup cleanup
seonghobae Sep 2, 2026
804d965
chore(postgres): preserve scanner evidence comments
seonghobae Sep 2, 2026
71451e5
test(health): redact driver failure details
seonghobae Sep 2, 2026
cf69773
fix(health): bound database failure details
seonghobae Sep 2, 2026
88cf659
test(health): require server driver forwarding
seonghobae Sep 2, 2026
88b44ce
fix(health): forward selected postgres driver
seonghobae Sep 2, 2026
a63fedc
test(compose): require postgres driver bootstrap seam
seonghobae Sep 2, 2026
825efc9
fix(compose): route bootstrap through postgres driver port
seonghobae Sep 2, 2026
9e4ab4b
test(postgres): bound driver candidate identity evidence
seonghobae Sep 2, 2026
7647dac
fix(postgres): bound driver candidate identity evidence
seonghobae Sep 2, 2026
f05a84f
test(postgres): reject control-bearing driver identity
seonghobae Sep 2, 2026
49ed804
fix(postgres): reject control-bearing driver identity
seonghobae Sep 2, 2026
1df9614
style(postgres): keep candidate identity guard lint-safe
seonghobae Sep 2, 2026
7a12c0c
test(postgres): require full supported Python parity
seonghobae Sep 2, 2026
8b4772d
fix(postgres): require full supported Python parity
seonghobae Sep 2, 2026
b545718
test(postgres): decouple schema and payload driver boundary
seonghobae Sep 2, 2026
6607b7a
feat(postgres): decouple schema and payload driver boundary
seonghobae Sep 2, 2026
b07c110
test(postgres): decouple model metadata driver boundary
seonghobae Sep 2, 2026
81fff93
feat(postgres): decouple model metadata driver boundary
seonghobae Sep 2, 2026
fb7280c
test(postgres): decouple durable lifecycle driver boundary
seonghobae Sep 2, 2026
e2cae6c
feat(postgres): decouple durable lifecycle driver boundary
seonghobae Sep 2, 2026
7bebc7b
test(postgres): reject Unicode format controls in candidate identity
seonghobae Sep 2, 2026
b0ae7c4
fix(postgres): reject format-control candidate identities
seonghobae Sep 2, 2026
53bffc4
test(postgres): define token counter driver-port seam
seonghobae Sep 2, 2026
48badbe
feat(postgres): route token counting through driver port
seonghobae Sep 2, 2026
942b7eb
test(postgres): distinguish candidate token-count errors
seonghobae Sep 2, 2026
8b8c7fc
fix(postgres): preserve token counter on transient driver errors
seonghobae Sep 2, 2026
27240e4
test(postgres): define orchestrator driver-port seam
seonghobae Sep 2, 2026
44e4dd2
feat(postgres): route batch orchestration through driver port
seonghobae Sep 2, 2026
ba2a74d
test(postgres): cover orchestrator driver propagation
seonghobae Sep 2, 2026
788cec7
test(cli): require driver-neutral DSN parsing
seonghobae Sep 2, 2026
c6580c0
fix(cli): route DSN parsing through driver port
seonghobae Sep 2, 2026
6eb18e7
test(cli): reject truthiness-based driver selection
seonghobae Sep 2, 2026
22428e5
fix(cli): select injected driver by identity
seonghobae Sep 2, 2026
05cae15
test(postgres): require vulnerability evidence for driver candidates
seonghobae Sep 2, 2026
dbe2727
feat(postgres): bind vulnerability evidence to driver admission
seonghobae Sep 2, 2026
cd46ced
test(postgres): preserve DSN selector parity in candidate gate
seonghobae Sep 2, 2026
0d6296d
feat(postgres): require explicit DSN selector parity
seonghobae Sep 2, 2026
9734dc6
test(postgres): require full supported Python parity
seonghobae Sep 2, 2026
1197ccb
fix(postgres): require full supported Python driver parity
seonghobae Sep 2, 2026
9c1461f
merge: inherit recovery coverage prerequisite
seonghobae Sep 2, 2026
1517b01
test(postgres): bound driver candidate evidence cardinality
seonghobae Sep 2, 2026
4a30a22
fix(postgres): bound driver candidate evidence cardinality
seonghobae Sep 2, 2026
2fa8a18
test(postgres): expose connection context parity gap
seonghobae Sep 2, 2026
934b72b
fix(postgres): require transactional context parity
seonghobae Sep 2, 2026
31b4f2a
test(postgres): bind candidate license evidence digest
seonghobae Sep 2, 2026
6c15f03
test(postgres): require license report on all candidates
seonghobae Sep 2, 2026
223b580
fix(postgres): bind candidate license evidence digest
seonghobae Sep 2, 2026
cbf951e
test(postgres): expose unbound parity evidence reports
seonghobae Sep 2, 2026
2e0b6fa
test(postgres): require versioned candidate evidence schema
seonghobae Sep 2, 2026
c19e7db
fix(postgres): version candidate evidence receipts
seonghobae Sep 2, 2026
9807f6d
test(postgres): reject candidate schema equality spoofing
seonghobae Sep 2, 2026
b9e18ab
fix(postgres): reject spoofed evidence schema versions
seonghobae Sep 2, 2026
5d1078b
test(postgres): reject non-PyPA candidate project names
seonghobae Sep 3, 2026
6b7f598
fix(postgres): validate candidate distribution names
seonghobae Sep 3, 2026
c7c8b35
test(postgres): reject surrogate candidate identity evidence
seonghobae Sep 3, 2026
ba635ec
fix(postgres): fail closed on malformed Unicode evidence
seonghobae Sep 3, 2026
1f8c2b6
test(postgres): expose RLS reservation and row-count ambiguity
seonghobae Sep 3, 2026
e3669fa
fix(postgres): bind RLS reservation and normalize row counts
seonghobae Sep 3, 2026
2b3295b
merge updated #233 into PostgreSQL driver migration
seonghobae Sep 3, 2026
3fb57ad
test(postgres): RED pg8000 candidate DB-API adapter contract
seonghobae Sep 3, 2026
26004c5
feat(postgres): GREEN pg8000 candidate DB-API adapter seam
seonghobae Sep 3, 2026
a92d5af
test(postgres): cover pg8000 candidate adapter edge branches
seonghobae Sep 3, 2026
f1e14cc
test(postgres): remove unused candidate test import
seonghobae Sep 3, 2026
dab97ab
test(postgres): RED fail closed on pg8000 paramstyle drift
seonghobae Sep 3, 2026
d73058a
feat(postgres): GREEN guard pg8000 DB-API parameter mode
seonghobae Sep 3, 2026
5d02d22
test(postgres): require immutable capability report identity
seonghobae Sep 3, 2026
2628a39
feat(postgres): bind candidate capabilities to immutable report
seonghobae Sep 3, 2026
c4f834d
test(postgres): cover capability report digest validation
seonghobae Sep 3, 2026
1a9327a
test(postgres): supply capability report in receipt fixtures
seonghobae Sep 3, 2026
0d10721
test(postgres): bind package-name fixtures to capability report
seonghobae Sep 3, 2026
8eb0c03
test(postgres): bind surrogate fixture to capability report
seonghobae Sep 3, 2026
c6c09e1
chore(pr323): restack driver migration on current recovery root
seonghobae Sep 3, 2026
9268b6d
test(postgres): align driver reservation regression with tenant scope
seonghobae Sep 3, 2026
76b7555
fix(test): preserve nextval fixture after tenant binding
seonghobae Sep 3, 2026
a25c1ae
test(postgres): RED own DB-API cursor cleanup in adapter
seonghobae Sep 3, 2026
11ea837
feat(postgres): GREEN own DB-API cursor cleanup in adapter
seonghobae Sep 3, 2026
b83c330
test(postgres): RED own candidate transaction context
seonghobae Sep 3, 2026
c2bfd6c
feat(postgres): GREEN own candidate transaction context
seonghobae Sep 3, 2026
a04294b
docs(postgres): define canonical connection context policy
seonghobae Sep 3, 2026
85c07b2
test(postgres): RED own candidate closed state
seonghobae Sep 3, 2026
63bee0f
feat(postgres): GREEN own candidate closed state
seonghobae Sep 3, 2026
85a16e2
docs(postgres): bound closed-state semantics
seonghobae Sep 3, 2026
866fd7e
test(postgres): RED discard cached connection after count failure
seonghobae Sep 3, 2026
d09bad9
fix(postgres): GREEN discard failed token-count connection
seonghobae Sep 3, 2026
c97fa09
test(postgres): RED reject pg8000 fetch overdelivery
seonghobae Sep 3, 2026
2304341
fix(postgres): GREEN enforce candidate fetch budget
seonghobae Sep 3, 2026
79de5c4
test(postgres): cover unsized candidate fetch rejection
seonghobae Sep 3, 2026
d8a12f9
test(postgres): add real pg8000 candidate smoke
seonghobae Sep 3, 2026
cd01897
test(postgres): close candidate fixture connection deterministically
seonghobae Sep 3, 2026
3446cc9
test(postgres): probe exact pg8000 artifact on Python 3.14
seonghobae Sep 3, 2026
dc52cb5
fix(ci): fold pg8000 parity into existing runtime job
seonghobae Sep 3, 2026
0d1b29c
test(ci): bind pg8000 candidate parity without new runner lane
seonghobae Sep 3, 2026
498b7dc
test(postgres): keep candidate smoke SQL structurally fixed
seonghobae Sep 3, 2026
6191f3d
fix(ci): generate candidate database credential per run
seonghobae Sep 3, 2026
bdb09e4
test(ci): require ephemeral candidate database credential
seonghobae Sep 3, 2026
89d9c61
test(postgres): preserve candidate transaction failure precedence
seonghobae Sep 3, 2026
b826a33
fix(postgres): preserve candidate transaction failure precedence
seonghobae Sep 3, 2026
25bfcb2
test(postgres): expose candidate execute cursor leak
seonghobae Sep 3, 2026
162f617
fix(postgres): close candidate cursor after execute failure
seonghobae Sep 3, 2026
4ef91ea
chore(postgres): stack driver candidate after CI foundations
seonghobae Sep 3, 2026
8c38c87
test(ci): reject stale setup-uv pin in candidate lane
seonghobae Sep 3, 2026
32fd667
fix(ci): align candidate setup-uv exact pin
seonghobae Sep 3, 2026
8dab90a
test(postgres): require candidate DB-API thread contract
seonghobae Sep 3, 2026
6439b74
fix(postgres): validate candidate DB-API thread semantics
seonghobae Sep 3, 2026
3fa5948
test(postgres): expose shared token connection concurrency
seonghobae Sep 3, 2026
86b57a9
fix(postgres): serialize shared token connection use
seonghobae Sep 3, 2026
b37324f
test(ci): make pg8000 dependency guard case-insensitive
seonghobae Sep 3, 2026
444988a
test(postgres): expose cursor cleanup error precedence
seonghobae Sep 3, 2026
5fc43b2
fix(postgres): preserve cursor application error on cleanup failure
seonghobae Sep 3, 2026
c170505
test(postgres): expose connection cleanup error precedence
seonghobae Sep 3, 2026
84f4fe9
fix(postgres): preserve application error over close failure
seonghobae Sep 3, 2026
6dbcaaf
test(ci): require immutable pg8000 dependency closure
seonghobae Sep 3, 2026
e2f67a7
fix(ci): pin pg8000 candidate dependency closure
seonghobae Sep 3, 2026
df54826
test(postgres): require file-only candidate credential handoff
seonghobae Sep 3, 2026
3a6688e
fix(postgres): read candidate credential from ephemeral file
seonghobae Sep 3, 2026
7ce0666
fix(postgres): minimize candidate credential propagation
seonghobae Sep 3, 2026
67a1e32
test(postgres): align candidate secret contract
seonghobae Sep 3, 2026
a645f98
test(license): require candidate wheel license evidence
seonghobae Sep 4, 2026
7399ff6
feat(license): verify candidate wheel closure metadata
seonghobae Sep 4, 2026
7309974
test(ci): require candidate license gate before install
seonghobae Sep 4, 2026
a315f5a
fix(ci): gate candidate install on license metadata
seonghobae Sep 4, 2026
3b78630
docs(gap): establish live commercial readiness baseline
seonghobae Sep 4, 2026
cf7c346
test(license): reject permissive substring false positives
seonghobae Sep 4, 2026
2d3874b
fix(license): require bounded permissive markers
seonghobae Sep 4, 2026
d2e93fa
test(postgres): reject driver fetch overdelivery
seonghobae Sep 4, 2026
10afce6
fix(postgres): enforce finite cursor fetch budgets
seonghobae Sep 4, 2026
5e74d95
test(postgres): preserve candidate closed state after close error
seonghobae Sep 4, 2026
545961d
test(postgres): keep close-state regression lint-clean
seonghobae Sep 4, 2026
b175332
fix(postgres): retain pg8000 terminal close state on failure
seonghobae Sep 4, 2026
f055a86
test(postgres): reject shaped candidate capability evidence
seonghobae Sep 4, 2026
e12bc01
fix(postgres): reject shaped capability evidence
seonghobae Sep 4, 2026
2feb630
test(compose): require default driver conninfo boundary
seonghobae Sep 4, 2026
78982a2
refactor(compose): route default conninfo through driver port
seonghobae Sep 4, 2026
49566dd
test(postgres): reject cross-thread pg8000 candidate reuse
seonghobae Sep 4, 2026
8372ac4
feat(postgres): add pg8000 thread-affinity candidate boundary
seonghobae Sep 4, 2026
6c95886
test(postgres): bind pg8000 candidate to owner thread
seonghobae Sep 4, 2026
634f8e5
test(postgres): exercise thread-affine pg8000 candidate
seonghobae Sep 4, 2026
f68d75c
test(postgres): require candidate thread-affinity evidence
seonghobae Sep 4, 2026
62126dc
fix(postgres): require candidate connection thread-affinity evidence
seonghobae Sep 4, 2026
1a9008e
test(postgres): add RED pg8000 undefined-function classifier contract
seonghobae Sep 4, 2026
5c5466c
feat(postgres): classify pg8000 undefined-function SQLSTATE
seonghobae Sep 4, 2026
d8f1060
test(postgres): prove pg8000 SQLSTATE classification on real PostgreSQL
seonghobae Sep 4, 2026
85ba4df
test(recovery): add RED terminal pg_tiktoken session cleanup
seonghobae Sep 4, 2026
ff0d9b0
fix(recovery): release pg_tiktoken session after terminal capability …
seonghobae Sep 4, 2026
89344c7
test(postgres): require pg8000 JSONB candidate adapter
seonghobae Sep 4, 2026
65489f0
feat(postgres): add pg8000 JSONB candidate adapter
seonghobae Sep 4, 2026
7007e21
test(postgres): exercise candidate JSONB adapter on real PostgreSQL
seonghobae Sep 4, 2026
509a0f8
test(postgres): pin candidate URI driver boundary
seonghobae Sep 4, 2026
ebafecb
feat(postgres): add candidate URI driver adapter
seonghobae Sep 4, 2026
47803c4
test(postgres): exercise candidate driver factory on real DB
seonghobae Sep 4, 2026
0f09b00
test(postgres): reject ambiguous candidate hosts
seonghobae Sep 4, 2026
ad8515a
fix(postgres): fail closed on ambiguous candidate hosts
seonghobae Sep 4, 2026
6448aa3
test(postgres): require centralized retained driver selection
seonghobae Sep 4, 2026
2d641a2
refactor(postgres): centralize retained driver selection
seonghobae Sep 4, 2026
4ab6c2d
fix(postgres): route default persistence through runtime driver
seonghobae Sep 4, 2026
ea85971
test(postgres): require centralized readiness driver selection
seonghobae Sep 4, 2026
c691694
fix(postgres): route readiness through runtime driver
seonghobae Sep 4, 2026
9c8a9ca
test(postgres): require centralized config driver selection
seonghobae Sep 4, 2026
2ca4a46
fix(postgres): route config stores through runtime driver
seonghobae Sep 4, 2026
d743e84
test(postgres): require centralized token driver selection
seonghobae Sep 4, 2026
c1a31c0
fix(postgres): centralize token driver selection
seonghobae Sep 4, 2026
654e8d8
test(postgres): add driver-port fake adapter
seonghobae Sep 4, 2026
04cb350
test(postgres): migrate token counter fakes to driver port
seonghobae Sep 4, 2026
a872e67
test(postgres): route batch token fakes through driver port
seonghobae Sep 4, 2026
291191b
test(postgres): migrate pg_tiktoken probe to driver port
seonghobae Sep 4, 2026
ec6983e
test(postgres): require centralized orchestrator driver selection
seonghobae Sep 4, 2026
e5bf5d0
fix(postgres): centralize orchestrator driver authority
seonghobae Sep 4, 2026
c70d7f7
test(postgres): migrate orchestrator fakes to driver port
seonghobae Sep 4, 2026
b65f0ed
test(postgres): migrate persistence failures to driver port
seonghobae Sep 4, 2026
c7369ae
test(postgres): inject driver in batch key authority tests
seonghobae Sep 4, 2026
5be8bd5
test(postgres): migrate lifecycle ownership tests to driver port
seonghobae Sep 4, 2026
c554ee6
test(postgres): require bounded keyword conninfo candidate support
seonghobae Sep 4, 2026
d5fdeaa
feat(postgres): parse bounded keyword conninfo for pg8000 candidate
seonghobae Sep 4, 2026
3e5fddc
test(postgres): cover keyword conninfo fail-closed edges
seonghobae Sep 4, 2026
2ffe05a
test(postgres): require injected service-selector resolution
seonghobae Sep 4, 2026
e04bdb0
feat(postgres): resolve service selectors through candidate ACL
seonghobae Sep 4, 2026
fcb4644
test(postgres): require bounded pg_service resolver
seonghobae Sep 4, 2026
ab94a13
feat(postgres): add bounded candidate pg_service resolver
seonghobae Sep 4, 2026
a6b79cb
fix(postgres): accept concrete pathlib service paths
seonghobae Sep 4, 2026
47bb705
fix(ci): accept concrete pathlib paths in license gate
seonghobae Sep 4, 2026
ad87a14
test(ci): cover candidate license CLI path handling
seonghobae Sep 4, 2026
2a39bff
test(ddd): require CLI to use runtime driver owner
seonghobae Sep 4, 2026
da8a190
test(ddd): require Compose to use runtime driver owner
seonghobae Sep 4, 2026
a1e3b87
refactor(ddd): centralize Compose driver selection
seonghobae Sep 4, 2026
f55b33a
refactor(ddd): centralize CLI driver selection
seonghobae Sep 4, 2026
43412f8
test(driver): remove stale checkpoint Psycopg monkeypatches
seonghobae Sep 4, 2026
01919ed
test(driver): bind DB tests to runtime selector seam
seonghobae Sep 4, 2026
5bdcdc1
test(driver): remove stale orchestrator Psycopg seams
seonghobae Sep 4, 2026
a4a84a9
test(driver): remove stale token-counter Psycopg seam
seonghobae Sep 4, 2026
857b092
test(postgres): reject blocking service-file authority
seonghobae Sep 4, 2026
c88a36f
fix(postgres): bound service-file descriptor authority
seonghobae Sep 4, 2026
8a816de
test(postgres): cover service descriptor failures
seonghobae Sep 4, 2026
e33d622
test(postgres): reject service-file metadata drift
seonghobae Sep 4, 2026
5cf3c57
fix(postgres): bind stable service-file snapshot
seonghobae Sep 4, 2026
8cfba1d
test(postgres): cover pg8000 restore catalog parity
seonghobae Sep 4, 2026
d26ef90
test(postgres): prove candidate keyword and service selectors
seonghobae Sep 4, 2026
4804dc1
test(postgres): prove candidate context commit on real PostgreSQL
seonghobae Sep 4, 2026
9ea37ce
test(postgres): prove candidate context rollback on real PostgreSQL
seonghobae Sep 4, 2026
0a804b8
test(postgres): align candidate terminal close contract
seonghobae Sep 5, 2026
c95275f
docs(postgres): complete driver adapter constructor contracts
seonghobae Sep 5, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 72 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -114,3 +114,75 @@ jobs:
run: bash tests/smoke_postgres_container_logging.sh
- name: Run legacy SQL cleanup integration smoke
run: bash tests/smoke_legacy_sql_cleanup.sh
- name: Set up Python 3.14 for candidate parity
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
python-version: "3.14"
- name: Set up uv for candidate parity
uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
with:
prune-cache: true
- name: Install locked project dependencies
run: uv sync --locked
- name: Download exact pg8000 candidate artifact
run: >-
python -m pip download --no-deps --only-binary=:all:
--dest /tmp/pg8000-candidate pg8000==1.31.5
- name: Verify pg8000 candidate artifact digest
run: >-
echo
"0af2c1926b153307639868d2ee5cef6cd3a7d07448e12736989b10e1d491e201 /tmp/pg8000-candidate/pg8000-1.31.5-py3-none-any.whl"
| sha256sum --check --strict
- name: Install candidate only into the CI environment
run: >-
uv pip install --python .venv/bin/python
/tmp/pg8000-candidate/pg8000-1.31.5-py3-none-any.whl
- name: Start candidate PostgreSQL runtime
shell: bash
run: |
candidate_password="$(python -c 'import secrets; print(secrets.token_urlsafe(32))')"
echo "::add-mask::$candidate_password"
password_file="$(mktemp)"
printf '%s' "$candidate_password" > "$password_file"
chmod 600 "$password_file"
container="pg-llm-batch-pg8000-${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT}"
echo "PG_LLM_BATCH_POSTGRES_PASSWORD=$candidate_password" >> "$GITHUB_ENV"
echo "PG8000_CANDIDATE_PASSWORD_FILE=$password_file" >> "$GITHUB_ENV"
echo "PG8000_CANDIDATE_CONTAINER=$container" >> "$GITHUB_ENV"
docker run --detach --name "$container" \
--publish 127.0.0.1:5432:5432 \
--mount "type=bind,source=$password_file,target=/run/secrets/postgres_password,readonly" \
--env POSTGRES_USER=pgllm \
--env POSTGRES_DB=pgllm \
--env POSTGRES_PASSWORD_FILE=/run/secrets/postgres_password \
pg-llm-batch-postgres:ci
- name: Wait for candidate PostgreSQL health contract
shell: bash
run: |
for attempt in $(seq 1 90); do
if docker exec "$PG8000_CANDIDATE_CONTAINER" \
pg_isready -U pgllm -d pgllm >/dev/null 2>&1; then
ready="$(docker exec "$PG8000_CANDIDATE_CONTAINER" \
psql -U pgllm -d pgllm -tAc \
"SELECT bool_and(is_ready) FROM pg_llm_batch_health_check() WHERE component IN ('database','pg_tiktoken','com_config')" \
2>/dev/null || true)"
if [ "$ready" = "t" ]; then
exit 0
fi
fi
sleep 2
done
docker logs "$PG8000_CANDIDATE_CONTAINER"
exit 1
- name: Run real pg8000 candidate PostgreSQL smoke
run: uv run --no-sync python tests/smoke_pg8000_candidate_postgres.py
- name: Tear down candidate PostgreSQL runtime
if: ${{ always() }}
shell: bash
run: |
if [ -n "${PG8000_CANDIDATE_CONTAINER:-}" ]; then
docker rm --force "$PG8000_CANDIDATE_CONTAINER" >/dev/null 2>&1 || true
fi
if [ -n "${PG8000_CANDIDATE_PASSWORD_FILE:-}" ]; then
rm -f "$PG8000_CANDIDATE_PASSWORD_FILE"
fi
33 changes: 26 additions & 7 deletions pg_llm_batch/checkpoint_store.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
validate_tenant_scope,
)
from .exceptions import ConfigError, PgLlmBatchError, ValidationError
from .postgres_driver_port import PostgresDriverPort
from .result_streaming import BatchResultCheckpoint

MIGRATION_PATH = (
Expand Down Expand Up @@ -85,6 +86,23 @@ def _validated_postgres_dsn(value: Any) -> str:
return value


def _connect_postgres(
postgres_dsn: str,
postgres_driver: PostgresDriverPort | None,
) -> Any:
"""Connect through an injected driver while preserving the legacy default.

The optional port lets one bounded persistence consumer migrate away from
Psycopg without changing its SQL, transaction, tenant, or checkpoint
semantics. Until the repository selects and validates a commercial
replacement, omitting the port retains the current Psycopg path explicitly.
"""
if postgres_driver is not None:
return postgres_driver.connect(postgres_dsn)
_require_psycopg()
return psycopg.connect(postgres_dsn)


def _validated_checkpoint(value: Any, field: str) -> BatchResultCheckpoint:
"""Require one immutable checkpoint whose counters fit PostgreSQL storage."""
if not isinstance(value, BatchResultCheckpoint):
Expand Down Expand Up @@ -171,13 +189,14 @@ def _checkpoint_values(checkpoint: BatchResultCheckpoint) -> tuple[Any, ...]:
def apply_result_checkpoint_schema(
postgres_dsn: str,
migration_path: Optional[str] = None,
*,
postgres_driver: PostgresDriverPort | None = None,
) -> None:
"""Apply the idempotent durable result-checkpoint migration."""
dsn = _validated_postgres_dsn(postgres_dsn)
_require_psycopg()
path = Path(migration_path) if migration_path else MIGRATION_PATH
sql = path.read_text(encoding="utf-8")
with psycopg.connect(dsn) as conn:
with _connect_postgres(dsn, postgres_driver) as conn:
with conn.cursor() as cur:
cur.execute(sql)
conn.commit()
Expand All @@ -191,9 +210,11 @@ def __init__(
postgres_dsn: str,
*,
tenant_scope: str = DEFAULT_TENANT_SCOPE,
postgres_driver: PostgresDriverPort | None = None,
) -> None:
"""Bind one explicit database and trusted local tenant scope to the store."""
"""Bind one explicit database, tenant scope, and optional driver port."""
self.postgres_dsn = _validated_postgres_dsn(postgres_dsn)
self._postgres_driver = postgres_driver
try:
self.tenant_scope = validate_tenant_scope(tenant_scope)
except ValidationError as exc:
Expand All @@ -210,8 +231,7 @@ def load(
endpoint_alias: str,
) -> Optional[BatchResultCheckpoint]:
"""Load the current checkpoint in one package-owned transaction."""
_require_psycopg()
with psycopg.connect(self.postgres_dsn) as conn:
with _connect_postgres(self.postgres_dsn, self._postgres_driver) as conn:
with conn.cursor() as cur:
return self.load_in_transaction(
cur,
Expand Down Expand Up @@ -257,8 +277,7 @@ def save(
expected_previous: Optional[BatchResultCheckpoint] = None,
) -> BatchResultCheckpoint:
"""Create or advance a checkpoint in one package-owned transaction."""
_require_psycopg()
with psycopg.connect(self.postgres_dsn) as conn:
with _connect_postgres(self.postgres_dsn, self._postgres_driver) as conn:
with conn.cursor() as cur:
saved = self.save_in_transaction(
cur,
Expand Down
78 changes: 51 additions & 27 deletions pg_llm_batch/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,17 +33,16 @@
import sys
import warnings
from contextlib import ExitStack
from functools import partial
from typing import List, Optional

from psycopg import ProgrammingError
from psycopg.conninfo import conninfo_to_dict

from . import db
from .batch_api_client import BatchAPIClient, config_credentials_provider
from .bootstrap import resolve_dsn, resolve_secret_key
from .config import PostgresConfigStore, SecretStore
from .exceptions import ConfigError, PgLlmBatchError
from .health import check_health, serve_healthz
from .postgres_driver_port import PostgresDriverPort
from .token_counter import TokenCounter

MAX_SECRET_INPUT_CHARACTERS = 65_536
Expand Down Expand Up @@ -73,28 +72,50 @@ def error(self, message: str) -> None:
super().error(redacted_message)


def _validate_cli_dsn(value: str) -> str:
"""Accept valid libpq selectors while refusing credential-bearing argv data."""
def _default_postgres_driver() -> PostgresDriverPort:
"""Load the retained Psycopg adapter only when a CLI parse needs the default."""
from .psycopg_driver_adapter import PsycopgDriverAdapter

return PsycopgDriverAdapter()


def _validate_cli_dsn(
value: str,
*,
postgres_driver: PostgresDriverPort | None = None,
) -> str:
"""Accept valid PostgreSQL selectors without concrete-driver coupling."""
driver = (
postgres_driver
if postgres_driver is not None
else _default_postgres_driver()
)
try:
parameters = conninfo_to_dict(value)
except ProgrammingError:
raise argparse.ArgumentTypeError(
"Postgres DSN must be valid libpq connection information"
) from None
parameters = driver.parse_conninfo(value)
except Exception as exc:
if driver.is_invalid_conninfo(exc):
raise argparse.ArgumentTypeError(
"Postgres DSN must be valid connection information"
) from None
raise
if CLI_DSN_SENSITIVE_PARAMETERS.intersection(parameters):
raise argparse.ArgumentTypeError(
"Credential-bearing Postgres DSNs are not accepted in --dsn; "
"use libpq secret mechanisms outside process argv"
"use PostgreSQL secret mechanisms outside process argv"
)
return value


def _add_common(parser: argparse.ArgumentParser) -> None:
def _add_common(
parser: argparse.ArgumentParser,
*,
postgres_driver: PostgresDriverPort | None = None,
) -> None:
"""Add the shared credential-free ``--dsn`` selector to a subcommand parser."""
parser.add_argument(
"--dsn",
default=None,
type=_validate_cli_dsn,
type=partial(_validate_cli_dsn, postgres_driver=postgres_driver),
help=(
"Credential-free Postgres selector "
"(else PG_LLM_BATCH_DSN bootstrap env var)"
Expand Down Expand Up @@ -186,40 +207,43 @@ def _read_token_input() -> str:
raise ConfigError("Token input must be valid UTF-8") from None


def build_parser() -> argparse.ArgumentParser:
"""Build the command-line parser and all supported subcommands."""
def build_parser(
*,
postgres_driver: PostgresDriverPort | None = None,
) -> argparse.ArgumentParser:
"""Build the command-line parser with an injectable PostgreSQL DSN parser."""
parser = _RedactingArgumentParser(
prog="pg_llm_batch",
description="Standalone Postgres LLM batch engine",
)
sub = parser.add_subparsers(dest="command", required=True)

p_init = sub.add_parser("init-db", help="Apply batch schema (idempotent)")
_add_common(p_init)
_add_common(p_init, postgres_driver=postgres_driver)

p_cfg = sub.add_parser("config", help="Manage KV config and secrets")
cfg_sub = p_cfg.add_subparsers(dest="config_command", required=True)
p_set = cfg_sub.add_parser("set", help="Set a config value")
_add_common(p_set)
_add_common(p_set, postgres_driver=postgres_driver)
p_set.add_argument("category")
p_set.add_argument("key")
p_set.add_argument("value")
p_get = cfg_sub.add_parser("get", help="Get a config value")
_add_common(p_get)
_add_common(p_get, postgres_driver=postgres_driver)
p_get.add_argument("category")
p_get.add_argument("key")
p_secret = cfg_sub.add_parser(
"set-secret",
help="Store a secret from a no-echo prompt or standard input",
)
_add_common(p_secret)
_add_common(p_secret, postgres_driver=postgres_driver)
p_secret.add_argument("secret_key")

p_count = sub.add_parser(
"count-tokens",
help="Count bounded UTF-8 stdin content without exposing it in argv",
)
_add_common(p_count)
_add_common(p_count, postgres_driver=postgres_driver)
p_count.add_argument("--model", required=True)
p_count.add_argument(
"--stdin",
Expand All @@ -229,38 +253,38 @@ def build_parser() -> argparse.ArgumentParser:
)

p_submit = sub.add_parser("submit", help="Upload payload + create batch job")
_add_common(p_submit)
_add_common(p_submit, postgres_driver=postgres_driver)
p_submit.add_argument("--endpoint", required=True, help="Endpoint alias")
p_submit.add_argument("--file-path", required=True, help="memory://<file_id>")
p_submit.add_argument("--batch-endpoint", default="/v1/chat/completions")

p_poll = sub.add_parser("poll", help="Poll a batch job status once")
_add_common(p_poll)
_add_common(p_poll, postgres_driver=postgres_driver)
p_poll.add_argument("--endpoint", required=True)
p_poll.add_argument("--batch-id", required=True)

p_wait = sub.add_parser("wait", help="Wait for a terminal batch status")
_add_common(p_wait)
_add_common(p_wait, postgres_driver=postgres_driver)
p_wait.add_argument("--endpoint", required=True)
p_wait.add_argument("--batch-id", required=True)
p_wait.add_argument("--poll-interval", type=float, default=5.0)
p_wait.add_argument("--timeout", type=float, default=3600.0)

p_retrieve = sub.add_parser("retrieve", help="Download batch results")
_add_common(p_retrieve)
_add_common(p_retrieve, postgres_driver=postgres_driver)
p_retrieve.add_argument("--endpoint", required=True)
p_retrieve.add_argument("--batch-id", required=True)

p_cancel = sub.add_parser("cancel", help="Cancel a provider batch job")
_add_common(p_cancel)
_add_common(p_cancel, postgres_driver=postgres_driver)
p_cancel.add_argument("--endpoint", required=True)
p_cancel.add_argument("--batch-id", required=True)

p_health = sub.add_parser("health", help="Print readiness report")
_add_common(p_health)
_add_common(p_health, postgres_driver=postgres_driver)

p_serve = sub.add_parser("serve-healthz", help="Serve GET /healthz")
_add_common(p_serve)
_add_common(p_serve, postgres_driver=postgres_driver)
p_serve.add_argument("--host", default="127.0.0.1")
p_serve.add_argument("--port", type=int, default=8080)

Expand Down
Loading
Loading