ADL v0.3 · Guide

ADL v0.3.1 Release Notes

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-side simplify for 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-typescript now 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 / geojson field type (#43). GeoJSON values with optional geometryType and srid (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 via ST_Simplify for wide-zoom rendering.

Infra: docker-compose.yml now ships postgis/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 a reversible flag.
  • POST /api/v1/domains/{name}/rollback (admin) — replays stored down-migrations newest-first (in a transaction), restores the prior model, logs rolled_back; optional target_version rolls 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 the update permission. Idempotent reference-data reloads.
  • Fixed a latent bug where the bulk response count was always 0.

Authentication: roles to the client — #56 (authd 1.2.0)

Clients can now learn a user’s roles instead of probing with 403s:

  • a roles claim in the access-token JWT;
  • data.roles + data.user.roles in /auth/login (and /auth/register);
  • a roles field in GET /auth/me;
  • /auth/refresh returns 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.json and the per-domain …/{name}/openapi.json are now served as bare OpenAPI documents (not inside the {status,code,data} envelope), so openapi-typescript/Swagger UI/Redoc consume them directly. A new _raw_json response marker enables this; the built-in Swagger UI at /api/v1/docs now 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 openapi produced KalosError.data: null (invalid) and transition paths missing the /transitions/ segment. Both fixed.
  • Predictable pluralization + discoverability (#58). -is → -es plural fix (MissionAnalysis → mission-analyses), an explicit api: { collection: … } override, and collectionPath/collectionSegment published in /schema.
  • CLI --config positional (#63). kalosd config.json now 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.
  • /source returns the authored document (#29). Previously a canonical re-serialization (comments/order/composition lost); now byte-identical to the submitted file, with ?format=resolved for the expanded form.
  • Parse errors return 422 (#30). Malformed-ADL submit returned 400; now 422 PARSE_ERROR, matching the spec and the other handlers.
  • Lua resource hooks fire (#31). A declared beforeCreate hook now runs and can merge fields.
  • Public view permissions honored (#32). views/{name} with ["*"] no longer returns 403.
  • Localization in responses (#33). Accept-Language is reflected, with _meta.locale on responses.
  • Notifications (#37, #45, #46). Declared notifications emit and the /notifications/log endpoint exists; domain-level notifications: blocks are parsed (resource: selector); the webhook channel does a real HTTP POST and the dispatch log reports honest sent/logged/failed status.
  • 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, with verify-poc-14.py as 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 /schema introspection (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.json and …/{name}/openapi.json are now unwrapped (bare OpenAPI document, no {status,code,data} envelope). Clients that unwrapped .data should read the document directly. (#62)
    • ADL parse failures on POST /domains now return 422 (was 400). (#30)
    • GET /domains/{name}/source now returns the verbatim authored document by default; use ?format=resolved for the old expanded form. (#29)
    • POST /domains and …/preview now 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 uses postgis/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 spatial geometry/geojson type 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.1 in 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.