Skip to content

Commit e8a39f7

Browse files
committed
feat: retain Community SPI compatibility slice
1 parent df69118 commit e8a39f7

41 files changed

Lines changed: 5137 additions & 177 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/ci.yml

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,8 @@ jobs:
6969
runs-on: ubuntu-latest
7070
steps:
7171
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.2.2
72+
with:
73+
submodules: recursive
7274
- name: Install Linux keyring dependencies
7375
run: sudo apt-get update && sudo apt-get install -y libdbus-1-dev pkg-config
7476
- uses: dtolnay/rust-toolchain@4cda84d5c5c54efe2404f9d843567869ab1699d4
@@ -83,6 +85,10 @@ jobs:
8385
cache-dependency-path: |
8486
java/pom.xml
8587
java/compat-runtime/pom.xml
88+
third_party/chat2db-community/chat2db-community-server/chat2db-community-bom/pom.xml
89+
third_party/chat2db-community/chat2db-community-server/chat2db-community-spi/pom.xml
90+
third_party/chat2db-community/chat2db-community-server/chat2db-community-plugins/chat2db-community-mysql/pom.xml
91+
third_party/chat2db-community/chat2db-community-server/chat2db-community-plugins/chat2db-community-h2/pom.xml
8692
- name: Package Java engine
8793
working-directory: java
8894
run: ./mvnw -B -DskipTests package
@@ -96,6 +102,14 @@ jobs:
96102
CHAT2DB_JAVA_ENGINE_JAR: "${{ github.workspace }}/java/compat-runtime/target/chat2db-compat-runtime-0.1.0-SNAPSHOT.jar"
97103
CHAT2DB_H2_DRIVER_JAR: "${{ github.workspace }}/java/compat-runtime/target/test-drivers/h2-2.3.232.jar"
98104
run: cargo test -p chat2db-java-bridge --features java-integration --test java_jdbc_h2 --locked
105+
- name: Verify fixed Community H2 classpath reproducibility
106+
run: ./scripts/verify-community-h2-reproducibility.sh
107+
- name: Verify real Community H2 SPI vertical slice
108+
env:
109+
CHAT2DB_JAVA_ENGINE_JAR: "${{ github.workspace }}/java/compat-runtime/target/chat2db-compat-runtime-0.1.0-SNAPSHOT.jar"
110+
CHAT2DB_H2_DRIVER_JAR: "${{ github.workspace }}/java/compat-runtime/target/test-drivers/h2-2.3.232.jar"
111+
CHAT2DB_COMMUNITY_CLASSPATH_DIR: "${{ github.workspace }}/target/community-h2-classpath"
112+
run: cargo test -p chat2db-java-bridge --features java-integration --test java_community_h2 --locked
99113
- name: Verify product H2 vertical slice
100114
env:
101115
CHAT2DB_JAVA_ENGINE_JAR: "${{ github.workspace }}/java/compat-runtime/target/chat2db-compat-runtime-0.1.0-SNAPSHOT.jar"
@@ -179,6 +193,9 @@ jobs:
179193
- name: Test Windows Java bridge path contracts
180194
if: runner.os == 'Windows'
181195
run: cargo test -p chat2db-java-bridge --lib --locked
196+
- name: Initialize fixed Community source on Windows
197+
if: runner.os == 'Windows'
198+
run: git submodule update --init --recursive third_party/chat2db-community
182199
- uses: actions/setup-java@c1e323688fd81a25caa38c78aa6df2d33d3e20d9 # v4.7.1
183200
if: runner.os == 'Windows'
184201
with:
@@ -188,6 +205,10 @@ jobs:
188205
cache-dependency-path: |
189206
java/pom.xml
190207
java/compat-runtime/pom.xml
208+
third_party/chat2db-community/chat2db-community-server/chat2db-community-bom/pom.xml
209+
third_party/chat2db-community/chat2db-community-server/chat2db-community-spi/pom.xml
210+
third_party/chat2db-community/chat2db-community-server/chat2db-community-plugins/chat2db-community-mysql/pom.xml
211+
third_party/chat2db-community/chat2db-community-server/chat2db-community-plugins/chat2db-community-h2/pom.xml
191212
- name: Package Java engine for Windows managed-driver test
192213
if: runner.os == 'Windows'
193214
working-directory: java
@@ -201,6 +222,20 @@ jobs:
201222
run: >-
202223
cargo test -p chat2db-core --features java-integration
203224
--test java_h2_product --locked
225+
- name: Build fixed Community H2 compatibility classpath on Windows
226+
if: runner.os == 'Windows'
227+
shell: bash
228+
run: ./scripts/build-community-h2-classpath.sh
229+
- name: Verify Windows real Community H2 SPI vertical slice
230+
if: runner.os == 'Windows'
231+
shell: pwsh
232+
env:
233+
CHAT2DB_JAVA_ENGINE_JAR: "${{ github.workspace }}/java/compat-runtime/target/chat2db-compat-runtime-0.1.0-SNAPSHOT.jar"
234+
CHAT2DB_H2_DRIVER_JAR: "${{ github.workspace }}/java/compat-runtime/target/test-drivers/h2-2.3.232.jar"
235+
CHAT2DB_COMMUNITY_CLASSPATH_DIR: "${{ github.workspace }}/target/community-h2-classpath"
236+
run: >-
237+
cargo test -p chat2db-java-bridge --features java-integration
238+
--test java_community_h2 --locked
204239
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
205240
with:
206241
node-version: "22.22.2"

