Date: 2026-06-24
Platform version: 0.3.1 · Display label: ADL v0.3
This release hardens the v0.3 runtime against a full conformance sweep and adds native GIS / spatial support. It is the result of driving every ADL v0.3 subsystem end-to-end (via the new conformance POC) and a real external application build (MDMP), filing and fixing each gap.
Component versions: core 0.3.1 · ADL plugin 0.3.1 · authd 1.2.0.
26 issues fixed via merged PRs; 2 delivered/handed off. No breaking changes to authored ADL. New behavior is additive or fixes incorrect behavior; a few HTTP responses changed shape where they were previously wrong (noted under Upgrade notes).
Highlights
- Native geometry / GIS — a
geometry(GeoJSON) field type, PostGIS-backed storage with GiST indexing, and spatial query operators (bbox/intersects/within), plus viewport + level-of-detail + server-sidesimplifyfor large map layers. See the new Chapter 41 — GIS & Spatial Data. - Auto-migration history + rollback — migrations are recorded with their SQL, and a domain can be rolled back to a prior version.
- Scalable bulk ingest — higher cap, faster (single transaction, no per-row middleware), and idempotent upsert.
- Auth roles exposed to clients — JWT claim +
/auth/login+/auth/me, so UIs can render role-gated affordances without trial-and-error. - OpenAPI fixed end-to-end — valid schemas, correct transition paths, served
unwrapped, per-domain spec repaired;
openapi-typescriptnow consumes it cleanly. - Security — domain submit and migration preview now require admin auth.
GIS & Spatial (new) — #43, #53
A complete spatial stack, delivered in four steps and verified live against PostGIS 3.5. Full detail in Chapter 41.
geometry/geojsonfield type (#43). GeoJSON values with optionalgeometryTypeandsrid(default 4326), validated as well-formed GeoJSON, accepted as object or string and emitted as an object. Round-trips through/schema, export, and the stored model; gets a GeoJSON OpenAPI schema. Works on every backend (JSON storage).- PostGIS storage + GiST index (#43). On
store.postgres+postgis, each geometry field gets a generated<field>__geom geometry(<Type>,<srid>)column (derived from the GeoJSON) and a GiST index. The JSONB column stays the source of truth — CRUD and the wire contract are unchanged. A capability probe falls back to JSONB-only (with a warning) when the extension is absent. - Spatial query operators (#43).
filter[geom][bbox]=minLon,minLat,maxLon,maxLat,filter[geom][intersects]=<GeoJSON>,filter[geom][within]=<GeoJSON>on list and count. Misuse (non-geometry field, SQLite) →400 INVALID_QUERY. - Viewport + LOD + simplify (#53). Viewport =
bbox; LOD = a normal indexed integer (minZoom) + filter;?simplify=<tolerance>thins geometry viaST_Simplifyfor wide-zoom rendering.
Infra:
docker-compose.ymlnow shipspostgis/postgis:17-3.5.
Auto-migration: history & rollback — #60
Migration on spec reload was fire-and-forget. Now each migration records the applied up SQL, generated down SQL, and a snapshot of the prior model.
GET /api/v1/domains/{name}/migrations(admin) — history with up/down SQL and areversibleflag.POST /api/v1/domains/{name}/rollback(admin) — replays stored down-migrations newest-first (in a transaction), restores the prior model, logsrolled_back; optionaltarget_versionrolls back multiple steps.
The pre-existing destructive-change guard (422 DESTRUCTIVE_MIGRATION) and
preview endpoint are unchanged. Limitations: down-migrations are lossy
(dropped columns aren’t resurrected), only the model snapshot is restored (not
the verbatim /source), and resource add/remove isn’t fully route-reverted.
Bulk ingest — #54
POST /{resource}/bulk is now usable at scale:
- Item cap raised from a hard 100 to a configurable default 1000
(
adl.bulk.max_items). - Sub-operations dispatch internally — no per-row auth/RBAC/audit re-run (the bulk request’s own permission check still applies).
- Upsert:
{"upsert": true, "conflict": [...]}→INSERT … ON CONFLICT DO UPDATE(SQLite + PostgreSQL), defaulting to the PK; requires theupdatepermission. Idempotent reference-data reloads. - Fixed a latent bug where the bulk response
countwas always0.
Authentication: roles to the client — #56 (authd 1.2.0)
Clients can now learn a user’s roles instead of probing with 403s:
- a
rolesclaim in the access-token JWT; data.roles+data.user.rolesin/auth/login(and/auth/register);- a
rolesfield inGET /auth/me; /auth/refreshreturns current roles and mints a token carrying them.
Roles come from the same source as RBAC enforcement, so they always agree.
OpenAPI & developer experience
- Unwrapped spec (#62).
GET /openapi.jsonand the per-domain…/{name}/openapi.jsonare now served as bare OpenAPI documents (not inside the{status,code,data}envelope), soopenapi-typescript/Swagger UI/Redoc consume them directly. A new_raw_jsonresponse marker enables this; the built-in Swagger UI at/api/v1/docsnow loads. - Per-domain spec repaired (#66). It returned an empty/invalid spec (a
Request("server.openapi")aggregate bug zeroed the action; the schema filter dropped referenced schemas → dangling$refs). Both fixed; the spec is valid and complete. - Offline export schemas valid (#35, #36).
adl export openapiproducedKalosError.data: null(invalid) and transition paths missing the/transitions/segment. Both fixed. - Predictable pluralization + discoverability (#58).
-is → -esplural fix (MissionAnalysis → mission-analyses), an explicitapi: { collection: … }override, andcollectionPath/collectionSegmentpublished in/schema. - CLI
--configpositional (#63).kalosd config.jsonnow works (in addition to--config), with a clear error for a missing path. - Clear plugin-dependency error (#61). A missing plugin dependency now names the requiring plugin and the fix (load order is irrelevant — plugins are dependency-sorted).
Correctness fixes
- Domain submit / preview require auth (#28). Both were unauthenticated;
now admin-gated (
401/403). Read-only introspection stays public. /sourcereturns the authored document (#29). Previously a canonical re-serialization (comments/order/composition lost); now byte-identical to the submitted file, with?format=resolvedfor the expanded form.- Parse errors return
422(#30). Malformed-ADL submit returned400; now422 PARSE_ERROR, matching the spec and the other handlers. - Lua resource hooks fire (#31). A declared
beforeCreatehook now runs and can merge fields. - Public view permissions honored (#32).
views/{name}with["*"]no longer returns403. - Localization in responses (#33).
Accept-Languageis reflected, with_meta.localeon responses. - Notifications (#37, #45, #46). Declared notifications emit and the
/notifications/logendpoint exists; domain-levelnotifications:blocks are parsed (resource:selector); the webhook channel does a real HTTP POST and the dispatch log reports honestsent/logged/failedstatus. - Workflows fire (#38). A configured resource event now creates a workflow instance.
- AEL boolean comparisons (#39). Boolean filters match (type-coercing equality).
- Transition
sets:$user.*interpolation (#57).sets: { by: "$user.username" }now stores the caller’s identity, not the literal token (also$user.id/email/role/roles).
Conformance & tooling
- Every-feature conformance POC (#34).
pocs/poc-14-colony(Space Colony Sim) exercises every ADL subsystem end-to-end, withverify-poc-14.pyas a release gate. It surfaced and drove the fixes above (#31, #32, #33, #37, #38, #39, #45, #46). - Admin UI (#59). Handed off — being built externally against the rich
/schemaintrospection (now including roles,collectionPath, etc.). - Dev infra:
pocs/gen-certs.sh(mkcert-aware, browser-trusted dev TLS certs; gitignored),pocs/run-all-pg-tls.sh(start all POCs’ Postgres+TLS servers),pocs/login.sh(JWT login helper). All POCs have unique ports so they run concurrently.
Upgrade notes
- HTTP response shapes that changed (these were previously incorrect):
GET /openapi.jsonand…/{name}/openapi.jsonare now unwrapped (bare OpenAPI document, no{status,code,data}envelope). Clients that unwrapped.datashould read the document directly. (#62)- ADL parse failures on
POST /domainsnow return422(was400). (#30) GET /domains/{name}/sourcenow returns the verbatim authored document by default; use?format=resolvedfor the old expanded form. (#29)POST /domainsand…/previewnow require admin auth. (#28)
- GIS: to use spatial features on PostgreSQL, install the extension once per
database:
CREATE EXTENSION IF NOT EXISTS postgis;. The bundled compose stack usespostgis/postgis:17-3.5. - Bulk: the per-request item cap default rose to 1000; tune with
adl.bulk.max_items. - No changes are required to existing authored ADL documents.
Documentation
- New Chapter 41 — GIS & Spatial Data (the full spatial specification).
- Updated Complete Reference and LLM Authoring Spec: platform version
bumped to
0.3.1, the spatialgeometry/geojsontype added to the type system, and the list endpoint noted as accepting spatial filters +simplify. - Updated Chapter 7 — Type System and Chapter 16 — Query Parameters: cross-references to Chapter 41 for the geometry type and spatial filter grammar.
- Version-string examples refreshed to
0.3.1in Chapter 4 (REST Transport), Chapter 37 (OpenAPI Generation), and Chapter 38 (Admin Endpoints). - Per-component changelogs:
core/CHANGES/CHANGES_0.3.1.md,plugins/adl/CHANGES/CHANGES_0.3.1.md,plugins/authd/CHANGES/CHANGES_1.2.0.md.