V0.3.0
Pre-release
Pre-release
OntoBricks — Release Notes V0.3.0
Release window: May, 2026
Test status: all changes shipped with the suite green (2045 passing, 80 CloudFetch probe tests conditionally skipped in CI).
Highlights
- Cohort Discovery — new end-to-end feature for business-friendly entity grouping: rule-based linkage (shared resources via predicates), compatibility constraints, a 6-stage deterministic engine, full Volume + Lakebase persistence, graph-triple + Unity Catalog Delta materialisation, and a natural-language Stage 2 agent that translates free-text prompts into validated
CohortRuleJSON. - Live Digital Twin build log — the Build page now shows a real-time per-step log panel with elapsed timers, phase descriptions, a one-click export to
.log, and honest background-archive handling.TaskManagergainsskip_step/complete_current_stepso skipped phases are labelled correctly. - Real
/healthreadiness probe — 11 checks covering filesystem, Databricks auth, SQL warehouse, registry Volume read/write, registry UC DDL permissions, Lakebase schema/table/sequence grants, and CloudFetch capability./health/detailedretired. New admin Health tab in Settings surfaces the same payload in-app. - Mapping diagnostics: source table permissions — new third section runs a non-destructive
SELECT … LIMIT 0against every Unity Catalog table referenced by the mapping and reportsok/PERMISSION_DENIED/TABLE_OR_VIEW_NOT_FOUNDper table, surfacing missing grants before a build attempt. - Knowledge Graph — right-click Expand neighbours — any node can be expanded N hops in place without re-running a full SPARQL query. Non-blocking spinner, depth picker, camera zoom + highlight on new nodes. KG preview/expand also hardened against timeouts and 502 errors on large graphs.
- Deployment: single source of truth —
scripts/deploy.config.sh+app.yaml.templatereplace scattered literals acrossMakefile,deploy.sh, and bootstrap scripts.app.yamlis now a generated artifact. App nameontobricks-030unified across all tooling. - CloudFetch — runtime capability probe —
DatabricksAuthdetects at runtime whether the Apps sandbox can actually reach the CloudFetch storage host, setsuse_cloud_fetchaccordingly, and exposes the verdict in/health. A new Settings → Global toggle lets admins override the default. - Task duration & UTC timestamps —
TaskManageremitsSTART task/END tasklog lines with compact durations, serialisesduration_secondsinto_dict(), and uses UTC-aware ISO timestamps throughout to prevent timezone-drift bugs in the browser.
Cohort Discovery
Stage 1 — deterministic engine, UI, persistence, materialisation
- New
CohortRulemodel withvalidate(), CRUD endpoints, dry-run + materialise, and a 6-stage pure-Python engine (CohortBuilder) that works against both Delta/Spark SQL and LadybugDB/Cypher backends. - Materialise to graph (idempotent
DELETEthenINSERT, per-rule:inCohort<RuleId>predicate) and to Unity Catalog Delta (partitioned byrule_id, chunked INSERT). - Content-hash cohort URIs (
<base>/cohort/<rule_id>/c-sha256(…)[:8]). - Live preview helpers: class stats, edge count, node count, sample property values,
explain_membership(Why? / Why not?). - 59 new tests across
test_cohort_models.py,test_cohort_builder.py,test_dtwin_cohort.py. - New
docs/cohort_discovery.mdwith mental model, UX walkthrough, 4 worked examples, API summary.
Stage 2 — NL agent for rule generation
agents/agent_cohort/— six read-only tools (list_classes,list_properties_of,count_class_members,sample_values_of,propose_rule,dry_run) wired to Stage 1 endpoints.- One-shot agent loop with 10-iteration cap; never writes — saving still goes through the Builder-protected endpoint.
- UI: Describe tab (NL prompt + agent trace with tool calls, durations, iterations) auto-switches to Build rule tab when a rule is proposed.
- Fix:
list_properties_ofwas returning empty arrays whenrdfs:domainwas stored as a local name or property URIs were missing — resolved with_domain_matches+_ensure_urihelpers and a fallback to the full object-property list. - 22 new tests across
test_agent_cohort_tools.py(19) andtest_agent_cohort_engine.py(3).
Cohort designer — UX refinements
- Three-tab layout (Describe / Build rule / Preview) replaces the full-width drawer; the Preview tab carries a live cohort-count badge.
- Saved rules pane promoted to a persistent right/left rail, always reachable regardless of active tab.
- Dependent dropdowns in the "Link members" section:
viaproperty narrows to predicates whosedomainis the source class andrangeis the chosen shared class; falls back to the full list when ontology metadata is incomplete. - camelCase rule name enforcement: live input sanitisation, paste-to-camelCase,
_isValidRuleNamevalidator,_toCamelCasehelper applied to agent-generated names.id = label(no more slug fork). - Rule-scoped predicate and UC table: membership triples use
:inCohort<RuleId>; Auto-pick proposescohorts_<snake_rule_name>. Both the graph-triples hint and the UC-table hint in the Configure-outputs modal now show the actual predicate / table name for the active rule. - Clickable entity badge in the Preview pane: each cohort member renders as a pill that links to the Sigma graph focused on that node. URI is demoted to inline parenthetical muted text.
- Configure-outputs modal — visible feedback:
Auto-pickandTest write accesswere silently swallowing errors. Both are now four-state (idle → working → success | error) with inline status lines, spinner, toast, and error-envelope surfacing. - Clearer step labels in the designer: "Link members via a shared entity" (was "When are two members linked?"), "Conditions every member must satisfy" (was "Compatibility policies"). Terminology switched from "class" to "entity" throughout user-facing strings.
- Cohort explain fix — namespace drift:
CohortBuilder._members_of_classnow checks all URI variants (ontology form, data form, raw) soexplain_membershipworks regardless of which normalisation path the loader used. Enhanced "not in class" diagnostics report typed-as, untyped, or URI not found with actionable advice. - Rule summary card: removed the redundant
rule_idchip (equal tolabelfor camelCase names); fixed binary UC/graph output display to enumerate all four states (graph + UC / graph only / UC only / no outputs).
Digital Twin Build
- Live build log panel (
#syncBuildLogCard): appears on Build click, grows row-by-row with icon, description, live sub-message, and per-step elapsed timer. Step labels rewritten to plain English ("Preparing mappings…", "Detecting what changed since last build", etc.). TaskManager.skip_step()marks a stepskippedand advancescurrent_stepso the label array stays aligned with execution when phases are conditionally bypassed (e.g. Detecting what changed on first build, Checking source tables on forced full rebuild).- Export build log: one-click export to
digital-twin-build_<timestamp>.log(plain text with a header block, per-step table, and result block). Button enabled from the first poll onwards. - Fast gzip + background archive:
GraphSyncService.sync_to_volumenow usescompresslevel=1(≈ 6–10× faster than the previous level 9). For session builds the Volume upload runs in a daemon thread; the build task completes immediately after the snapshot step with the note "Registry backup continues in the background." - Archive checkbox: new "Archive graph to registry" checkbox on the Build page (default on); when off, the archive step is marked skipped with a plain-English reason.
- Honest timing: archive row shows "Continues after build" with a tooltip instead of a misleading 0ms duration when the upload is backgrounded.
- Background archive task tracked as a separate
registry_archiveTaskManagertask with its own navbar hourglass entry.
Task Manager & Notifications
Task.duration_seconds()— computed live for running/pending, frozen atcompleted_at − started_atfor terminal tasks; serialised into_dict().TaskManagerlifecycle log lines unified toSTART task <id> [<type>] — <name>/END task <id> [<type>] completed|failed|cancelled in <duration>._format_durationproduces compact strings:450ms,2.40s,1m 23.5s,1h 5m.- Navbar hourglass: running rows show a live 1 s ticking elapsed time; bell toasts append
(in 1m 23s). - All task/step timestamps emitted in UTC-aware ISO format (
+00:00);duration_seconds()tolerates mixed naive/aware legacy timestamps. - Removed the broken "Open" link from terminal task completion toasts.
Health & Observability
/health readiness probe (replaces static {"status":"healthy"})
11 probes, each timed and wrapped in _safely_run so one failure never breaks the overall response:
| Probe | What it checks |
|---|---|
runtime |
Python + OntoBricks version |
filesystem.tmp |
Write sentinel + shutil.disk_usage thresholds (warn < 1 GB, error < 100 MB) |
filesystem.session_dir |
Same check against the session directory |
filesystem.log_dir |
Same check against the log directory |
databricks.auth |
has_valid_auth + OAuth token mint in App mode |
databricks.warehouse |
SELECT 1 against the configured warehouse |
databricks.cloudfetch |
Real SQL probe with use_cloud_fetch=True; cached 5 min |
registry.cfg |
Resolved catalog / schema / volume |
registry.volume_read |
VolumeFileService.list_directory on the registry volume |
registry.volume_write |
Write + delete of a sentinel file via the Files API |
registry.uc_schema_ddl |
CREATE OR REPLACE VIEW … AS SELECT 1 + DROP VIEW |
lakebase |
init_status() with fine-grained warning/error mapping |
lakebase.permissions |
Schema USAGE/CREATE, table DML, sequence grants |
/health/detailedretired; its information is included in the unified response.- HTTP 200 is intentional even when
status: error— load-balancers should inspectsummary.errors.
Settings → Health tab
- Admin-only tab calls
GET /healthon open (lazy-load) and on Refresh. - KPI tiles (Total / Passed / Warnings / Errors) + sortable table (errors → warnings → ok) with status icon, label, name, detail (long details collapse into
<details>), and per-check duration badge.
Mapping Diagnostics — source table permissions
- New third accordion section Source table permissions probes every distinct UC table referenced by the mapping with
SELECT * FROM … LIMIT 0. - Classifies warehouse errors as
PERMISSION_DENIED,TABLE_OR_VIEW_NOT_FOUND,SCHEMA_NOT_FOUND,CATALOG_NOT_FOUND, or "Probe failed". - When no warehouse client is available (local dev without PAT), returns a
warningadvisory row so the section explains the gap rather than appearing broken. - Global KPI tiles at the top of Mapping Diagnostics now include permission check results.
- 23 new tests in
test_mapping_service.py.
Knowledge Graph
- Right-click Expand neighbours: new context-menu entry on any non-cluster node. Fetches the N-hop induced subgraph via
GET /dtwin/neighbors, de-duplicates against existing results, highlights newly-added nodes for 6 s, and frames the camera around them. Default depth reads the current slider value. - Non-blocking expand spinner (
#sgExpandSpinner): replaces the blocking "Expanding…" toast; positioned top-right over the canvas withpointer-events: none. - Depth slider default lowered from 3 to 2.
- KG preview / expand hardening:
_fetchWithTimeoutwithAbortControllerapplied to preview and expand requests — users see a clear timeout message instead of an indefinite spinner.find_seed_subjectsnow accepts an optionallimitso the API requests at mostmax_preview + 1subjects, avoiding unbounded scans.- In Apps mode: expansion depth capped to 3, max entities capped to 3000, triple fetch batched (250 subjects/batch), 40 s time budget returning partial results rather than failing.
- 502 error path now shows an actionable message ("reduce Depth / Max entities and retry").
_parseJsonResponsesafely handles non-JSON upstream errors that previously causedUnexpected token 'u'.
Deployment & Infrastructure
Single source of truth
scripts/deploy.config.sh(new): one file for app names, DAB target, warehouse ID, registry coords, Lakebase project/branch/database, andapp.yamlruntime fallbacks. Four sections;DEFAULT_*block at the top so changing a default is a one-line edit.app.yaml.template(new):app.yamlis now generated byscripts/_render-app-yaml.pyfrom the template and config.app.yamladded to.gitignore.deploy.shsourcesdeploy.config.shbefore doing anything; prints a config summary before validation; accepts--skip-app-yamland--no-bootstrap.Makefileslimmed: removed hard-codedAPP_NAMEvariable;deploy*targets just calldeploy.sh; newmake render-app-yamltarget.databricks.ymlapp name unified toontobricks-030(wasontobricks-020in the DAB resource butontobricks-030in the Makefile — now consistent everywhere).bootstrap-app-permissions.shandbootstrap-lakebase-perms.shreadAPP_NAME/MCP_APP_NAMEfrom env with documented override patterns;bootstrap-app-permissions.shautomatically grantsCAN_USEfrom the MCP SP to the main app.bootstrap-lakebase-perms.shgains--branchsupport for branch-accurate grant targeting.- Post-deploy resource-binding verification for both apps (warehouse, volume, postgres branch/database on lakebase targets).
Dependency / startup hardening
requirements.txtreduced touv+pydantic-settings>=2.1.0bootstrap +-e ".[lakebase]"editable install sopyproject.tomlremains the single source of Python dependencies.docs/and*.mdexcluded from Databricks bundle sync via.databricksignoreanddatabricks.ymlsync exclusions.- Credential helpers (
get_databricks_client,get_databricks_host_and_token,resolve_warehouse_id) now acceptdomain=Nonevia_domain_databricks()helper — fixes/healthwarehouse probe crash. - 4 new regression tests in
test_helpers.pyfor thedomain=Nonesafety contract.
Scripts cleanup
- Deleted orphaned
scripts/run_tests.shandscripts/test_UI.sh(superseded bymake test). - Deleted stale
tests/reports/artifacts (2026-02-25 snapshots when the suite was 306 tests).
CloudFetch
DatabricksAuthinitially disabled CloudFetch unconditionally in Apps mode to fix aConnection refusedcrash on the*.storage.cloud.databricks.comstorage host.- Replaced with a runtime capability probe (
probe_cloud_fetch_capability()) that issues a boundedSELECT 1withuse_cloud_fetch=Trueand caches the(capable, reason)outcome for ~5 minutes.can_use_cloud_fetch()reads the cached verdict;get_sql_connection_params()sets the flag accordingly. cloud_fetch_status(force=False)for diagnostic callers./healthprobedatabricks.cloudfetchcalls the real probe and surfaces the actual outcome.- New Settings → Global → "Enable CloudFetch for SQL queries" toggle persisted via
GlobalConfigService; propagated toDatabricksClientandDatabricksAuththrough a newresolve_use_cloud_fetchhelper. DATABRICKS_DISABLE_CLOUD_FETCH/DATABRICKS_FORCE_CLOUD_FETCHenv vars for CI isolation.
Lakebase connection resilience
- TCP keepalives added to
LakebaseAuth.kwargs()/conninfo():keepalives_idle=10,keepalives_interval=5,keepalives_count=3. Zombie sockets now surface in ~25 s instead of ~130 s. - Stale cache on Lakebase failure:
GlobalConfigService.load()now returns the previously-cached config (up to 30-minute stale window) instead of falling back to_empty()on a blip._CACHE_TTLbumped from 60 s → 300 s. - Cohort
preview()wrapped in a 60 sAbortControllerbudget with a clear "Preview timed out" message.
Documentation
docs/cohort_discovery.md(new) — mental model, 5-section UX walkthrough, output destinations, persistence in both registry backends, 6-stage algorithm summary, 4 worked examples, API summary, Stage 2 walkthrough.docs/deployment.md— updated fordeploy.config.sh,app.yaml.template, Lakebase--branchbootstrap, and the real/healthprobe payload.docs/development.md—TestHealthRoutesrow updated;/health/detailedretirement noted.docs/user-guide.md— "Right-click on a node (Expand neighbours)" section added.README.md— new bullet for cohort discovery + right-click expand.
Upgrade notes
- App name: the dev sandbox is now
ontobricks-030in all tooling (databricks.yml,deploy.config.sh,scripts/bootstrap-*.sh,Makefile). Update any scripts or docs that still referenceontobricks-020. app.yamlis generated: do not edit it directly — it is gitignored. Editapp.yaml.templateand runmake render-app-yaml(ormake deploy, which runs the renderer automatically). Commitapp.yaml.templateandscripts/deploy.config.shinstead.deploy.config.shrequired:scripts/deploy.shaborts if the config file is missing. Copyscripts/deploy.config.sh.exampleand fill in your workspace values before the first deploy on a new checkout.- Cohort predicate migration: graphs that hold the old global
<base>/inCohortmembership triples (v0.3.0-dev only — the feature was never deployed externally) will not be cleaned up by a re-materialise with the new rule-scoped predicate. RunDELETE FROM … WHERE predicate = '<base>/inCohort'manually before re-running Materialise. /health/detailedremoved: external probes that hit/health/detailedshould switch toGET /healthand inspectstatus+summary.errors. The route returns404.make test:scripts/run_tests.shandscripts/test_UI.share deleted — usemake testoruv run pytestdirectly.- Lakebase bootstrap:
bootstrap-lakebase-perms.shnow accepts--branch <branch>to target the correct Lakebase branch endpoint. PassLAKEBASE_BOOTSTRAP_BRANCHvia env or letdeploy.shhandle it automatically.