.gitmodules

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
[submodule "third_party/chat2db-community"]
2+
path = third_party/chat2db-community
3+
url = https://github.com/OtterMind/Chat2DB.git

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Makefile

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
.PHONY: verify rust rust-process-tests java ipc-integration jdbc-h2-integration \
2-
product-h2-integration frontend-deps frontend desktop generate-contracts \
3-
check-contracts
2+
community-h2-classpath community-h2-reproducibility community-h2-integration \
3+
product-h2-integration \
4+
frontend-deps frontend desktop generate-contracts check-contracts
45

56
JAVA_ENGINE_JAR := $(CURDIR)/java/compat-runtime/target/chat2db-compat-runtime-0.1.0-SNAPSHOT.jar
67
H2_DRIVER_JAR := $(CURDIR)/java/compat-runtime/target/test-drivers/h2-2.3.232.jar
8+
COMMUNITY_CLASSPATH_DIR := $(CURDIR)/target/community-h2-classpath
79

810
verify: rust rust-process-tests java ipc-integration jdbc-h2-integration \
9-
product-h2-integration frontend desktop
11+
community-h2-integration product-h2-integration frontend desktop
1012

1113
rust:
1214
cargo fmt --all --check
@@ -25,6 +27,15 @@ ipc-integration: java
2527
jdbc-h2-integration: java
2628
CHAT2DB_JAVA_ENGINE_JAR="$(JAVA_ENGINE_JAR)" CHAT2DB_H2_DRIVER_JAR="$(H2_DRIVER_JAR)" cargo test -p chat2db-java-bridge --features java-integration --test java_jdbc_h2 --locked
2729

30+
community-h2-classpath:
31+
./scripts/build-community-h2-classpath.sh
32+
33+
community-h2-reproducibility:
34+
./scripts/verify-community-h2-reproducibility.sh
35+
36+
community-h2-integration: java community-h2-classpath
37+
CHAT2DB_JAVA_ENGINE_JAR="$(JAVA_ENGINE_JAR)" CHAT2DB_H2_DRIVER_JAR="$(H2_DRIVER_JAR)" CHAT2DB_COMMUNITY_CLASSPATH_DIR="$(COMMUNITY_CLASSPATH_DIR)" cargo test -p chat2db-java-bridge --features java-integration --test java_community_h2 --locked
38+
2839
product-h2-integration: java
2940
CHAT2DB_JAVA_ENGINE_JAR="$(JAVA_ENGINE_JAR)" CHAT2DB_H2_DRIVER_JAR="$(H2_DRIVER_JAR)" cargo test -p chat2db-core --features java-integration --test java_h2_product --locked
3041

README.md

Lines changed: 34 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ Private implementation of the Chat2DB Community hybrid runtime.
44

55
## Current state
66

