Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
a434444
feat: require owned request intent before Python execution (#43)
Oct 1, 2026
7e9b593
fix(trace): retain Python canonical SQL origins and descendant log pr…
Oct 2, 2026
b56c8c0
fix(trace): isolate graph lineage and defer audits until commit (#43)
Oct 2, 2026
a2c5edf
fix(trace): isolate Python graph ownership and reject mixed versions …
Oct 2, 2026
de95831
fix(trace): own Python page and stream intent across deferred executi…
Oct 2, 2026
8cbebd9
fix(trace): retain Python mutation readbacks and canonical query orig…
Oct 3, 2026
a58fd87
fix(trace): own whole-mutation intent privacy and loaded scalar snaps…
Oct 3, 2026
e38b218
fix(trace): preserve loaded delete and unchanged scalar privacy (#43)
Oct 3, 2026
1e41efa
fix(trace): preserve Python aggregate membership and reject incomplet…
Oct 3, 2026
1bb80ff
fix: retain generated aggregate projections outside mutation ledger (…
Oct 3, 2026
b34585a
fix(trace): preserve Python membership across filtered reference load…
Oct 3, 2026
9670008
test: assert provider request intent diagnostics in both log modes (#43)
Oct 3, 2026
ec99c25
test: prove live query trace isolation through SQLite statements (#43)
Oct 3, 2026
736d22d
test: verify generated bootstrap trace and committed visibility (#43)
Oct 3, 2026
fd46068
fix: preserve nested and loaded relation facet trace (#43)
Oct 3, 2026
fe291e8
fix: preserve empty facet membership for absent parent keys (#43)
Oct 3, 2026
4518e59
fix: scope nested Facet membership before execution (#43)
Oct 4, 2026
8465f77
test: gate Python request intent before policy and SQLite IO (#43)
Oct 4, 2026
e459548
fix: retain exact LIKE operands for invocation privacy (#43)
Oct 4, 2026
4da53af
test: exercise overlapping generated Checker save outcomes (#43)
Oct 4, 2026
94e8697
test: verify allocated child identities at generated save sinks (#43)
Oct 4, 2026
79e6360
fix: retain grouped window clauses and numeric trace evidence (#43)
Oct 4, 2026
be8d5b2
fix: retain native batch root lineage on each executed item (#43)
Oct 4, 2026
09e8853
test: observe native ledger precedence at execution boundaries (#43)
Oct 4, 2026
2c578d3
fix: preserve known FK when forward details are filtered (#43)
Oct 4, 2026
22369be
test: retain generated Facet trace acceptance in runtime examples (#43)
Oct 4, 2026
65c57c7
test: include read-only generated School fixture in Facet example (#43)
Oct 4, 2026
21181e7
test: assert canonical generated three-relation SQL trace paths
Oct 4, 2026
339d8f3
test: observe shared Context storage during live query graphs
Oct 4, 2026
85b4372
test: verify typed graph identities at Python mutation boundaries
Oct 4, 2026
a5de2fc
fix: align graph local reasons with Unicode intent whitespace
Oct 4, 2026
2c18292
test: observe generated bootstrap intent with logging enabled and dis…
Oct 5, 2026
a40690a
test: verify generated Python readback request intent
Oct 5, 2026
982eeb4
test: require complete private lineage at Python generated save bound…
Oct 5, 2026
7b80c15
test: inspect generated Facet raw paths with logging disabled
Oct 5, 2026
d9bf873
test: observe generated aggregate paths with logs disabled and filter…
Oct 5, 2026
6bb54c6
fix: stabilize grouped relation limits by group keys (#43)
Oct 6, 2026
6efb8fd
test: reject wrong-kind nodes at generated graph lineage boundaries (…
Oct 6, 2026
4167e1d
ci: install ripgrep for complete example verification
Oct 6, 2026
f9d7032
ci: install native Python example test dependencies
Oct 6, 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
4 changes: 3 additions & 1 deletion .github/workflows/example-gate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,11 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install example verification tools
run: sudo apt-get update && sudo apt-get install -y ripgrep
- uses: actions/setup-python@v5
with:
python-version: '3.10'
cache: pip
- run: pip install -e . aiosqlite
- run: pip install -e . aiosqlite pytest pytest-asyncio
- run: ./scripts/verify-examples.sh
81 changes: 78 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ configure those separately. This setting does not erase older plaintext files.
Restrict access and retention when using plaintext diagnostics, then unset the
variable and restart processes when troubleshooting is complete.

TeaQL Python SDK is a runtime engine and toolkit for building data-driven business applications. It provides seamless integration with the TeaQL ecosystem, fully aligned with the `teaql-rs` baseline.
TeaQL Python SDK is a runtime engine and toolkit for building data-driven business applications. It uses `teaql-rs` as the cross-language design baseline; verified coverage and remaining gaps are tracked in [conformance](https://github.com/teaql/teaql-conformance).

## Recommended Agent Harness

Expand All @@ -41,8 +41,8 @@ and evidence-based verification as the generator and runtimes evolve.

## 2. Tests Performed

After rigorous AST semantic analysis and manual verification, this SDK has successfully passed the following tests:
* ✅ **100% API Signature and Logic Parity**: Scanned with Tree-Sitter and implemented all internal methods and logic to match the Rust baseline.
The SDK includes tests in the following areas. These suites do not establish complete Rust parity or acceptance of every external provider:
* **Cross-language contracts**: Shared fixtures and integration tests verify individual portable behaviors; unresolved coverage stays explicit in conformance.
* ✅ **`teaql.core` Core Tests**: Extensively tested attribute extraction, safe nullability checks, and relationship building for `Value`, `GraphNode`, `Entity`, `Mutation`, `Query`, `Expr`, and `SafeExpression`.
* ✅ **`teaql.runtime` Runtime Tests**: Verified the context propagation of `UserContext` and the complete lifecycle hooking for `record_sql_log` and `record_metadata_log`.
* ✅ **`teaql.sql` / `teaql.data_service` Tests**: Tested the SQL AST compilation engine and the underlying command dispatch mechanism.
Expand Down Expand Up @@ -92,6 +92,81 @@ orders = await client.execute_query(FederalQuery(
await client.aclose()
```

## Trace Chain Local Source Status

Query and Mutation Requests own their required non-blank intent independently
of logging. Physical SQL paths use the Rust canonical algorithm, with twelve
byte-identical frozen vectors, owned query origins and qualified relation frames.
Native SQLite tests observe three real relation levels without inserting expected
frames, including a deepest-query failure. A successful write and a failed
authoritative readback keep separate canonical paths and statement outcomes;
the failed transaction rolls back.

Parent query diagnostics include declared descendant binding provenance before
safe projection. The existing mask algorithm, expanded SQL and execution values
are unchanged; provenance is not stored on Context or exposed in log entries.
This additional compilation has not been performance benchmarked.

Graph saves now use an explicit invocation-owned `GraphMutationSession` and an
immutable persistent parent scope. The original Context's data service is never
swapped; child saves receive the session and their parent scope explicitly.
Unannotated children inherit, local reasons append once, and deletion creates its
scope before execution. The typed ledger can store an owned complete replacement
chain. Five shared graph fixtures verify fifteen per-entity expectations; these
helper tests are separate from generated traversal evidence.

[The generated Trace Chain example](examples/trace-chain/) drives six mutations
through actual Q/E/save APIs and SQLite, observing request lineage, SQL metadata
and safe audits after commit. It also verifies three query relation levels,
concurrent independent saves in one Context, write/readback failure rollback,
and missing root intent before transaction access. Its verifier executes twice
on one SQLite file without cleanup and checks all generated-library hashes.
All ten example groups and the source suite pass twice locally (424 tests and
eight explicit external-provider skips per source run).

SQL transactions queue owned audit snapshots until commit, including explicit
transactions and automatic batches. Rollback discards them. A completion failure
is reported with `GraphCommittedError.committed == True`; all remaining audit
deliveries and graph cleanup callbacks are still attempted, with no postcommit
rollback. Reentrant independent root saves fail instead of silently joining.
The private generated save implementation has changed; old generated libraries
must be regenerated, while public `audit_as(...).save(context)` stays unchanged.

The aggregate follow-up adds runtime-owned query projection snapshots and generated
`query_projection(alias)` / `has_query_projection(alias)` accessors. Missing aliases
raise `KeyError`; zero and null are present values. Projections are owned snapshots,
not writable model fields, live calculations or part of a mutation payload. The
generated example covers root/nested counts, original Trace Chain ancestry,
masked descendant intent, logging on/off, provider failure recovery and saving
only a modeled field. An alias named `save` cannot replace the save method.
Regenerate libraries to acquire this projection API; related aggregate streaming
remains unsupported and rejects before provider I/O.

Relation attachment retains original scalar keys in invocation-local runtime
state. It does not infer membership from hydrated objects: a forward reference
filtered to `None` must not remove its child from a separately loaded parent
list, and a later sibling can still load using the same FK. Keys are not added
to returned records or mutation payloads. Native SQLite cases exercise these
boundaries with related counts, nested ancestry and logging both on and off.

The reusable [generated Facet example](examples/facet-trace/) exercises 20
root/nested/loaded-relation scenarios twice on one retained SQLite database.
It checks actual counts and returned metadata, original root/ancestor routes,
future-binding masking, logging on/off, and independent next-query state. The
all-examples gate now includes it and fingerprints the generated library.

This is local-source/generated-consumer evidence, not complete Trace Chain.
Prepared-batch grouping, generated ledger overrides, complete entry-point and
inherited mutation privacy coverage, successful readback diagnostic alignment,
live PostgreSQL/MySQL graph acceptance, and immutable internal Registry replay
remain open. No released-package parity is claimed.

```bash
PYTHONDONTWRITEBYTECODE=1 PYTHONPATH=src python -m pytest -q
PYTHONDONTWRITEBYTECODE=1 bash scripts/verify-examples.sh
# Repeat both commands without source changes.
```

## Security Foundation Status

Python currently provides governed local SQL execution and a **TFP client**;
Expand Down
1 change: 1 addition & 0 deletions examples/conformance/Q.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,4 @@ def work_items() -> WorkItemRequest:
def work_items_minimal() -> WorkItemRequest:
return WorkItemRequest(minimal=True)


24 changes: 12 additions & 12 deletions examples/conformance/app/masking_lifecycle.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,19 +46,19 @@ def capture(entry):
entries.append(entry)
sink.write(entry)
context.set_diagnostic_sql_log_sink(SimpleNamespace(write=capture))
async def insert(entity_id, target=None):
async def insert(entity_id, target=None, request_context=None):
command = (InsertCommand('MaskCustomer').value('id', entity_id).value('version', 1)
.value('display_name', 'Riverside'))
command.trace_chain = [TraceNode(comment='what: seed Riverside lifecycle fixture')]
return await (target or service).mutate(context, MutationRequest(command))
return await (target or service).mutate(request_context or context, MutationRequest(command, comment='what: seed Riverside lifecycle fixture'))
try:
for entity_id in [1, 2, 3]:
await insert(entity_id)
entries.clear()
output.clear()
request = QueryRequest(SelectQuery('MaskCustomer')
.filter(Expr.eq('display_name', 'Riverside')).limit(3)
).comment('what: inspect customers').purpose('why: verify stream lifecycle')
, _comment='what: runtime regression fixture', _purpose='why: verify runtime behavior').comment('what: inspect customers').purpose('why: verify stream lifecycle')
# async-for break alone does not promise immediate generator close
# in Python. aclosing makes ownership explicit and deterministic.
async with aclosing(service.query_stream(context, request, 1)) as stream:
Expand Down Expand Up @@ -100,17 +100,17 @@ async def insert(entity_id, target=None):

context.insert_resource('dataService', service)
entries.clear()
async def partial_graph():
target = context.require_resource('dataService')
await insert(30, target)
await insert(777, target)
await insert(31, target)
async def partial_graph(graph):
await insert(30, graph.transaction, graph.context)
await insert(777, graph.transaction, graph.context)
await insert(31, graph.transaction, graph.context)
try:
await context.execute_graph_save(partial_graph)
await context.execute_graph_save(partial_graph, comment='what: runtime regression fixture')
raise AssertionError('partial graph unexpectedly committed')
except TransportError:
pass
assert [entry.execution_outcome for entry in entries] == ['success','success','success']
assert [entry.execution_outcome for entry in entries] == ['success','success','success','success']
assert [entry.trace_path[-1].name for entry in entries] == ['insert','select','insert','select']
assert entries[-1].result_count == 0
assert not await service.transport.fetch_all_sql(CompiledQuery(
'SELECT id FROM mask_customer_data WHERE id IN (30,31,777)', []))
Expand All @@ -120,11 +120,11 @@ async def partial_graph():

command = (InsertCommand('MaskChild').value('id', 1).value('version', 1).value('parent_id', 1))
command.trace_chain = [TraceNode(comment='what: seed child for relation verification')]
await service.mutate(context, MutationRequest(command))
await service.mutate(context, MutationRequest(command, comment='what: runtime regression fixture'))
graph_request = QueryRequest(SelectQuery('MaskCustomer').project('id')
.filter(Expr.eq('id', 1)).and_filter(Expr.eq('display_name', 'Riverside'))
.relation_query('children', SelectQuery('MaskChild').project('id').limit(2)).limit(1)
).comment('what: load Riverside graph').purpose('why: verify inherited relation masking')
, _comment='what: runtime regression fixture', _purpose='why: verify runtime behavior').comment('what: load Riverside graph').purpose('why: verify inherited relation masking')
entries.clear()
await service.transport.execute_sql(CompiledQuery(
'ALTER TABLE mask_child_data RENAME TO mask_child_unavailable', []))
Expand Down
Loading
Loading