test(java): jsonb open-bag runtime-write gate + api-contract harness (#98)#113
Merged
Conversation
…98) Complete the Java side of the jsonb-open-bag cross-port consistency work. Mandate 1 — OMDB runtime write path audit: OMDB has NO write-path gap for the open bag (`field.string @dbColumnType:jsonb`). Its write path correctly serializes + binds a JSON-text value via isOpenJsonbField -> serializeOpenJsonb -> bindJsonbParameter (Postgres setObject(.., Types.OTHER)). The persistence corpus never exercised this through the runtime write codec (asset-uuid seeds payload via raw SQL and only READS; roundtrip-all-types writes the *typed* @storage:jsonb path), so OpenJsonbWriteRoundtripTest fills the gap: it INSERTs an Asset whose payload is JSON text through ObjectManagerDB.createObject (NOT raw SQL) and reads it back parsed. The apparent "object is rejected" is NOT an OMDB write-path defect: OMDB's ValueObject model is statically typed by field subtype (ADR-0017), so a structured value assigned to a field.string is coerced to its java toString() at SET time (DataObjectBase._setObjectAttribute -> DataConverter.toType(STRING, ..)) — generic field-typing, identical for every string field, by design. (Verified empirically: a Map bound to the bag binds the malformed text "{a=1, b=2}" and Postgres rejects it with "invalid input syntax for type json".) The cross-port "write an OBJECT" half lives at the DTO/serialization boundary (the generated DTO is Object, #103): a consumer serializes the posted object to JSON text before it reaches OMDB's field.string, and the read side re-parses it. No OMDB production change was needed — hence the test() prefix. Mandate 2 — api-contract Java harness for the jsonb sub-corpus, mirroring the single-entity Author and m2m/tph harnesses, both lanes: - JsonbReferenceServer (hand-rolled JDK HttpServer + Postgres testcontainer). - GeneratedJsonbControllerHarness (codegen-spring DocumentController/DocumentDto compiled + booted over MockMvc behind the in-memory DocumentRepository seam). The generated DocumentDto types the bag as Object (#103), so a posted JSON object binds and reads back PARSED, never a JSON-encoded string. This LOCKS the API-boundary contract. Tests (Testcontainers Postgres, mvn -f integration-tests/pom.xml): - OpenJsonbWriteRoundtripTest 1/1 green - JsonbApiContractConformanceTest (reference) 1/1 green - JsonbGeneratedApiContractConformanceTest 1/1 green - existing ApiContract*/M2m*/Tph* 48/48 green (no regression) Deferred: Kotlin + C# api-contract jsonb harnesses (sibling worktrees); a cross-port persistence-conformance open-bag roundtrip scenario (would require all five ports' roundtrip writers, out of this Java-scoped change). Part of #98 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LuZWKnWzYGVnESijL7uuky
dmealing
force-pushed
the
fix/java-jsonb-runtime-write-98
branch
from
June 29, 2026 18:02
288e959 to
500a250
Compare
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.
Completes the Java side of the jsonb-open-bag (
field.string @dbColumnType:jsonb) cross-port consistency work. Two mandates.Mandate 1 — OMDB runtime write path: did it have the gap? No (well-evidenced).
The TS gap (PR #110 found) was a runtime write validator rejecting a posted object. OMDB has no analogous gap:
isOpenJsonbField→serializeOpenJsonb→bindJsonbParameter(PostgressetObject(.., Types.OTHER)).asset-uuid-roundtrip.yamlseedspayloadvia raw SQL and only READS;roundtrip-all-types.yamlwrites the typed@storage:jsonbpath (Settings), not the open@dbColumnType:jsonbbag.OpenJsonbWriteRoundtripTestfills that gate — it INSERTs anAssetwhosepayloadis JSON text throughObjectManagerDB.createObject(NOT raw SQL) and reads it back parsed.The "object is rejected" symptom is not an OMDB write-path defect. OMDB's
ValueObjectmodel is statically typed by field subtype (ADR-0017), so a structured value assigned to afield.stringis coerced to its javatoString()at set-time (DataObjectBase._setObjectAttribute→DataConverter.toType(STRING, …)), identical for every string field — by design. Verified empirically: aMapbound to the bag binds the malformed text{a=1, b=2}, which Postgres rejects withinvalid input syntax for type json. The cross-port "write an OBJECT" half lives at the DTO/serialization boundary (the generated DTO isObject, #103): a consumer serializes the posted object to JSON text before it reaches OMDB'sfield.string, and the read side re-parses it.No OMDB production change was needed — hence the
test()prefix. (GenericSQLDriveris byte-pristine; I prototyped awriteValueForraw-read fix, proved via empirical RED that set-time coercion defeats it forValueObject, and reverted it.)Mandate 2 — Java api-contract jsonb harness (both lanes)
Mirrors the single-entity
Author+m2m/tphharnesses for the newfixtures/api-contract-conformance/jsonb/corpus:JsonbReferenceServer(JDKHttpServer+ Postgres testcontainer,documentstable withpayload JSONB).GeneratedJsonbControllerHarness(codegen-springDocumentController/DocumentDtocompiled + booted over MockMvc behind the in-memoryDocumentRepositoryseam).The generated
DocumentDtotypes the bag asObject(#103), so a posted JSON object binds and reads back parsed, never a JSON-encoded string. This LOCKS the API-boundary contract.Test evidence (Testcontainers Postgres)
Deferred
op: roundtripscenario (would require all five ports' roundtrip writers — out of this Java-scoped change).Part of #98
🤖 Generated with Claude Code