7-
The repository has completed the first six buildable stages:
7+
The repository has completed Stages 1 through 6 and the first two independently
8+
buildable Stage 7 slices:
89

910
- canonical Rust API contracts;
1011
- a transport-neutral Rust application service root;
@@ -50,16 +51,24 @@ The repository has completed the first six buildable stages:
5051
plus a JSON CLI for datasource discovery, forced-read-only query lifecycle,
5152
cancellation, and retained-result paging; and
5253
- an `rmcp` 2.2 stdio server with five bounded datasource/query tools backed by
53-
that same running `Application`.
54+
that same running `Application`;
55+
- strict local JDBC driver-pack discovery, hash verification, startup preload,
56+
and immutable Core/Axum/Tauri inventory; and
57+
- a fixed Community 5.3.0 compatibility classpath that discovers real
58+
`IPlugin` implementations and exposes H2 plugin catalog, schema metadata,
59+
`CREATE SCHEMA` builder, and retained ANTLR parser operations over Protobuf,
60+
with every one of its 148 JARs bound to the source commit by a checked-in
61+
filename, byte-length, and SHA-256 lock.
5462

5563
Stage 6 is complete. Web and desktop own the product runtime and publish its
5664
owner-only local endpoint; CLI and MCP attach to that host and never contact
5765
Java directly. The current MCP surface is deliberately read-only and does not
5866
accept JDBC bind parameters. A complete end-user Agent workspace and
59-
CLI-started headless host remain follow-on product work. The first Stage 7
60-
slice adds strict local driver-pack manifests, bounded hash verification,
61-
startup preload, and Core/Axum/Tauri inventory. Signing, downloading, updating,
62-
rollback, and the existing Chat2DB plugin/ANTLR estate remain Stage 7 work.
67+
CLI-started headless host remain follow-on product work. Stage 7A implements
68+
strict local driver packs. Stage 7B pins Community source, loads its runtime in
69+
an isolated Java classloader, and proves one real H2 SPI/ANTLR vertical slice.
70+
Signing, downloading, updating, rollback, product wiring for Community
71+
metadata, and full per-dialect compatibility remain Stage 7 work.
6372

6473
## Architecture
6574

@@ -100,9 +109,25 @@ Java verification downloads H2 `2.3.232` into
100109
`java/compat-runtime/target/test-drivers/` as an external Stage 3 test fixture.
101110
H2 is not a runtime dependency of the compatibility engine, and the packaged
102111
JAR integration test rejects any build that embeds `org/h2/Driver.class`.
103-
The H2 gates cover both the Stage 3 bridge and the Stage 5 product path from a
104-
vault-backed datasource through Java streaming into retained-result paging and
105-
cancellation. H2 is a test fixture rather than a bundled product driver.
112+
The H2 gates cover the Stage 3 JDBC bridge, the Stage 5 product path from a
113+
vault-backed datasource through retained-result paging and cancellation, and
114+
the Stage 7B Community path through real `IPlugin`, `IDbMetaData`,
115+
`ISqlBuilder`, and ANTLR parser implementations. H2 remains an external test
116+
driver rather than a runtime dependency of either Java classpath.
117+
118+
Build and run only the fixed Community H2 compatibility gate with:
119+
120+
```bash
121+
make community-h2-integration
122+
```
123+
124+
That target requires a clean submodule at the fixed commit, builds through the
125+
checked-in Maven Wrapper and a repository-local Maven cache, derives archive
126+
timestamps from the commit, excludes the H2 JDBC driver, and deterministically
127+
removes dependency-manifest `Class-Path` entries before rejecting any JAR set
128+
that differs from `third_party/community-h2-classpath.lock`. Run
129+
`make community-h2-reproducibility` to compare every artifact byte across two
130+
consecutive clean builds.
106131

107132
Generate or verify the external contracts:
108133

