Skip to content

Release/2.3.3#23

Merged
andrehrferreira merged 42 commits into
mainfrom
release/2.3.3
Jun 10, 2026
Merged

Release/2.3.3#23
andrehrferreira merged 42 commits into
mainfrom
release/2.3.3

Conversation

@andrehrferreira

@andrehrferreira andrehrferreira commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

Release 2.3.3 — audit closure + Docker-validation fixes

Closes the 2.3.3 audit-task batch (GH #14#22) and fixes seven additional pre-existing bugs found during manual Docker validation.

Closes #15 — explicit-transaction COMMIT no longer runs the O(N) rebuild_indexes_from_storage() scan; scoped incremental maintenance over the transaction's own write set (storage watermarks captured at BEGIN + tracked created-entity lists). Commit cost now scales with the transaction's write set, not graph size. Guarded by the explicit_commit_keeps_property_index_seek contract test and an incremental-vs-full-rebuild equivalence test. (dfb6315b)

Closes #16RecordStore::clone is now a shared handle (mmaps, file handles, property store, id counters behind Arcs), so the per-write refresh_executor does a handful of Arc::clones instead of 2–6 file-open + mmap syscalls. Guarded by clone_is_shared_handle_not_reopen (Arc::ptr_eq + cross-handle visibility). (310cb285 / 9506b912)

Additional fixes (found by manual Docker validation; all reproduce on published 2.3.2)

  • ROLLBACK now undoes standalone in-tx Cypher CREATE (session watermark sweep)
  • BEGIN/COMMIT/ROLLBACK/SAVEPOINT over POST /cypher reach the engine (were silent no-ops)
  • SET on an indexed property no longer leaves stale typed-index entries (wrong results both directions)
  • Non-tx Cypher CREATE maintains the typed property index immediately
  • EXPLAIN/PROFILE parse at top level (were returning empty ASTs); PROFILE CALL { … } works
  • Chain-walk off-by-one in find_relationship_between (rel_id+1 pointer encoding)
  • Async-WAL shutdown drains the channel (accepted entries were dropped — 1990/2000 recovered)

Validation

  • cargo test --workspace: 4716 passed / 0 failed; clippy 0 warnings; fmt clean
  • Neo4j compatibility suite: 300 passed — identical to published 2.3.2 (zero regression; the 10 section-18 point-accessor failures are pre-existing parity gaps, registered as phase7_point-property-accessors-parity)
  • Docker E2E battery: 11/11 functional + durability checks across container restarts

🤖 Generated with Claude Code

andrehrferreira and others added 30 commits June 9, 2026 16:59
Write/ingest hot-path audit (companion to #11/#12/#13): per-COMMIT full index rebuild (#15), refresh_executor RecordStore mmap clone per write (#16), RelationshipIndex nested-lock deadlock hazard (#17), swallowed relationship-index add error (#18), WAL channel blocking send (#19), hub chain-walk warn timing (#20), per-write indexed-property loop (#21), CALL IN TRANSACTIONS single-tx OOM guard (#22). Plus #14 (UNWIND+MATCH+MERGE edge upsert) already tasked.
…index commit contract guard

Investigated removing the per-COMMIT rebuild_indexes_from_storage (#15): it is load-bearing because the explicit-transaction CREATE path does not synchronously maintain the typed property index (find_exact/NodeIndexSeek), so removal makes seeks miss explicit-tx-committed nodes. Kept the rebuild; documented the constraint inline. Added a contract-guard test a future incremental fix must keep green. Issue #15 stays open.
create_relationship no longer silently swallows a relationship-index update failure: it logs at error level and marks the index dirty. The next find_relationship_between rebuilds the index from storage (new rebuild_relationship_index_from_storage, shared with the startup rebuild) and clears the flag, restoring the O(1) exact-edge fast path. Correctness was already preserved by the authoritative chain-walk fallback; this prevents a silent persistent O(degree) degradation and makes the failure observable. Test: relationship_index_self_heals_when_dirty. nexus-core lib 2371 passed.
add_relationship held stats.write() while acquiring node_index.read() to set the approximate total_nodes, forming a lock-order cycle with the node_index write blocks (latent deadlock under concurrent edge inserts). Read node_index.len() into a local BEFORE taking stats.write() so the two locks are acquired sequentially, never nested. Test: concurrent_add_relationship_does_not_deadlock (8 threads x 500 edges + interleaved stats()).
find_relationship_between now emits the O(degree) hub-walk warning the moment it crosses 1000 hops, instead of only after the loop completes. This surfaces the pathology in real time and also fires when the edge is eventually found (the early return previously skipped the post-loop warning entirely). Observability-only; no behavior change.
…21)

maintain_indexed_properties (runs on every node write) iterated every property x every label calling get_key_id + has_index. Add PropertyIndex::has_any_index() and early-return when no property index is registered at all (the common case for un-indexed graphs), so an un-indexed write does zero index work. Behaviour-preserving (the loop was already a no-op without indexes). Tests: has_any_index_reflects_registration + existing index-maintenance coverage.
…19)

The async WAL command channel was bounded by channel_buffer_size (1000) while max_queue_depth (10000) only fed a counter, so append() blocked far earlier than configured and a full-channel stall was silent. Size the channel from max(channel_buffer_size, max_queue_depth) so the depth knob is real, and submit via try_send: on a genuinely full channel, increment a backpressure_blocks stat + warn (so the stall is observable) before the blocking send. Ordering + durability unchanged (crossbeam blocks, never drops). Test: test_backpressure_burst_does_not_deadlock (2000-entry burst into a 16-slot channel completes).
#22)

Since the #12 fix runs the subquery once and materializes the full result in one transaction (per-OF-n-ROWS commit batching is unimplemented), an enormous subquery could OOM the server building all_results + the response. Cap at 1_000_000 rows: past the cap, abort the wrapper transaction and return a structured ERR_CALL_IN_TX_RESULT_TOO_LARGE instead of OOMing. Under-cap behaviour unchanged (call_in_transactions_terminates). Cap-exceed verified by inspection (a 1M-row e2e test is impractical).
The UNWIND-write path rejected a per-row MATCH after UNWIND, so the batched edge-upsert shape (UNWIND rows AS row MATCH (a {row.fk}),(b {row.tk}) MERGE (a)-[r:T]->(b) ON CREATE/ON MATCH SET ...) errored 'Unsupported clause after UNWIND' — forcing one HTTP request per edge. Two changes: (1) allow Clause::Match in the post-UNWIND loop, running process_match_clause_multi against the fresh per-row context (row.fk/row.tk resolve via the active unwind binding); (2) process_merge_relationship now applies ON CREATE / ON MATCH SET to the edge via a new apply_merge_rel_set helper (storage.update_relationship_properties; evaluate_set_expression resolves row.* + r.* self-refs). Edge analogue of the #13 node fix — one request per batch for edges. Test: unwind_match_merge_edge_upsert (edge created, ON CREATE SET w=5, idempotent, ON MATCH SET w=7). nexus-core lib 2375 passed.
…ser/clauses.rs into submodules

Pure code-move refactor — zero logic changes, public paths preserved
via pub use re-exports in facade mod.rs files. Visibility widened only
where the split moved code one module level deeper (pub(super) ->
pub(in super::super) / pub(in crate::executor::planner)).

- planner/queries.rs (4430 lines) -> queries/{mod,planner_core,strategy,
  relationships,spatial,qpp,cost,expressions,notifications,unindexed}
- eval/projection.rs (4168 lines) -> projection/{mod,core,fn_string,
  fn_math,fn_temporal,fn_list,fn_geo,fn_graph}
- parser/clauses.rs (3120 lines) -> clauses/{mod,read,write,pattern,
  subquery,admin}

Part of phase5_split-oversized-files (rulebook task).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…rators/aggregate.rs, operators/procedures.rs into submodules

Pure code-move refactor — zero logic changes, public paths preserved
via facade mod.rs re-exports. Visibility widened minimally where code
moved one module level deeper (pub(super) -> pub(in super::super),
private helpers -> pub(in crate::executor)).

- parser/expressions.rs (1652) -> expressions/{mod,identifier,literals,
  precedence,primary,structured}
- parser/tests.rs (2345) -> tests/{mod,clauses,ddl,expressions,
  external_ids,patterns,tokens}
- operators/aggregate.rs (2090) -> aggregate/{mod,core,columnar,
  parallel,alias,tests} — 5/5 tests preserved
- operators/procedures.rs (2088) -> procedures/{mod,call,db_schema,
  db_indexes,dbms,fts,spatial_procs}

Part of phase5_split-oversized-files (rulebook task).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…submodules

Pure code-move refactor — zero logic changes, public paths preserved.
Facade mod.rs files keep struct definitions, constructors and module
declarations; impl blocks moved to cohesive submodules. Visibility
widened minimally for cross-submodule calls (documented per item).

- engine/mod.rs: 5853 -> 861 lines; new submodules query_pipeline,
  ddl, write_exec, match_exec, constraints, transactions
- wal/mod.rs: 1824 -> 925 lines; new submodules record, writer
- catalog/mod.rs: 1944 -> 744 lines; new submodules types, store,
  mappings, stats, extensions (36/36 tests preserved)

Part of phase5_split-oversized-files (rulebook task).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…cognition.rs into submodules

Pure code-move refactor — zero logic changes, zero assertion changes.

- engine/tests.rs (3417) -> tests/{mod,basics,crud,errors,query,write,
  constraints,fulltext,indexes,transactions} — 104/104 tests preserved
- graph/correlation/mod.rs (2313 -> 172) -> +graph_types,graph_builder,
  collection_query — 59/59 tests preserved
- graph/correlation/pattern_recognition.rs (1734) -> pattern_recognition/
  {mod,types,detectors,overlay,quality,recommendation} — 30/30 tests

Part of phase5_split-oversized-files (rulebook task).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…pi/streaming.rs into submodules

Pure code-move refactor — zero logic changes, Neo4j-compatible response
format untouched. Two split-introduced lints fixed (doc indent, tail
return). Visibility widened minimally for cross-submodule calls.

- engine/crud.rs (1561) -> crud/{mod,nodes,relationships,lookup,
  index_maintenance} — 3/3 tests preserved
- api/cypher/execute.rs (1661) -> execute/{mod,handler,write_ops}
- api/streaming.rs (1720) -> streaming/{mod,service,tools,dispatcher,
  handlers,tests} — 19/19 tests preserved

tests/integration_test.rs NOT split: discovered to be dead code (root
is a virtual workspace; no [[test]] target references it; it never
compiles). Follow-up task: phase5_wire-or-remove-dead-integration-test.

Part of phase5_split-oversized-files (rulebook task).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…up tasks

- CHANGELOG Unreleased/Changed entry for the 17-file split
- tasks.md fully checked for phase5_split-oversized-files
- new tasks: phase5_wire-or-remove-dead-integration-test,
  phase5_split-oversized-files-wave2 (raw-line metric stragglers)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…aph/clustering.rs into submodules

Pure code-move refactor (wave 2, raw-line metric) — zero logic changes,
facade mod.rs re-exports preserve all paths.

- index/mod.rs (1797 -> 179) -> +dist,label_index,knn_index,
  property_index — 49/49 tests preserved
- graph/algorithms/traversal.rs (1715) -> traversal/{mod,bfs_dfs,
  shortest_path,components,centrality,similarity,mst}
- graph/clustering.rs (1669) -> clustering/ — 24/24 tests preserved

Part of phase5_split-oversized-files-wave2 (rulebook task).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…aph/procedures.rs into submodules

Pure code-move refactor (wave 2) — zero logic changes, facades preserve
all paths; parent private use bindings kept so child use super::* keeps
resolving (downward privacy inheritance).

- graph/core.rs (1641) -> core/{mod,graph,node,edge,ids,property_store,
  stats} — 19/19 tests preserved
- correlation/data_flow/mod.rs (1632 -> 39) -> +tracker,types,analyzer,
  optimization,statistics — 33/33 tests preserved
- graph/procedures.rs (1594) -> procedures/{mod,types,shortest_path,
  centrality,community,similarity,topology,custom,registry} — 12/12

Part of phase5_split-oversized-files-wave2 (rulebook task).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…age/adjacency_list.rs, neo4j comparison test into submodules

Pure code-move refactor (wave 2 final batch) — zero logic changes,
facades preserve all paths. CHANGELOG updated for both wave-2 batches.

- executor/mod.rs (1588 -> 264) -> +types,shared,dispatch — 5/5 tests
- graph/correlation/component.rs (1542) -> component/ — 12/12 tests
- storage/adjacency_list.rs (1520) -> adjacency_list/{mod,types,store,
  tests} — 34/34 tests
- tests/neo4j_result_comparison_test.rs (1591) -> directory test target
  (main.rs + feature-area submodules) — 20/20 tests

No source file in crates/ exceeds 1500 lines anymore (raw wc -l).
Part of phase5_split-oversized-files-wave2 (rulebook task).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
….rs into submodules

Task phase5_split-oversized-files-wave2 item 1.7: split the 1591-line
neo4j_result_comparison_test.rs into tests/neo4j_result_comparison_test/
(main.rs + basic_return, expressions, functions, operators,
parameters_and_null, query_clauses modules) and storage/mod.rs into
record_store.rs, record_store_ops.rs, records.rs, and
adjacency_list/ submodule; also splits
graph/correlation/component.rs into component/ submodule.
Task phase5_wire-or-remove-dead-integration-test item 1.1 — inventory
tests/integration_test.rs and diff against per-crate integration tests
to quantify unique coverage. CHANGELOG entry documents finding that the
1892-line root-level file is unwired dead code (no [[test]] target in
any Cargo.toml) and summarises coverage overlap.
Inventory of all 30 tests (2091 lines) across four groups (A: storage/
WAL/tx/cache; B: executor E2E; C: API error-handling; D: API perf).
Coverage diff shows zero unique compilable tests — Group A (15 tests)
fully duplicated by crates/nexus-core/tests/integration.rs; Groups B–D
uncompilable due to stale Executor::new/NexusServer constructors and a
syntax error at line 687. Design doc records findings and recommends
removal pending user authorization. tasks.md marks item 1.1 [x].

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…user authorization

Task phase5_wire-or-remove-dead-integration-test items 1.2-1.4 + tail.
The 2091-line file was unwired dead code: no [[test]] target in any
Cargo.toml, uncompilable (syntax error at line 687, stale Executor::new
and NexusServer constructor signatures), and zero unique compilable
coverage — all 15 storage/catalog/WAL/tx tests are identically covered
by crates/nexus-core/tests/integration.rs (15/15 passing). User chose
REMOVE over wire per design.md recommendation. Task archived.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…sts (#22)

Task phase6_call-in-tx-result-cap. Extract the #22 materialization cap
into check_call_in_tx_result_cap (engine/ddl.rs): default 1M rows,
NEXUS_CALL_IN_TX_MAX_ROWS env override, structured
ERR_CALL_IN_TX_RESULT_TOO_LARGE error, clean wrapper-tx abort at the
call site. Adds boundary + env-knob tests. Corrects the stale #22
premise in docs: per-OF-n-ROWS commit batching IS implemented for
top-level queries via the executor operator; the cap guards the legacy
engine path only. Task archived.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
#15)

Task phase6_fix-rebuild-indexes-per-commit. Explicit-transaction COMMIT
no longer runs rebuild_indexes_from_storage() (O(N) full scan per
commit, amplified by the #11 property-index backfill). BEGIN captures
storage node/rel watermarks on the session; COMMIT applies
apply_committed_entity_index_updates over the watermark range union the
session's created-entity lists: label bitmap + typed property B-tree
per created node (the part the rebuild was load-bearing for) and the
relationship index per created rel. All inserts idempotent. Commit cost
now scales with the transaction's write set, not graph size. Contract
guard explicit_commit_keeps_property_index_seek stays green; new
equivalence test asserts incremental == full-rebuild ground truth.
2377 lib + 15 integration tests pass; clippy clean. Task archived.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…t on remove (#17)

Task phase6_fix-relindex-nested-lock. Completes the 46bb910 hotfix:
documents the lock-order invariant (type -> node -> edge -> stats,
never inverse) on the struct, applies the same read-len-before-stats
discipline to remove_relationship, and fixes total_nodes going stale
after removals (it was only refreshed on add). Adds
total_nodes_stays_correct_across_add_and_remove alongside the existing
concurrency test. 5/5 module tests pass; clippy clean. Task archived.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Task phase6_fix-unwind-match-merge-edge-upsert. Completes the 6093bfc
slice: relationship bindings in the write path now accumulate one entry
per row (the per-variable insert previously kept only the last edge),
and evaluate_return_expression_with_rels implements count(r) over the
deduped upserted edge ids (was falling through to null). Distinct-edge
semantics match the #13 node-count behavior. New multi-row test
unwind_match_merge_edge_upsert_every_row (count(r)=2, per-row property
values). 2379 lib tests pass; clippy clean. Task archived.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ce (#21)

Task phase6_prefilter-indexed-properties. Completes the 399ae32 slice:
adds PropertyIndex::has_index_for_label and a prefilter in
maintain_indexed_properties so that on a graph WITH property indexes,
writes to nodes whose labels have no registered index skip the
per-property get_key_id LMDB reads entirely. Registration set is the
live (label,key) tree map itself (kept in sync by CREATE/DROP INDEX and
the #11 startup rebuild). New registration-tracking test; full lib
2380/2380; clippy clean. Task archived.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Task phase6_propagate-relindex-add-error. Completes the 06ac218
dirty-bit/self-heal fix: adds the MERGE-no-duplicate regression test
(wiped relationship index + dirty flag, repeated Cypher edge-MERGE,
relationship_count unchanged), documents the Phase-8 manager decision
(logged-at-warn; dirty-bit guarantee not low-cost for secondary
structures that play no role in MERGE existence), and records the #18
entry in the CHANGELOG. transactions module 10/10. Task archived.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
andrehrferreira and others added 12 commits June 10, 2026 09:07
Task phase6_relwalk-warn-at-fallback. The find_relationship_between
chain walk read read_rel(rel_ptr) without decoding the rel_id+1 pointer
encoding (0 = end-of-chain sentinel), so the authoritative fallback
silently returned None — or an off-by-one rel id — whenever the
exact-edge index missed, letting an edge-MERGE duplicate the edge.
Now decodes pointers like executor/operators/path.rs, returns the true
rel id, and ignores deleted records (fast-path parity). Also adds the
fallback-entry debug log (completing 412f1ac's in-loop threshold warn)
and a test that captures the 1000-hop warning with a counting tracing
layer against a 1100-edge hub for a FOUND edge. Full lib 2382/2382;
clippy clean. Task archived.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
)

Task phase6_share-recordstore-arc. The core #16 implementation shipped
with the storage split (9506b91): mmaps, file handles, property store
and id counters live behind Arcs inside RecordStore, so the per-write
refresh_executor clone is a handful of Arc::clones instead of 2-6
file-open + mmap syscalls. This task adds the structural guard test
(Arc::ptr_eq on mmaps/files/property store + cross-handle write
visibility + shared id counters), documents the interior-shared-handle
decision over an engine-level Arc<RwLock<RecordStore>>, and records the
CHANGELOG entry. Full workspace suite green (0 failures). Task archived.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Task phase6_wal-async-backpressure. Extending the #19 backpressure test
with a WAL-replay durability assertion found a real bug: the shutdown
flag popped the writer loop while accepted Append commands still sat in
the channel, silently dropping them (1990/2000 recovered). The writer
thread now drains the channel on exit before the final flush, restoring
the accepted => durable contract. Also documents the deliberate
deviation from sync-append-on-Full (would interleave WAL entries out of
order; ordered blocking send + backpressure_blocks stat instead).
async_wal 6/6 (3x no flake), full lib 2383/2383, clippy clean.
Task archived.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…mark

Task phase6_fix-rollback-executor-created-nodes. Manual Docker
validation found ROLLBACK kept a standalone in-tx Cypher CREATE alive
(pre-existing; reproduces on published 2.3.2): the executor write path
never reports created ids into session.created_nodes, which is all the
rollback arm swept. The arm now unions the session storage watermark
range captured at BEGIN (same write-set source as the #15 scoped
commit), gated on an active transaction so a stray ROLLBACK cannot
sweep a stale watermark. Test rollback_undoes_executor_created_nodes
(rolled-back CREATE invisible, pre-existing data intact, engine usable
after). Full lib 2384/2384; clippy clean.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… HTTP

Task phase6_fix-rollback-executor-created-nodes (Docker validation
finding #2). Explicit transaction commands sent via POST /cypher
matched no handler branch and fell through to the bare executor clone,
which silently no-opped them (HTTP 200, empty result): BEGIN never
opened a transaction, ROLLBACK rolled nothing back, and COMMIT/ROLLBACK
without BEGIN succeeded. The handler now detects transaction-command
clauses in the parsed AST and routes them through engine.execute_cypher
so the session-transaction machinery engages. Pre-existing (reproduces
on published 2.3.2). nexus-server tests green; clippy clean.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…fter Docker validation

Docker validation battery: 11/11 functional checks + 4/4 durability
checks (restart) on the rebuilt 2.3.3 image — all audit fixes
(#12-#22), the rollback watermark sweep, and the server transaction
routing verified end-to-end over POST /cypher.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Task phase6_fix-set-indexed-property-stale-index. Two index-correctness
bugs found by manual Docker validation (both reproduce on published
2.3.2): (1) SET on an indexed property left the typed B-tree stale --
seek by the new value missed the node while seek by the old value
returned it (wrong results both directions); persist_node_state now
captures the pre-write bag + labels and refreshes the typed index
(typed_index_refresh_node) alongside the FTS/spatial siblings.
(2) Non-tx Cypher CREATE (executor path) never indexed the node at all,
so it was seek-invisible and a follow-up MATCH {prop} SET no-opped; the
standalone-CREATE branch now indexes the allocated id range via a
node-count watermark (index_typed_properties_for_new_nodes). TDD tests
for both; full lib 2386/2386 x3; clippy clean. Task archived.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…struction

Backlog task documenting the third Docker-validation finding: the
legacy execute_cypher_ast read fallback re-parses format!("{:?}") AST
dumps (CypherSyntax errors), and PROFILE CALL { ... } fails to retain
the inner query string. Low impact today (legacy path internal-only);
full analysis and fix plan in the proposal.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ndoff

Task phase7_fix-query-to-string-debug-reconstruction. Three fixes:
(1) EXPLAIN/PROFILE were missing from is_clause_boundary, so any
EXPLAIN/PROFILE query parsed to an EMPTY AST and failed downstream with
'Query must contain at least one clause' (existing tests were soft and
hid it). (2) The EXPLAIN/PROFILE inner parsers had no CALL arm; added
the subquery/procedure dual shape, so PROFILE CALL { ... } IN
TRANSACTIONS works. (3) The legacy execute_cypher_ast read fallbacks
re-parsed query_to_string Debug output (never valid Cypher); they now
hand the parsed AST to the executor via the one-shot
preparsed_ast_override. New tests: profile_over_call_subquery and
legacy_call_subquery_path. Workspace 4716/0; clippy clean. Archived.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ection

Workspace version 2.3.2 -> 2.3.3, Dockerfile tags + OCI version label,
CHANGELOG [Unreleased] -> [2.3.3] 2026-06-10 with release summary
(audit batch #14-#22 closure + seven pre-existing bugs fixed during
manual Docker validation).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The Neo4j compat suite grew to 325 tests; the 10 section-18 failures
(point property accessors x/y/z/crs/longitude/latitude/height, 3D
constructors, positional withinBBox) are pre-existing parity gaps --
identical 300/10 result on the published 2.3.2 image, zero regression
in 2.3.3. Backlog task with full analysis registered.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Fixes the lint version-consistency gate on PR #23 (Cargo.toml and
CHANGELOG were already at 2.3.3; the README badge was missed in the
release bump).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant