New Features Round 1, 2 & 3#8
Open
therecluse26 wants to merge 24 commits into
Open
Conversation
- Add per-variant ErrorEnvelope snapshot tests for every PlenumError variant (CapabilityViolation, ConnectionFailed, QueryFailed, InvalidInput, EngineError, ConfigError) so the agent-facing JSON wire format is pinned and any future code/message drift requires explicit review. - Add multi-statement rejection tests through the public validate_query() API for all three engines (Postgres, MySQL, SQLite) plus a SELECT-then-DDL payload. Multi-statement preprocessing is engine-agnostic today; the parameterized tests will catch any future engine-specific bypass. - Add a CI live-db-tests job that brings up Postgres 16 and MySQL 8 service containers and runs the 14 #[ignore]'d engine integration tests with --ignored. Kept as a separate job so a transient service outage doesn't poison the main test signal. Items 4 (password_env tests) and 5 (panic handler test) from REF-40's plan are already explicitly inside REF-36's and REF-37's own acceptance criteria and need no separate child issues. Item 6 (MCP stdio blackbox) is already covered: tests/mcp_protocol.rs spawns the compiled plenum binary, which dispatches to plenum::mcp::serve() in src/main.rs — the actual MCP entry. Co-Authored-By: Paperclip <noreply@paperclip.ing>
Adds four targeted tests to tests/edge_cases.rs covering audit checklist items that were previously untested: - test_numeric_affinity_promotes_real_to_integer: SQLite NUMERIC affinity stores 3.0 as INTEGER 3; verify JSON emits an integer, not a float. - test_real_nan_and_infinity_become_null: divide-by-zero paths must not panic and must produce valid JSON. - test_blob_with_valid_utf8_bytes_is_base64_encoded: a BLOB containing the UTF-8 bytes of "Hello" must still be base64-encoded, distinguishable from a TEXT column with the same string. Guards against the MySQL-style bug tracked in REF-46. - test_dynamic_typing_all_storage_classes: smoke for every SQLite storage class (INTEGER/REAL/TEXT/BLOB/NULL) in one dynamic column. Adds a create_test_db_named() helper to avoid the nanosecond timestamp collisions observed in the shared create_test_db() under parallel test execution. Refs REF-39. Engine-level bugs tracked in REF-45 (Postgres), REF-46 (MySQL), and REF-47 (output marker). Co-Authored-By: Paperclip <noreply@paperclip.ing>
CLI accepted --password-env but dropped it on the floor: non_interactive_connect ignored the value with a TODO, and save_connection hardcoded password_env: None when constructing StoredConnection. The flag silently no-op'd despite the StoredConnection::resolve() machinery already being in place. Changes: - non_interactive_connect honors --password-env: validates the env var resolves at connect time (clear error if unset/empty), rejects --password and --password-env together, rejects --password-env for sqlite, and propagates the env var name to the caller. - save_connection takes password_env: Option<String> and persists it on the StoredConnection so resolve_connection() can read it back. - Unit tests for password_env JSON round-trip, omission when None, and env-var precedence over inline password on resolve. - CLI integration test exercises end-to-end success, missing-var error, and mutual-exclusion error against the built binary; asserts no secret ever lands in stdout or the persisted config. Co-Authored-By: Paperclip <noreply@paperclip.ing>
…-41) The prior `is_read_only_*` allowlist accepted any query starting with `WITH ` on the assumption that CTEs are read-only. PostgreSQL writable CTEs (`WITH x AS (INSERT/UPDATE/DELETE ... RETURNING ...) SELECT ...`) and MySQL/SQLite trailing-DML forms (`WITH cte AS (SELECT ...) INSERT/UPDATE/DELETE ...`) both bypass this prefix check while still executing writes server-side. Replace the bare `sql.starts_with(\"WITH \")` admission with `is_safe_cte_query`, which: 1. Requires the `WITH ` prefix, and 2. Scans the (already comment-stripped, uppercased) body with `scan_for_write_keyword` for any DML/DDL token (INSERT, UPDATE, DELETE, MERGE, REPLACE, COPY, TRUNCATE, DROP, ALTER, CREATE, GRANT, REVOKE, RENAME, ATTACH, DETACH, LOAD, VACUUM, REINDEX, LOCK, UNLOCK, CALL, INTO), respecting `'...'`, `\"...\"`, and `` `...` `` quoted regions so that keywords inside string literals or quoted identifiers do not cause false positives. Coverage: - Postgres writable CTE INSERT/UPDATE/DELETE/COPY: rejected - MySQL/SQLite trailing-DML after CTE: rejected - EXPLAIN-prefixed CTE-DML: rejected (via existing EXPLAIN strip) - `WITH cte AS (SELECT ...) SELECT * FROM cte`: still allowed - Recursive read-only CTEs: still allowed - CTEs with DML keywords inside string literals / quoted identifiers / identifier substrings (e.g. `delete_at`): still allowed 15 new tests added covering all acceptance criteria plus regression guards for the false-positive cases. Co-Authored-By: Paperclip <noreply@paperclip.ing>
The previous `is_read_only_sqlite` accepted any query starting with
`PRAGMA `, which admitted destructive PRAGMAs:
- PRAGMA writable_schema = 1 (enables sqlite_master modification — the
most dangerous bypass)
- PRAGMA wal_checkpoint[(FULL|RESTART|TRUNCATE)]
- PRAGMA optimize / incremental_vacuum
- PRAGMA page_size = 4096 / auto_vacuum = FULL (structural mutation)
- PRAGMA journal_mode = WAL (settable PRAGMAs in assignment form)
Replace the blanket allow with `is_safe_pragma`, a two-allowlist gate:
1. READ_ONLY_SQLITE_PRAGMAS_WITH_ARGS — names whose `PRAGMA name(arg)`
form is a pure read (table_info, index_list, foreign_key_list, …).
2. READ_ONLY_SQLITE_PRAGMAS_BARE — names whose bare `PRAGMA name` form
is read-only. Settable PRAGMAs (journal_mode, user_version, …) only
appear here, so their `= value` and `(value)` setter forms are
rejected.
Any query containing `=` is rejected unconditionally (the assignment
setter form). Unknown PRAGMAs fail closed, so future SQLite versions
that introduce new write PRAGMAs are rejected until the allowlist is
explicitly updated.
Defence-in-depth: connections still open with SQLITE_OPEN_READ_ONLY in
src/engine/sqlite/mod.rs, but the query-layer check gives clearer
errors before the driver call and prevents PRAGMAs (like
writable_schema) that are accepted on read-only connections.
This commit also lands the previously-uncommitted dirty state from the
parent REF-21 / REF-35 read-only enforcement audit work that the same
engineer agent left in the working tree (REF-41 CTE write-keyword scan,
REF-43 SELECT INTO OUTFILE/DUMPFILE/@var block), so the audit fixes
are now on the branch atomically.
Tests: 26 new SQLite PRAGMA tests covering every acceptance criterion:
writable_schema (= 1, = ON, = 0, paren-form, bare), wal_checkpoint
(bare, FULL, RESTART, TRUNCATE), optimize, incremental_vacuum,
journal_mode (bare allowed, = WAL rejected, (WAL) rejected),
user_version (bare allowed, = N rejected), page_size = 4096,
auto_vacuum = FULL, unknown-name fail-closed, empty PRAGMA, malformed
trailing token; plus regression tests that table_info(users),
database_list, table_list, index_list(users), foreign_key_list(users),
integrity_check, compile_options, schema_version still pass.
cargo test: 201/201 passing.
cargo build --release: clean.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
…es (REF-261) Defense-in-depth layer: the database itself now enforces read-only mode on every execute() call, independent of the SQL parser. - SQLite: open_connection(read_only=true) → SQLITE_OPEN_READ_ONLY at OS/VFS level; adds unit test proving DML and DDL fail on a raw connection bypassing the parser - PostgreSQL: SET default_transaction_read_only = ON immediately after connect; adds ignored integration test verifying the session flag and write rejection - MySQL: SET SESSION TRANSACTION READ ONLY immediately after connect; adds ignored integration test verifying transaction_read_only=1 and DML rejection (MySQL DDL is non-transactional and remains covered exclusively by the parser) All existing tests pass; cargo build --release clean. Co-Authored-By: Paperclip <noreply@paperclip.ing>
Extend plenum introspect with two new metadata fields across all three engines: - `ColumnInfo.comment`: column-level comment/description - `TableInfo.comment`: table-level comment/description - `TableInfo.row_estimate`: engine statistics row count estimate Both fields always serialize (explicit null, never omitted) per spec. Engine behaviour: - PostgreSQL: column comments via pg_description JOIN; table comment via obj_description(pg_class); row_estimate from reltuples (null when -1, meaning never analyzed) - MySQL: column_comment from information_schema.columns; table_comment + table_rows from information_schema.tables; empty strings normalised to null - SQLite: comment always null (no native comment storage); row_estimate from sqlite_stat1.stat after ANALYZE, null otherwise Tests: - SQLite: two inline unit tests covering null output before ANALYZE and non-null row_estimate after ANALYZE - MySQL/PostgreSQL: ignored integration tests exercising full round-trip with COMMENT ON / CREATE TABLE COMMENT metadata Also fixes pre-existing compilation errors introduced by the linter: - Capabilities.offset field added to struct literals in mcp.rs and tests - mcp.rs execute calls updated with empty params slice (&[]) - json_to_pg_value return type adds Send to unblock the Postgres execute future All 218 tests pass; cargo build --release clean. Co-Authored-By: Paperclip <noreply@paperclip.ing>
… (REF-268)
- plenum connect --list emits { connections:[...], default:"name" }
- Connections sorted alphabetically for deterministic output
- Secrets redacted: password field never emitted; password_env shows var name only
- Empty/missing project config returns empty list, not an error
- --project-path honored for cross-project listing
- Added list_connections_raw() to config module (raw StoredConnection, no resolve)
- Fixed pre-existing build failures: Deserialize removed from output-only envelope
types (incompatible with &'static str field), contract_version snapshots updated,
max_bytes/truncated_by fields added to test Capabilities/QueryResult initializers
Co-Authored-By: Paperclip <noreply@paperclip.ing>
…F-269) - Add max_bytes: Option<usize> to Capabilities struct - Add apply_byte_budget() that trims rows at row boundaries when the cumulative serialized size would exceed the budget - Add rows_truncated serialization (skip_serializing_if = false) and truncated_by: Option<String> to QueryResult so MCP callers see both flags directly in the response body - Add truncated_by: Option<String> to Metadata for the CLI envelope's meta section; set to "bytes" when max_bytes fires, omitted otherwise - Wire --max-bytes CLI arg into handle_query; wire max_bytes JSON param into MCP tool_query; both apply apply_byte_budget post-engine - Add unit tests in engine::tests and integration tests in tests/output_validation.rs covering: truncation, no-truncation, zero budget, metadata signalling, and per-engine SQLite path All 176 lib + 27 integration tests pass; cargo build --release clean. Co-Authored-By: Paperclip <noreply@paperclip.ing>
- Add 5 integration tests in tests/cli_connect.rs covering: - Reachable SQLite connection returns ConnectionInfo envelope - Unreachable/missing file returns CONNECTION_FAILED (no credential leakage) - --test with --name uses a saved connection by name - --test does not write config to disk - --test and --save are mutually exclusive (clap-level rejection) - Add rusqlite to dev-dependencies so tests can create valid SQLite fixtures - All 8 cli_connect tests pass; all 265+ tests pass; cargo build --release clean Co-Authored-By: Paperclip <noreply@paperclip.ing>
…s (REF-265) - Add `JsonSchema` derive to all output types (ErrorEnvelope, SuccessEnvelope, Metadata, ConnectionInfo, QueryResult, IntrospectResult and sub-types) - Add `generate-schemas` binary (cargo run --bin generate-schemas) to regenerate schemas/error_envelope.json, connect_success.json, introspect_success.json, and query_success.json from live Rust types via schemars - Add drift test in tests/schema_drift.rs that fails CI when types change without a schema regen - Export IntrospectResult, IndexSummary, ViewInfo from crate root - Fix rows_truncated to carry #[serde(default)] so the schema marks it optional - Remove stale hand-written success_envelope.json (superseded by command-specific schemas) - Document schemas/ in README with table, regen instructions, and drift test note Co-Authored-By: Paperclip <noreply@paperclip.ing>
…(REF-272) - Add src/dsn.rs: per-engine DSN/URL parsing (postgres://, postgresql://, mysql://, sqlite:) - Engine inferred from scheme; invalid schemes fail fast with structured error - Credentials redacted in all error output via redact_dsn() - --dsn is mutually exclusive with --name and explicit connection flags; never mutates stored config - Wire --dsn into CLI (query, introspect) and MCP tool schemas (dsn field) - Fix pre-existing duplicate field errors in config/mod.rs test fixtures - 27 new unit tests: all parsers, redaction, scheme errors, edge cases - cargo test --no-default-features --features postgres,sqlite: 215 passed, 1 pre-existing keychain failure Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ces (REF-271) - StoredConnection gains password_command (shell command) and keychain_entry (OS keychain via keyring crate) fields; exactly one indirect source per connection is enforced in resolve() - password_command runs via sh -c; non-zero exit or empty stdout fails fast with no secret in the error message - keychain_entry looks up via keyring::Entry; runtime uses the real OS keychain (macOS/Windows/Linux Secret Service) - Test isolation: cfg(test) thread-local MOCK_KEYCHAIN replaces keyring's EntryOnly mock (which shares no state across Entry::new calls) so keychain tests are deterministic and require no external services - save_connection signature extended with new optional fields; all call sites in main.rs updated; KeychainEntry re-exported from lib.rs Co-Authored-By: Paperclip <noreply@paperclip.ing>
CI Rustfmt and Clippy (-D warnings) were failing. Apply rustfmt and resolve the pedantic/style lints: - doc_markdown: backtick identifiers in doc comments (regenerates schemas/, whose descriptions are derived from Rust doc comments via schemars) - uninlined_format_args, manual_string_new, redundant_clone, approx_constant, needless_debug_formatting: mechanical fixes - trivially_copy_pass_by_ref: allow on is_false (serde skip_serializing_if requires fn(&T) -> bool) - type_complexity: extract RawConnectionListing alias - similar_names / needless_pass_by_value: rename bindings; take ssl_mode as Option<&str> in build_tls_config - update stale benches/query.rs to the current execute(config, query, params, caps) signature No behavior change; contract_version and schema structure unchanged. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The new live-db-tests CI job (running the #[ignore]d Postgres+MySQL integration tests against service containers) was failing. Root causes and fixes: Real code bugs: - MySQL wildcard NULL panic: DATABASE() returns SQL NULL in wildcard mode; get::<String>() panics converting NULL (mysql Row::get returns None only for out-of-range indices, not NULL values). Read as Option<Option<String>> in validate_connection and determine_target_schema. Fixes the wildcard tests. - Timeout backstop race (Postgres + MySQL): the client-side tokio backstop used the same duration as the server-side statement_timeout / MAX_EXECUTION_TIME, so the client guard could win the race and surface QUERY_FAILED instead of QUERY_TIMEOUT. Add a fixed grace (CLIENT_TIMEOUT_BACKSTOP_GRACE) so the server cancels first; the client guard now only fires if the server never responds. Test corrections: - MySQL REF-263 test used an empty root password; the mysql:8.0 container requires MYSQL_ROOT_PASSWORD. Use 'password'. - MySQL timeout test used SELECT SLEEP(10), which MAX_EXECUTION_TIME interrupts but still returns success (SLEEP returns 1) -> no error. Use a heavy information_schema cartesian join, which is cancelled with error 3024. - MySQL verify-full TLS test could never pass against the container's auto-generated self-signed certs (unknown CA + CN != localhost). Retarget to sslmode=require, which establishes TLS accepting the self-signed cert. - Postgres capability tests written against a removed write-capability model: deleted the obsolete *_with_capability tests (they asserted writes succeed, contradicting the strictly read-only spec) and updated *_without_capability assertions to the actual read-only rejection message. - Postgres fixture setup (introspect/max_rows/REF-263) ran DDL/DML through the now read-only engine and silently failed. Seed fixtures via a raw writable client (setup_client helper), mirroring the MySQL raw-Conn pattern. Verified locally against postgres:16 and mysql:8.0 containers: 20 passed, 0 failed. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
native-tls (pulled in only by the postgres feature) linked against the system OpenSSL, so cargo build --release panicked in openssl-sys when OPENSSL_LIB_DIR pointed at an invalid path (e.g. a Nix -bin output with no lib/ dir). Enable native-tls's 'vendored' feature to compile OpenSSL from source, removing the dependency on system OpenSSL and the fragile OPENSSL_LIB_DIR/OPENSSL_DIR env vars. Produces portable binaries. Verified: cargo build --release succeeds with a deliberately-broken OPENSSL_LIB_DIR still set; cargo test --lib -> 225 passed, 0 failed. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…y; timeout-as-error (REF-258) Four MySQL bugs surfaced by live testing against MySQL 8.0, all rooted in the binary prepared-statement protocol and statement classification: - Bug 1: transaction-control statements (BEGIN/START TRANSACTION) failed with error 1295 under the prepared protocol. execute_query now routes unparameterized statements through the text protocol (query/query_iter) and only uses the prepared protocol when bound params are present. - Bug 2: EXPLAIN was classified as a non-row statement, silently dropping its plan rows. EXPLAIN is now treated as row-returning. - Bug 3: EXPLAIN variants (FORMAT=..., ANALYZE, EXTENDED, parenthesized option lists, SQLite QUERY PLAN) were wrongly rejected as writes. strip_explain_prefix now strips all option/modifier forms before read-only validation, fixing this centrally for all three engines while still rejecting writes hidden behind an EXPLAIN decoration. - Bug 4: SELECT SLEEP() swallows the server-side MAX_EXECUTION_TIME interrupt and returned partial data as success. The client-side tokio timeout is now the authoritative deadline (exactly timeout_ms -> QUERY_TIMEOUT); MAX_EXECUTION_TIME is set longer as a server-side cleanup backstop only. Postgres/SQLite derive row-vs-affected dynamically from the prepared statement, so they do not share Bugs 1/2/4; they shared Bug 3, fixed by the capability change (added SQLite EXPLAIN QUERY PLAN and Postgres paren-option coverage). Adds deterministic capability tests for all EXPLAIN variants (per engine, plus hidden-write rejection) and ignored MySQL integration tests for transaction control, EXPLAIN row capture, and timeout-as-error. Verified live against MySQL 8.0: all four repros fixed; writes/DDL/CTE-DML and EXPLAIN-hidden writes still rejected. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…, test-live.sh (REF-275) Docker Compose provisions seeded MySQL 8.0/8.4 and PostgreSQL 16 with healthchecks; vendor-specific init-script seeds (no shared SQL between engines) cover the REF-274 dataset. New live_mysql/live_postgres suites drive the compiled plenum binary end-to-end, are #[ignore]d for offline runs, and fail fast when PLENUM_TEST_*_DSN is missing under --include-ignored. scripts/test-live.sh wraps up --wait → test → down, with --keep for iteration. Co-Authored-By: Paperclip <noreply@paperclip.ing>
Testing Expectations now covers spin-up-as-needed via scripts/test-live.sh for engine/JSON-contract changes, --keep iteration with explicit teardown, the offline cargo test default, and the PLENUM_TEST_*_DSN env contract. Co-Authored-By: Paperclip <noreply@paperclip.ing>
35 tests covering the full SQLite coverage matrix per the REF-274 plan: - connect: valid path; nonexistent file → CONNECTION_FAILED - introspect: tables, columns + declared type affinity, single/composite PK, FK, composite FK, named UNIQUE index, view list, view details, ListIndexes per table, stable deterministic JSON shape - query allowed: SELECT (type_matrix w/ emoji/BLOB/NULL, customers, view), EXPLAIN, EXPLAIN QUERY PLAN, PRAGMA table_info/index_list, transaction control (BEGIN, COMMIT/ROLLBACK/SAVEPOINT/RELEASE not rejected as CAPABILITY_VIOLATION) - query denied: INSERT/UPDATE/DELETE/CREATE/DROP/ALTER all produce CAPABILITY_VIOLATION; re-query after each proves DB state unchanged - safety: max_rows truncates 1500-row bulk_rows table, rows_truncated flag set; no truncation when max_rows > count; timeout_ms completes fast queries; sqlite3_interrupt fires on 1B-row CTE → QUERY_TIMEOUT - envelope: QueryResult/IntrospectResult JSON shape, execution_ms excluded from determinism check, error code + message present Fixture mirrors MySQL/Postgres logical dataset: type_matrix, customers, orders, orders/order_items with composite PK+FK, bulk_rows (1500 rows), v_order_totals view. Explicit CREATE UNIQUE INDEX used instead of inline UNIQUE constraint so the index is visible in Plenum's introspect output (auto-created sqlite_autoindex_* names are filtered by the engine). Runs fully offline — no Docker. Plain cargo test includes all 35 tests. Co-Authored-By: Paperclip <noreply@paperclip.ing>
Expand tests/live_mysql.rs from the harness smoke test to the full approved REF-274 matrix, run against both MySQL 8.0 and 8.4 via a mysql_matrix! macro (39 tests total): - connect: valid creds surface server version/metadata; wrong password -> normalized CONNECTION_FAILED envelope with no credential leak; --password-env and --password-command sources - introspect: table list, ENUM/SET/JSON/generated column types, composite PK/FK (incl. composite FK order_items -> orders), indexes, views (list + details), databases (wildcard "*" connection) - query allowed: SELECT (exact rows), EXPLAIN, SHOW, DESCRIBE, transaction control - query denied: INSERT/UPDATE/DELETE/CREATE/DROP/ALTER/TRUNCATE -> CAPABILITY_VIOLATION with follow-up probes proving state unchanged - safety: --max-rows truncation meta (rows_truncated/has_more/ next_offset) on the 1,500-row table; SLEEP() + --timeout-ms -> structured QUERY_TIMEOUT naming the budget (locks in REF-258) - envelope: top-level key sets match schemas/*.json; deterministic with execution_ms redacted; stdout single JSON doc, empty stderr - cross-version: canonical invocations byte-identical across 8.0/8.4 after redacting version strings, row estimates, and timing Verified: scripts/test-live.sh green (39 mysql + 17 postgres); plain cargo test green with all live tests ignored. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Co-Authored-By: Paperclip <noreply@paperclip.ing>
…ew definitions (REF-277) Three engine bugs surfaced by the new PostgreSQL live matrix: - NULL detection probed columns as Option<String>, which fails with a type error (not Ok(None)) on NULL non-text columns such as boolean. Replaced with a type-agnostic NullProbe FromSql impl that accepts every type. - Foreign-key introspection joined constraint_column_usage without ordinal correlation, so composite FKs degenerated into the cross-product of their columns. The referenced side now comes from key_column_usage on the unique constraint, correlated via position_in_unique_constraint. Grouping now uses BTreeMap so multi-FK output order is deterministic. - View details selected a nonexistent "definition" column; the information_schema.views column is view_definition. Co-Authored-By: Paperclip <noreply@paperclip.ing>
…ty, envelope (REF-277) Implements the approved REF-274 plan section 4 for postgres16, on top of the REF-275 harness. 17 tests, all #[ignore]d so plain cargo test stays offline: - connect: server metadata contract, wrong password -> CONNECTION_FAILED without echoing the credential, password_env and password_command sources (including proof the plaintext never lands in the saved config, and a missing env var fails with CONFIG_ERROR) - introspect: full type matrix (arrays, JSONB, enum), composite PK/FK with column-order fidelity, unique/non-unique indexes, view listing + details, multi-schema listing and --schema scoping - query allowed: SELECT round-trip of seeded unicode/NULL/enum/array/jsonb values, EXPLAIN, EXPLAIN ANALYZE, transaction control statements - query denied: INSERT/UPDATE/DELETE/TRUNCATE/CREATE/DROP/ALTER -> CAPABILITY_VIOLATION, then re-queried to prove DB state unchanged - safety: max_rows truncation + pagination meta on the 1,500-row table, timeout_ms via pg_sleep -> QUERY_TIMEOUT - envelope: required-field checks against schemas/*.json, identical output across runs with execution_ms redacted, JSON-only stdout Verified: scripts/test-live.sh green (mysql 39, postgres 17), plain cargo test green, cargo build --release clean. Co-Authored-By: Paperclip <noreply@paperclip.ing>
Replace the broken inline-services stub with a job that delegates the full DB lifecycle to scripts/test-live.sh: - Removes the GitHub services: block (wrong credentials, no seeding, missing MySQL 8.4) in favour of docker compose driven by the script. - scripts/test-live.sh handles compose up --wait (seeded, healthchecked), exports the PLENUM_TEST_*_DSN env vars, runs `cargo test --test live_mysql --test live_postgres -- --include-ignored`, and tears down via trap EXIT — containers never leak on test failure. - Host ports set explicitly in env (43306 / 43307 / 45432) to avoid runner collisions and keep the job self-documenting. - Adds cargo registry + build caches to the job. - Offline test job is unchanged; the two jobs run independently. Co-Authored-By: Paperclip <noreply@paperclip.ing>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
PR: Round 1 + Round 2 agent-ergonomics & read-only hardening
Branch
feature/ai-updatesdelivers the first two roadmap rounds scoped under REF-258, plus the earlier capability-hardening work, bringing Plenum's read-only enforcement, output contract, and connection ergonomics up to spec. 11 commits, +5,117 / −465 across 33 files.Summary
This branch closes out the highest-risk correctness gaps in capability enforcement, hardens read-only mode at the session level for every engine, and adds the agent-facing quality-of-life features (connection listing, ping, byte budgets, versioned schemas) that make Plenum safer and more deterministic for autonomous callers.
Added
schemas/(error_envelope,connect_success,introspect_success,query_success), generated from the Rust types viaschemars(cargo run --bin generate-schemas). Atests/schema_drift.rstest fails CI if checked-in schemas diverge from live types.meta.contract_versionon all envelopes — agents can guard against silent breaking changes by checking a single field.plenum connect --list(REF-268) — enumerate saved connections as JSON without opening a database.plenum connect --test(ping) (REF-267) — validate a stored/explicit connection end-to-end; addsrusqliteas a dev-dependency for SQLite test fixtures.plenum query --max-bytes(REF-269) — byte-budget guard that caps serialized result size to stay under MCP token limits, complementing--max-rows.plenum introspectcolumn comments + row estimates (REF-263) — richer schema output for all three engines.plenum connect --password-env(REF-36) — wired end-to-end so credentials can be referenced from the environment instead of stored in plaintext.live-db-testsjob — runs the previously#[ignore]d Postgres + MySQL integration tests against service containers, isolated from the main test signal.Changed
Fixed
INSERT/UPDATE/DELETEinside aWITHCTE are now rejected across all engines.Tests & docs
tests/output_validation.rs,instasnapshots).README.mddocuments theschemas/directory, thegenerate-schemasbinary, the drift test, and thecontract_versionguard.Compatibility notes
connect,introspect,query) unchanged; new behavior is additive flags.meta.contract_version; existing consumers that ignore unknownmetafields are unaffected.Verification
cargo test(unit + snapshot + drift) — expected green in CI.live-db-testsCI job exercises Postgres 16 and MySQL 8.0 integration paths.