crates/chat2db-core/src/error.rs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -484,6 +484,7 @@ impl From<BridgeError> for AppError {
484484
| BridgeError::Spawn(_)
485485
| BridgeError::MissingPipe(_)
486486
| BridgeError::DriverArtifact { .. }
487+
| BridgeError::CommunityArtifact { .. }
487488
| BridgeError::DriverSnapshotDirectory { .. }
488489
| BridgeError::NonUtf8DriverArtifact(_)
489490
| BridgeError::StaleHandle(_)
@@ -493,6 +494,8 @@ impl From<BridgeError> for AppError {
493494
| BridgeError::InvalidHandshake(_)
494495
| BridgeError::UnexpectedResponse(_)
495496
| BridgeError::SupervisorTask(_)
497+
| BridgeError::ProcessCleanup { .. }
498+
| BridgeError::CleanupAfterFailure { .. }
496499
| BridgeError::Frame(_) => Self::internal(),
497500
}
498501
}
@@ -504,6 +507,7 @@ mod tests {
504507
AgentError, ConfigError, ExecutionOutcome, ProviderError, ProviderKind, ToolExecutionError,
505508
};
506509
use chat2db_contract::ApiErrorDetails;
510+
use chat2db_java_bridge::BridgeError;
507511
use chat2db_storage::StorageError;
508512

509513
use super::{AppError, AppErrorKind};
@@ -570,6 +574,42 @@ mod tests {
570574
assert_no_sentinel(&mapped);
571575
}
572576

