Skip to content

feat: add trusted sqlite qpk-vnext N2 store#259

Closed
Pigbibi wants to merge 1 commit into
mainfrom
codex/qpk-vnext-n2-sqlite-reslice
Closed

feat: add trusted sqlite qpk-vnext N2 store#259
Pigbibi wants to merge 1 commit into
mainfrom
codex/qpk-vnext-n2-sqlite-reslice

Conversation

@Pigbibi

@Pigbibi Pigbibi commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

Fresh QPK-N2 SQLite reslice

Fresh base: origin/main@a06a99f74566ce60ae91fc39317cdfc16558e325; no code from frozen PRs #257/#258 is reused.

N2 is explicitly a trusted single-process local research backend using only stdlib sqlite3:

  • constructor validates explicit absolute DB path with zero filesystem side effects
  • durable operations use fixed rollback journal + synchronous=FULL; ephemeral rejects before open/create
  • schema marker is exactly qpk-vnext/result/v2; no legacy reads
  • complete N1 key primary key, canonical payload BLOB, explicit identity columns/index
  • transaction-scoped insert/idempotent byte compare/conflict no-overwrite
  • exact get and deterministic domain/profile/timing selector listing with per-row N1 decode/key/column validation
  • latest deferred; no caller/orchestrator/network/S3/live integration
  • trusted-root contract explicitly does not claim hostile parent-directory protection

Validation

  • focused: 5 passed
  • full suite: 619 passed, 1 skipped
  • ruff, compileall, diff-check: passed

Review mode: preflight then one concentrated closure maximum; remediation_count=0.

@github-actions

github-actions Bot commented Jul 14, 2026

Copy link
Copy Markdown

🤖 Codex PR Review

🚫 Merge blocked: 1 serious issue(s) found in high-risk files

🚫 Blocking Issues

These issues must be fixed before this PR can be merged:

1. 🟠 [HIGH] Bug in src/quant_platform_kit/strategy_lifecycle/qpk_vnext_n2_store.py

Existing databases are reopened through a raw SQLite URI built with f"file:{self.db_path}?mode=rw". Absolute POSIX paths containing URI-significant characters such as ?, #, or % are not escaped, so a valid configured database path can be misparsed on subsequent reads/writes, causing failures or opening the wrong target file. (line 87)

Suggestion: Do not interpolate filesystem paths directly into a SQLite URI. Either reopen with sqlite3.connect(str(self.db_path), ...) and avoid URI mode here, or build the URI from a properly escaped path such as self.db_path.as_uri()/URL quoting before appending mode=rw.

ℹ️ Other Findings

1. 🟡 [MEDIUM] Reliability in src/quant_platform_kit/strategy_lifecycle/qpk_vnext_n2_store.py

get() and list_keys() assume payload is bytes-like and call bytes(row[1]). SQLite does not enforce BLOB affinity, so a corrupted or manually edited row can store TEXT in payload; in that case bytes(str_value) raises TypeError, which is not caught here. Instead of failing closed with StoreError, callers get an unexpected raw exception. (line 138)

Suggestion: Validate that payload is bytes-like before decoding, or catch TypeError alongside the existing decode exceptions in both read paths so malformed rows are sanitized into StoreError.

2. 🟡 [MEDIUM] Reliability in src/quant_platform_kit/strategy_lifecycle/qpk_vnext_n2_store.py

When _configure() rejects an opened database by calling _fail() (for example, wrong namespace marker or missing required tables), _connect() lets that StoreError escape without closing the already-open sqlite3.Connection. Repeated probes against unrelated/corrupt databases will leak file descriptors and can retain locks until garbage collection. (line 91)

Suggestion: Ensure _connect() closes conn on every failure path, including StoreError raised from _configure(), for example by wrapping the configure step in a nested try/except that closes the connection before re-raising a sanitized error.


Review by Codex PR Review bot • PR

Co-Authored-By: Codex <noreply@openai.com>
@Pigbibi Pigbibi force-pushed the codex/qpk-vnext-n2-sqlite-reslice branch from f6cb211 to 1421edd Compare July 14, 2026 20:51
@Pigbibi

Pigbibi commented Jul 14, 2026

Copy link
Copy Markdown
Contributor Author

Concentrated closure (head 1421edd) separates creation from validation: read paths use existing read-write mode and reject missing/empty/unmarked/unrelated DBs before schema writes; only a new empty file may initialize the exact namespace schema. Added missing/unrelated DB no-mutation regressions and repeated concurrency preflight.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f6cb211218

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +89 to +91
if bytes(row[0]) == payload:
conn.execute("COMMIT")
return "idempotent"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Validate existing rows before returning idempotent

If an existing row has the same key and canonical payload but stale selector columns (for example after local DB corruption or a partial/manual migration), this branch commits and returns idempotent without running _validate_columns. Because list_keys filters by domain/profile/timing before it decodes rows, that “successful” retry leaves the result hidden under its real selector and only fails if queried through the corrupted selector, so the store no longer fails closed on identity-column corruption.

Useful? React with 👍 / 👎.

Comment on lines +77 to +78
with pytest.raises(StoreError):
SqliteResultStore(db).put(contract())

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Make the permission test safe for root runners

In environments where pytest runs as root (including many Docker CI jobs), chmod(0o500) does not stop root from creating results.sqlite, so SqliteResultStore(db).put(contract()) succeeds and this pytest.raises assertion fails; I reproduced this with PYTHONPATH=src python -m pytest tests/test_qpk_vnext_n2_store.py -q in the provided container. Skip/adjust this case for root or assert permissions with a mechanism that is effective for the runner.

Useful? React with 👍 / 👎.

Comment on lines +58 to +59
conn.execute("INSERT OR IGNORE INTO store_meta(key, value) VALUES (?, ?)",
("namespace", NAMESPACE))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reject unmarked existing databases instead of stamping them

When db_path accidentally points at an existing SQLite database without the qpk-vnext namespace row, this INSERT OR IGNORE path creates the marker (after also creating tables) and subsequent calls use that file as an N2 store instead of failing as an incompatible/unmarked store. That violates the exact schema-marker/no-legacy-read contract and can mutate an unrelated local database before the caller notices the wrong path.

Useful? React with 👍 / 👎.

"FROM results WHERE key=?", (key,)).fetchone()
if row is None:
_fail()
item = decode_wire(json.loads(bytes(row[1]).decode("utf-8")))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Sanitize non-BLOB payload corruption

If a local/corrupted database stores payload as TEXT (SQLite permits this even for a BLOB column), bytes(row[1]) raises TypeError, which is not caught here, so exact reads leak an unexpected exception instead of the documented sanitized StoreError fail-closed behavior; the selector decode path has the same issue for any selected corrupted row.

Useful? React with 👍 / 👎.

Comment on lines +128 to +131
domain, profile = _segment(domain), _segment(profile)
except ContractError:
_fail()
if timing not in {"next_open", "next_close"}:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Validate selectors with the full N1 rules

list_keys accepts invalid selectors that ResultContract would reject: for example profile='soxl' passes _segment and silently returns an empty result even though N1 requires uppercase profiles, while an unhashable timing such as [] raises TypeError instead of StoreError. These cases can make case/config mistakes look like missing experiment output or break the store's sanitized fail-closed API.

Useful? React with 👍 / 👎.

@Pigbibi

Pigbibi commented Jul 14, 2026

Copy link
Copy Markdown
Contributor Author

PERMANENT_FREEZE: closure exhausted; URI/path lifecycle and payload/connection hardening indicate scope must narrow before successor reslice. Frozen evidence retained.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant