From 8cb09c87b78b18709f2fef6dbef1189f89975c56 Mon Sep 17 00:00:00 2001 From: Doug Mealing Date: Thu, 22 Oct 2026 20:27:31 -0400 Subject: [PATCH 1/2] fix: regen showcase for value-object null omission and gate all ports in the full CI run --- .../.metaobjects/.gen-state/.hashes.json | 6 ++-- .../csharp/SubscriberBlurbPayload.g.cs | 1 + .../java/acme/SubscriberBlurbPayload.java | 2 ++ .../kotlin/acme/SubscriberBlurbPayload.kt | 2 ++ .../generated/python/subscriber_router.py | 11 ++++++-- scripts/ci-local.sh | 28 +++++++++++++++++-- 6 files changed, 43 insertions(+), 7 deletions(-) diff --git a/examples/showcase/.metaobjects/.gen-state/.hashes.json b/examples/showcase/.metaobjects/.gen-state/.hashes.json index a0e94cbd4..a1f97cfa9 100644 --- a/examples/showcase/.metaobjects/.gen-state/.hashes.json +++ b/examples/showcase/.metaobjects/.gen-state/.hashes.json @@ -1,16 +1,16 @@ { "generated/csharp/AppDbContext.g.cs": "314c6078030ba151687430b37e6b6ea936238bb5df02df238e4acce2f95a430d", "generated/csharp/Subscriber.g.cs": "3ca160941378fbb8b6ad6acf080ff85573f463931e21b8756873a4476e5dd163", - "generated/csharp/SubscriberBlurbPayload.g.cs": "6b1ce5b5a4018c647586d86125718c4764ae5d6eeb94e9545e8006a7ec2b2b6f", + "generated/csharp/SubscriberBlurbPayload.g.cs": "30ae31959c0cd98ba4ab12b2e827fa48f3bf59a05aa34974ebc4a1fe930378be", "generated/csharp/SubscriberFilterAllowlist.g.cs": "7428d47f6ccb64bb315266ee0564fa3803736623a34ab491435c042848c5d472", "generated/csharp/SubscriberNames.g.cs": "91f6eba78657b31fceef7021c6ca6bd799524dcb5120acbc8fcbe18786787174", - "generated/csharp/SubscriberRoutes.g.cs": "a14815f7870e309102d57a0227555191b3f32a4012e04fb213ef17abe12ab3f4", + "generated/csharp/SubscriberRoutes.g.cs": "e98387adf0d9fd5c2743409e5820d72400373ca6a758176cba76112fa1efc02a", "generated/python/Subscriber.py": "9dd4c0acea47f99d1daf79930f9de2e400f30780ebce6c63f023be528a72b762", "generated/python/SubscriberBlurbPayload.py": "a5ef8a08e63efbe0f73e39a21a368cff5150c68cc8beeea6e8aab0c755f8bbdd", "generated/python/__init__.py": "daa04cec586768866d0996c779f62073e6773fe63750ff668327991fac2fd0f5", "generated/python/subscriber_filter_allowlist.py": "b0414cc95ff3982c80a0e1a06839f090c0b423e41f5a94275df93426cea09df5", "generated/python/subscriber_names.py": "269637a02bf9b46f55658b58cb42f83196c03a877e1e2101337847fd406c1561", - "generated/python/subscriber_router.py": "734f9c91aa1ea475ac8014e9f5db47fe54c8f5e35a25d1ae64c1dc3276ad2546", + "generated/python/subscriber_router.py": "9c2393da6c055bd7a76b81cbc1492fccc44bae257cab5755830ab58f59f522c7", "generated/ts/Subscriber.queries.ts": "9f1a459c57fafc98bf074b4443b506d4a1e7023ed188f293021ae89c298d4562", "generated/ts/Subscriber.routes.ts": "147d32ae658f985e024c3de6e8894ca7359f767eeaa765cec70756802e3bd300", "generated/ts/Subscriber.ts": "a78c17f915222ee108290c28468313d77f77b5cff438f485324409302e389a96", diff --git a/examples/showcase/generated/csharp/SubscriberBlurbPayload.g.cs b/examples/showcase/generated/csharp/SubscriberBlurbPayload.g.cs index 33d8e9964..a8ce3bcde 100644 --- a/examples/showcase/generated/csharp/SubscriberBlurbPayload.g.cs +++ b/examples/showcase/generated/csharp/SubscriberBlurbPayload.g.cs @@ -13,6 +13,7 @@ public class SubscriberBlurbPayload { public enum SubscriberBlurbPayloadStatus { active, paused, cancelled } [JsonPropertyName("name")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? Name { get; set; } [JsonPropertyName("status")] public SubscriberBlurbPayloadStatus Status { get; set; } diff --git a/examples/showcase/generated/java/acme/SubscriberBlurbPayload.java b/examples/showcase/generated/java/acme/SubscriberBlurbPayload.java index b0e6be9ee..d436bc252 100644 --- a/examples/showcase/generated/java/acme/SubscriberBlurbPayload.java +++ b/examples/showcase/generated/java/acme/SubscriberBlurbPayload.java @@ -1,8 +1,10 @@ package acme; import jakarta.validation.constraints.*; +import com.fasterxml.jackson.annotation.JsonInclude; /** GENERATED — value object SubscriberBlurbPayload. Do not hand-edit; regenerated from metadata. */ +@JsonInclude(JsonInclude.Include.NON_NULL) public record SubscriberBlurbPayload( String name, @NotNull Status status diff --git a/examples/showcase/generated/kotlin/acme/SubscriberBlurbPayload.kt b/examples/showcase/generated/kotlin/acme/SubscriberBlurbPayload.kt index acb1c79b0..503d969fd 100644 --- a/examples/showcase/generated/kotlin/acme/SubscriberBlurbPayload.kt +++ b/examples/showcase/generated/kotlin/acme/SubscriberBlurbPayload.kt @@ -1,5 +1,6 @@ package acme +import com.fasterxml.jackson.`annotation`.JsonInclude import jakarta.validation.constraints.NotNull import kotlin.String import kotlin.jvm.JvmStatic @@ -7,6 +8,7 @@ import kotlin.jvm.JvmStatic /** * GENERATED — do not hand-edit. Regenerated from metadata. */ +@JsonInclude(JsonInclude.Include.NON_NULL) public data class SubscriberBlurbPayload( public val name: String? = null, @field:NotNull diff --git a/examples/showcase/generated/python/subscriber_router.py b/examples/showcase/generated/python/subscriber_router.py index fab10e0c5..e25715a76 100644 --- a/examples/showcase/generated/python/subscriber_router.py +++ b/examples/showcase/generated/python/subscriber_router.py @@ -75,6 +75,9 @@ class _SortClause(BaseModel): _DECIMAL_LIMITS: dict[str, tuple[int, int]] = {} +_NESTED_DECIMALS: dict[str, Any] = {} + + async def _json_body(request: Request) -> dict[str, Any] | None: """GENERATED — the request body as a JSON object (non-integer numbers are exact `Decimal`s), or None.""" try: @@ -182,7 +185,9 @@ def create_subscriber( ): # the body was not a JSON object: the cross-port 400, not FastAPI's 422 return JSONResponse(status_code=400, content={"error": "validation"}) try: - dto = coerce_decimal_fields(dto, _DECIMAL_FIELDS, _DECIMAL_LIMITS) + dto = coerce_decimal_fields( + dto, _DECIMAL_FIELDS, _DECIMAL_LIMITS, _NESTED_DECIMALS + ) except ValueError: return JSONResponse(status_code=400, content={"error": "validation"}) # FR-036: run the generated field constraints (length / pattern / @@ -215,7 +220,9 @@ def update_subscriber( ): # the body was not a JSON object: the cross-port 400, not FastAPI's 422 return JSONResponse(status_code=400, content={"error": "validation"}) try: - dto = coerce_decimal_fields(dto, _DECIMAL_FIELDS, _DECIMAL_LIMITS) + dto = coerce_decimal_fields( + dto, _DECIMAL_FIELDS, _DECIMAL_LIMITS, _NESTED_DECIMALS + ) except ValueError: return JSONResponse(status_code=400, content={"error": "validation"}) # FR-035 PATCH-2: an explicit null on a @required field is a 400 — diff --git a/scripts/ci-local.sh b/scripts/ci-local.sh index dd59f9e4a..297a37da2 100755 --- a/scripts/ci-local.sh +++ b/scripts/ci-local.sh @@ -399,10 +399,22 @@ gate_no_api_prefix() { bun scripts/check-no-api-prefix.ts; } # # Deliberately does NOT run the other four ports: that shells out to mvn/dotnet/uv, # and this lane is `step_if bun` — guarded on bun alone and included in `--quick`, the -# documented pre-PR command. The release preflight runs `--check --all-ports`, which -# refuses to leave a port out. +# documented pre-PR command. The other four ports are checked by `gate_showcase_all_ports` +# in the heavy tier of the flagless run, and again by the release preflight +# (`--check --all-ports`); both refuse to leave a port out. gate_site_payload() { bun_install && bun test scripts/site && bun scripts/build-site-payload.ts --check; } +# ── the showcase is current in EVERY port, not just ts+sql ──────────────────── +# `gate_site_payload` can only afford `--bun-only`, so a change to the python, csharp, +# java or kotlin emitter left the committed showcase stale while the full run stayed +# green, and the first thing to notice was the release preflight (`--check --all-ports`) +# refusing the release cut (#447/#448 value-object null omission). No single CI lane has +# mvn, dotnet and uv together, so this runs in the flagless full run only, which is the +# one place all three coexist; it is absent from `--quick` (bun-only by design) and from +# every `--only` lane. It needs the workspace built (`gate_ts_build_typecheck` earlier in +# the same run) for the reason given above `gate_site_payload`. +gate_showcase_all_ports() { bun_install && bun scripts/regen-showcase.ts --check --all-ports; } + # ── the published metamodel reference tracks the registry ───────────────────── # site-reference/ is rendered HTML the site COPIES at deploy — the deploy has no install # step for this repo, so the rendering happens here and the release tag carries the pages. @@ -924,6 +936,18 @@ else if want python; then step_if uv "conformance: python" gate_conf_python; fi if want_any java java-fast; then step_if mvn "conformance: kotlin" gate_conf_kotlin; fi if want_any java java-slow; then step_if mvn "java-reactor (install)" gate_java_reactor; fi + # Needs all three toolchains at once, which no single --only lane has: flagless run only. + if [ -z "$ONLY" ] && [ "$INTEG_ONLY" -eq 0 ]; then + if have mvn && have dotnet && have uv; then + step "showcase current in all ports" gate_showcase_all_ports + elif [ "$STRICT" -eq 1 ]; then + echo ""; echo " ✖ showcase current in all ports — needs mvn, dotnet and uv (strict-toolchains)" >&2 + FAIL+=("showcase current in all ports (missing toolchain, strict)") + else + echo ""; echo "── ⊘ showcase current in all ports — SKIPPED (needs mvn, dotnet and uv) ──" >&2 + SKIP+=("showcase current in all ports (needs mvn+dotnet+uv)") + fi + fi # Docker integration — full suite when no --only, per-port otherwise. # --no-integration drops this whole half; --integration-only keeps ONLY it, which is # why it asks in_lane / in_lane_any (selection) where the checks above ask want. From a4c9f8c535b67c95474fd9e69408241789c5a927 Mon Sep 17 00:00:00 2001 From: Doug Mealing Date: Thu, 22 Oct 2026 22:05:13 -0400 Subject: [PATCH 2/2] no-mistakes(document): Updated CONTRIBUTING.md full-run lane list for the new all-ports showcase gate. Left AGENTS.md unchanged: it omits the new gate but is not wrong, and it is a human-owned memory file. --- CONTRIBUTING.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f6bab75b8..5e8888de5 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -83,7 +83,8 @@ a PR, so nothing red leaves your machine: ```bash scripts/ci-local.sh # full parity: hygiene.yml's leak-scan, all-port # conformance, the java reactor, the drift/mutation - # gates, + integration-tests.yml's docker suite + # gates, the showcase current in all five ports, + + # integration-tests.yml's docker suite scripts/ci-local.sh --quick # hygiene.yml in full + the TypeScript half of # conformance.yml; skips the C#/Java/Kotlin/Python # ports, the java reactor and the docker suite