577+
#[test]
578+
fn community_artifact_errors_are_internal_and_hide_local_paths() {
579+
let mapped = AppError::from(BridgeError::CommunityArtifact {
580+
operation: "snapshot",
581+
path: SENTINEL.into(),
582+
source: std::io::Error::other(SENTINEL),
583+
});
584+
585+
assert_eq!(mapped.kind(), AppErrorKind::Internal);
586+
assert_eq!(mapped.api_error().code, "internal_error");
587+
assert_no_sentinel(&mapped);
588+
}
589+
590+
#[test]
591+
fn process_cleanup_errors_are_internal_and_hide_retained_snapshot_paths() {
592+
let errors = [
593+
BridgeError::ProcessCleanup {
594+
retained_snapshot: SENTINEL.into(),
595+
message: SENTINEL.to_owned(),
596+
},
597+
BridgeError::CleanupAfterFailure {
598+
primary: Box::new(BridgeError::ShutdownTimeout),
599+
cleanup: Box::new(BridgeError::ProcessCleanup {
600+
retained_snapshot: SENTINEL.into(),
601+
message: SENTINEL.to_owned(),
602+
}),
603+
},
604+
];
605+
for error in errors {
606+
let mapped = AppError::from(error);
607+
assert_eq!(mapped.kind(), AppErrorKind::Internal);
608+
assert_eq!(mapped.api_error().code, "internal_error");
609+
assert_no_sentinel(&mapped);
610+
}
611+
}
612+
573613
#[test]
574614
fn cancellations_are_non_retryable_conflicts() {
575615
for mapped in [

crates/chat2db-engine-protocol/build.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,20 @@ fn main() -> Result<(), Box<dyn Error>> {
66
let proto_directory = repository_root.join("proto");
77
let compatibility_protocol = proto_directory.join("chat2db/compat/v1/compat.proto");
88
let jdbc_protocol = proto_directory.join("chat2db/compat/v1/jdbc.proto");
9+
let community_protocol = proto_directory.join("chat2db/compat/v1/community.proto");
910

1011
println!(
1112
"cargo:rerun-if-changed={}",
1213
compatibility_protocol.display()
1314
);
1415
println!("cargo:rerun-if-changed={}", jdbc_protocol.display());
16+
println!("cargo:rerun-if-changed={}", community_protocol.display());
1517

1618
let mut config = prost_build::Config::new();
1719
config.protoc_executable(protoc_bin_vendored::protoc_bin_path()?);
18-
config.compile_protos(&[compatibility_protocol, jdbc_protocol], &[proto_directory])?;
20+
config.compile_protos(
21+
&[compatibility_protocol, jdbc_protocol, community_protocol],
22+
&[proto_directory],
23+
)?;
1924
Ok(())
2025
}

crates/chat2db-engine-protocol/src/lib.rs

Lines changed: 60 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,29 @@ pub async fn read_frame_with_limit<R, M>(
7979
where
8080
R: AsyncRead + Unpin,
8181
M: Message + Default,
82+
{
83+
let Some(payload) = read_frame_payload_with_limit(reader, local_maximum).await? else {
84+
return Ok(None);
85+
};
86+
Ok(Some(M::decode(payload.as_slice())?))
87+
}
88+
89+
/// Reads one frame and returns its undecoded Protobuf payload.
90+
///
91+
/// This is intended for callers that must inspect the original wire data
92+
/// before decoding can discard unknown or duplicate fields. The configured
93+
/// limit is always capped by [`MAX_FRAME_BYTES`].
94+
///
95+
/// # Errors
96+
///
97+
/// Returns [`FrameError`] when the pipe fails, the length violates either
98+
/// receive limit, or the frame is truncated.
99+
pub async fn read_frame_payload_with_limit<R>(
100+
reader: &mut R,
101+
local_maximum: usize,
102+
) -> Result<Option<Vec<u8>>, FrameError>
103+
where
104+
R: AsyncRead + Unpin,
82105
{
83106
let mut header = [0_u8; 4];
84107
let bytes_read = reader.read(&mut header[..1]).await?;
@@ -92,7 +115,7 @@ where
92115

93116
let mut payload = vec![0_u8; payload_length];
94117
reader.read_exact(&mut payload).await?;
95-
Ok(Some(M::decode(payload.as_slice())?))
118+
Ok(Some(payload))
96119
}
97120

98121
/// Writes one four-byte big-endian length-prefixed Protobuf frame.
@@ -164,8 +187,8 @@ mod tests {
164187
use tokio::io::{AsyncWriteExt, duplex};
165188

166189
use super::{
167-
FrameError, MAX_FRAME_BYTES, current_version, read_frame, read_frame_with_limit, wire,
168-
write_frame, write_frame_with_limit,
190+
FrameError, MAX_FRAME_BYTES, current_version, read_frame, read_frame_payload_with_limit,
191+
read_frame_with_limit, wire, write_frame, write_frame_with_limit,
169192
};
170193

171194
#[tokio::test]
@@ -251,6 +274,40 @@ mod tests {
251274
));
252275
}
253276

277+
#[tokio::test]
278+
async fn raw_frame_reader_preserves_the_exact_protobuf_payload() {
279+
let payload = wire::ClientEnvelope {
280+
meta: Some(wire::RequestMeta {
281+
request_id: "raw-frame".to_owned(),
282+
trace_id: "raw-frame".to_owned(),
283+
..Default::default()
284+
}),
285+
payload: Some(wire::client_envelope::Payload::Ping(wire::Ping {
286+
nonce: 9,
287+
})),
288+
}
289+
.encode_to_vec();
290+
let (mut writer, mut reader) = duplex(256);
291+
writer
292+
.write_all(
293+
&u32::try_from(payload.len())
294+
.expect("test payload length must fit u32")
295+
.to_be_bytes(),
296+
)
297+
.await
298+
.expect("header must write");
299+
writer
300+
.write_all(&payload)
301+
.await
302+
.expect("payload must write");
303+
304+
let raw = read_frame_payload_with_limit(&mut reader, MAX_FRAME_BYTES)
305+
.await
306+
.expect("raw frame must read")
307+
.expect("raw frame must be present");
308+
assert_eq!(raw, payload);
309+
}
310+
254311
#[tokio::test]
255312
async fn negotiated_peer_limit_is_enforced_before_writing() {
256313
let (mut writer, _reader) = duplex(256);

crates/chat2db-java-bridge/Cargo.toml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ publish = false
1212
[dependencies]
1313
chat2db-engine-protocol = { path = "../chat2db-engine-protocol" }
1414
prost.workspace = true
15+
rustix.workspace = true
1516
sha2 = "0.10"
1617
tempfile = "3"
1718
thiserror.workspace = true
@@ -37,6 +38,11 @@ name = "java_jdbc_h2"
3738
path = "tests/java_jdbc_h2.rs"
3839
required-features = ["java-integration"]
3940

41+
[[test]]
42+
name = "java_community_h2"
43+
path = "tests/java_community_h2.rs"
44+
required-features = ["java-integration"]
45+
4046
[features]
4147
default = []
4248
java-integration = []

0 commit comments

Comments
 (0)