Phase 6: Core Foundation Types#76
Conversation
- Add Gson 2.13.2 and SnakeYAML 2.6 to core build.gradle.kts - Create package-private JsonUtil with shared Gson instance using LOWER_CASE_WITH_UNDERSCORES - Fix Java toolchain: use sourceCompatibility/targetCompatibility instead of strict toolchain for JDK portability
- RebuildCollectionResultTest: full JSON, missing optionals, snake_case mapping, long values - CompactionResultTest: nested collections, boxed Long null/present semantics - WALPruneResultTest: full result, null optional Longs, dry_run/vacuum_requested booleans - BackupManifestTest: full manifest, empty files, metadata fields, int/long types
- RebuildCollectionResult: 12 fields matching Go rebuild.go struct - CompactionCollectionResult: boxed Long for optional pending ops - CompactionResult: nested List<CompactionCollectionResult> - WALPruneCollectionResult: 5 boxed Long fields for optional sequence numbers - WALPruneResult: nested List<WALPruneCollectionResult> with dry_run/vacuum booleans - BackupFileMetadata: path, sizeBytes, mode, sha256, modifiedAt as String - BackupManifest: nested List<BackupFileMetadata> with int fileCount and long totalBytes
- SUMMARY.md documenting 7 result POJOs, JsonUtil, and 15 tests - STATE.md advanced to plan 2 of 3, performance metrics recorded - ROADMAP.md updated with phase 06 progress (1/3 plans) - REQUIREMENTS.md: FOUND-01 and FOUND-04 marked complete
- RebuildOptionsTest: defaults, toJson, validation rejection - WALPruneOptionsTest: toJson, policy validation, watermark pairs - BackupOptionsTest: toJson, destination validation, flag defaults - CompactRequestTest: collection and all request toJson, validation
…omaRuntime interface - AbstractChromaRuntime with static ReentrantLock and callFfiHandle/callFfiJson/callFfiVoid/callFfiBorrowedString template methods - Three abstract methods (readBorrowedString, readOwnedString, readLastError) for backend implementation - ChromaRuntime interface now includes startServer(String) returning ServerSession - ServerSession with full public API surface (lifecycle, accessors, maintenance stubs) - Tests verify lock serialization, error propagation, and template method delegation
- RebuildOptions: name required, keepBackup defaults true, toJson() matches Go FFI format - WALPruneOptions: boxed Long for nullable policies, watermark pair validation, dryRun/policy rule - BackupOptions: destinationPath required, includeMetadata/leaveStopped/leaveClosed flags - CompactCollectionRequest: name required, databaseName min 3 chars validation - CompactAllRequest: optional tenantId/databaseName with validation - 32 tests covering defaults, toJson serialization, and validation rejection
…intenance stubs - Constructor validation tests for zero handle and null callbacks - Accessor delegation tests verifying handle is passed to callbacks - URL composition test (http://address:port) - Close idempotency and try-finally ordering tests - ensureOpen guard verification for all public methods after close - Maintenance method stubs throw UnsupportedOperationException when open
- ServerConfigBuilderTest: default YAML, custom values, CORS, OTel, rawYaml, validation - EmbeddedConfigBuilderTest: default YAML, custom values, rawYaml, validation
- ServerConfigBuilder: defaults match Go's DefaultServerConfig(), CORS/OTel conditional sections - EmbeddedConfigBuilder: defaults match Go's DefaultEmbeddedConfig(), 3-field YAML - Both support rawYaml() escape hatch overriding all other fields - Both validate at build() time: port range, non-null persistPath - Golden tests parse YAML semantically to avoid quoting brittleness
- SUMMARY.md with 4 TDD commits, 49 tests, 13 files - STATE.md updated with decisions and metrics - ROADMAP.md updated with phase 06 progress - REQUIREMENTS.md: FOUND-02, FOUND-03 marked complete
…ialization guards
… calls ServerSession.close() now wraps stopAction/freeAction in try-finally so freeAction always runs, preventing native handle leaks on stop failure. Removed readLastError() drain calls from callFfiHandle, callFfiJson, and callFfiBorrowedString success paths — the Rust shim's chroma_get_last_error borrows but never clears the error slot, making drain calls no-ops in production. The error-checking protocol (only read errors when return value indicates failure) is sufficient.
…serialization - Extract Validation utility with validateTenantId/validateDatabaseName to eliminate 4x copy-pasted validation blocks across builder types - Release FFI_LOCK before JSON deserialization in callFfiJson to reduce lock contention — only the FFI call and string read need the lock - Fix maxBytes error message to use snake_case (max_bytes) for consistency
Both runtimes were missing the startServer(String) method required by the ChromaRuntime interface, causing compilation failures on all CI Java jobs.
|
@claude PTAL |
|
Claude finished @tazarov's task in 3m 17s —— View job Phase 6 Code Review
Overall this is a well-structured phase. The type system is clean, builder validation is thorough, and the Bug: Panama
|
| Severity | Issue | File |
|---|---|---|
| Bug | Panama serverStop throws WrongMethodTypeException |
PanamaChromaRuntime.java:213 |
| Design | AbstractChromaRuntime dead code until Phase 8 |
AbstractChromaRuntime.java |
| Design | callFfiVoid reads stale error slot |
AbstractChromaRuntime.java:61 |
| Minor | chroma_server_stop return not checked |
JnaChromaRuntime.java:142 |
| Minor | List accessors return null |
result POJOs |
The Panama serverStop bug is the only issue that will cause a test failure at make test-java (if the smoke test calls close() on a ServerSession). Everything else is either a future concern or a documentation/design note.
Panama's invokeExact enforces exact type matching — discarding the int return caused WrongMethodTypeException at runtime. JNA silently ignored the error code; now checks rc and throws on failure.
|
@claude review |
|
Claude finished @tazarov's task in 3m 7s —— View job Phase 6 Code Review
Good incremental progress. The previous review's main bugs are resolved. Two latent issues remain worth addressing before Phase 8 wires things up, and a few minor observations below. Fixed since last review ✅
Design:
|
| Severity | Issue | File |
|---|---|---|
| Design | AbstractChromaRuntime dead code until Phase 8 — add comment |
AbstractChromaRuntime.java |
| Bug | callFfiVoid reads stale error slot — shim never clears LAST_ERROR on read |
AbstractChromaRuntime.java:61 + shim/src/lib.rs:95 |
| Minor | WALPruneOptions.Builder(String name) skips name validation |
WALPruneOptions.java:56 |
| Minor | List accessor null contract undocumented | result POJOs |
| Nit | PanamaChromaRuntime.close() rollback rationale worth a comment |
PanamaChromaRuntime.java:341 |
The callFfiVoid stale-error issue is the only one that can cause incorrect runtime behavior once Phase 8 wires in AbstractChromaRuntime. Everything else is documentation/validation hygiene.
…date WALPruneOptions name - Change Rust shim last_error_cstring/last_error_message to use slot.take() instead of slot.as_ref()/slot.clone() so the error is consumed on read, preventing false-positive ChromaExceptions from stale errors in callFfiVoid - Add Phase 8 wiring comment to AbstractChromaRuntime - Add blank name validation to WALPruneOptions.Builder.build() consistent with RebuildOptions
- Fix Panama serverStop silently discarding return code (now checks rc != 0) - Fix JNA serverStop missing ChromaException rethrow guard - Fix ServerSession.close() losing stop exception when free also throws (stop exception is now primary, free exception is suppressed) - Fix EmbeddedSession.close() to roll back closed flag on failure (retriable) - Fix chroma_server_port to set LAST_ERROR on null handle - Add negative port validation in JNA and Panama serverPort methods - Make ServerSession.handle() package-private to prevent raw handle leaks - Return Collections.emptyList() instead of null from list accessors in BackupManifest, CompactionResult, RebuildCollectionResult, WALPruneResult - Add sqliteFilename validation to EmbeddedConfigBuilder and ServerConfigBuilder - Add maxPayloadSizeBytes validation to ServerConfigBuilder - Defensively copy corsAllowOrigins in ServerConfigBuilder - Extract validateRequiredName/validateOptionalName to Validation helper - Document thread-safety limitation on JNA and Panama runtime classes - Add startServer smoke tests for JNA and Panama - Add tests for otelEndpoint without serviceName, empty corsAllowOrigins, config builder validation, EmbeddedSession handle accessor
The Rust shim stores the config port in ServerHandle.port without updating it after the OS assigns an ephemeral port, so port 0 tests always see port=0. Use findFreePort() to reserve a real port, matching the Go test pattern (reserveFreeLoopbackPort).
|
@claude PTAL |
|
Claude finished @tazarov's task in 2m 15s —— View job Phase 6 Code Review
All four issues flagged in the previous review (2026-03-25) have been resolved. This is clean and ready to merge. Fixed since last review ✅
Remaining nit (non-blocking)
SummaryThe phase is in good shape. All previously identified bugs and design issues are resolved. The LGTM from a code review standpoint. |
Summary
Phase 6: Core Foundation Types
Goal: The core module contains all shared interfaces, builders, result types, and FFI safety infrastructure so that backend modules (JNA, Panama) can implement against stable contracts without any FFI dependency in core.
Verification: Passed (14/14 must-haves, 128 tests, 0 failures)
Establishes the complete Java type system for the v0.5.0 milestone. Creates 21 production classes in the
coremodule covering result POJOs, option/request builders, config builders, FFI safety infrastructure, and server session lifecycle — all with zero JNA or Panama dependencies.Changes
Plan 06-01: Result POJOs + Dependencies
Added Gson 2.13.2 and SnakeYAML 2.6 to core module. Created
JsonUtilwith shared Gson instance (LOWER_CASE_WITH_UNDERSCORES policy). Implemented 7 result POJOs:RebuildCollectionResult,CompactionCollectionResult,CompactionResult,WALPruneCollectionResult,WALPruneResult,BackupFileMetadata,BackupManifest. All use final fields, package-private constructors, accessor-style methods, andCollections.unmodifiableList()for list returns.Plan 06-02: Option/Request Types + Config Builders
Created 5 option/request types with nested Builders and
toJson()serialization:RebuildOptions,WALPruneOptions,BackupOptions,CompactCollectionRequest,CompactAllRequest. CreatedServerConfigBuilderandEmbeddedConfigBuilderproducing YAML output matching Go's config format, withrawYaml()escape hatch and validation atbuild()time. ExtractedValidationutility for shared tenant/database name checks.Plan 06-03: FFI Safety Infrastructure + ServerSession
Created
AbstractChromaRuntimewith staticReentrantLockand template methods (callFfiHandle,callFfiJson,callFfiVoid,callFfiBorrowedString) for safe FFI call patterns. ExtendedChromaRuntimeinterface withstartServer(String). CreatedServerSessionwith callback slots for stop/free/port/address/persistPath,try-finallyclose semantics, and maintenance method stubs for Phases 8-10.Post-implementation hardening
try-finallyensuresfreeActionalways runs; non-retriable close semanticscallFfiVoidchecksreadLastError()after void callsChromaExceptionwrapping and cause chain preservationCollections.unmodifiableList()on all list-returning accessorstenantIdvalidation across all builders;maxAgeSeconds/maxBytesreject negative valuesfinal;rawYamlblank validation addedcallFfiJsonreleases FFI lock before JSON deserializationRequirements Addressed
Verification
Key Decisions
Longfor optional numericsReentrantLockin AbstractChromaRuntimechroma_last_error)try-finallyfor ServerSession closefreeActionalways runs even ifstopActionfails; non-retriable to prevent double-freereadLastError()drain on success pathschroma_get_last_errorborrows but never clears the error slotTest plan
gradle :core:test)make test-javapasses (JNA + Panama smoke tests)make test-allpasses (Go + Rust + Java)