New in the 0.4.0-SNAPSHOT development line of the Vidocq Runtime — everything listed here landed after the 0.3.0 release and is not part of it. Every entry links to the section that documents it, and those sections carry the NEW badge. Each brick of the suite keeps its own page of this kind; the ecosystem index gathers them. When the next release train ships, this page is frozen with it and restarts empty on the dev line.

Runtime

  • Startup banner with the exact Vidocq version — every boot tells which Vidocq starts, once per JVM, before anything later can fail: Vidocq 0.4.0-SNAPSHOT (9beafc47+dirty, built 2026-09-17T14:02:11Z), then the Java version, the profile or dev launch, and the application. A terminal, a dev profile or IntelliJ’s Run console get the art on standard output; containers, CI, pipes and tests get one log line. vidocq.banner.mode=auto|console|log|off, a custom vidocq-banner.txt with placeholders, and VidocqBootstrap.banner(BannerMode.OFF) for embedders. vidocq dev now starts its spinner after the banner. Startup banner.

  • The launch mode, and the debugger, in the startup identity — every boot says which of dev, test and prod it is, with the signal it was read from: Java 25+36-LTS | dev (IntelliJ agent) | debug *:5005 | mcp-time-server 0.1.0-SNAPSHOT. prod never shows without (no dev or test signal), since nothing proves it. A JVM started with -agentlib:jdwp=… shows the address a debugger has to attach to, in every mode, and repeats it on a Vidocq debugger: attach to … record when the 80 columns of the banner could not hold it. Neither the mode nor the address is ever lost to the JVM vendor, which the line gives up first. vidocq.launch.mode=dev|test|prod forces the mode; it changes what is printed, and the runtime itself behaves the same in every mode. Launch mode.

  • Build identity in every jar — vidocq-parent writes META-INF/vidocq/build-info/<artifactId>.properties (version, abbreviated commit, dirty flag, commit time, and build time outside releases, which stay reproducible), so two snapshots with the same version are told apart. Build identity.

  • Every Java 25 main shape starts the application — when Vidocq picks the main itself, it now picks the one java would: main(String[]) and main(), static or an instance method, public or not, declared in the class, inherited from a superclass, or a default method of an implemented interface. The selection mirrors the launcher’s, down to the parts nobody guesses — a declared main ends the search even when it cannot be used, so a private main(String[]) sends the launch to main(). An IDE on JDK 25 flags the public of a main as an unnecessary modifier: following that advice no longer makes vidocq:run fail. A private or non-void main is named as such, with the class that declares it, instead of being reported as a missing main (Vidocq/vidocq#88). Which main methods are accepted.

  • Readable console logs by default — when the application has not configured logging, Vidocq replaces the JDK’s two-line records on standard error (red in an IDE) with one aligned line per record on standard output: [LEVEL][timestamp][thread][class#method] : message, with a coloured level on a terminal or in IntelliJ’s Run console. Any logging setup of the application wins (logging.properties, an SLF4J or Log4j bridge, vidocq.log.console=jdk); vidocq.console.color and NO_COLOR control the colours. The examples no longer carry their own copies of a stdout handler and formatter. Console logging.

  • A startup report at the end of every boot — one INFO record, just before Vidocq - Started in, says what has just started: the launch mode and the signal it was read from, the application layer and its modules, the configuration sources and the audited namespaces (never a value), the extensions and the time of each onStart, and the section of every library that contributes one. One line per section by default, everything on the first boot of a dev launch, nothing for an embedded deployment such as the TCK; vidocq.startup.report=auto|off|summary|detailed chooses. Every anomaly is its own WARNING record, its code first, logged even with the report off: VIDOCQ-CFG-001 for an invalid vidocq.startup.report or vidocq.launch.mode, VIDOCQ-CFG-003 for a key read by nothing — vidocq.startup.reprot included, and vidocq doctor now knows the vidocq.startup.* keys — VAUBAN-009 for load-time weaving that could not be prepared, VIDOCQ-RPT-001 and VIDOCQ-RPT-002 for a contributor that fails or takes an id already used. A boot that fails logs one warning naming what was running, the time spent and the codes already logged, with the partial report in a dev launch, then lets the same exception through, untouched. A library adds its section with StartupReportContributor, in the new package io.vidocq.runtime.spi.report of vidocq-runtime-spi: an extension only implements it, any other library declares it as a service, and nothing needs vidocq-runtime-core. Extensions read the launch mode with ExtensionContext.launchMode(). Startup report, Contributing to the startup report.

  • Chappe listeners say where they really listen, and an extension can bring its own — every listener now logs the address it bound, so port=0 prints the real port and an IPv6 address is bracketed. An extension declares a listener of its own with ChappeMountPoint.declareListener(listener, options): it may fall back to a free port when its port is taken, log quietly, stop with a short grace period, and is handed the address it bound. vidocq.chappe.listeners can no longer list such a listener, nor silently replace it. A listener of an extension’s own.

  • The dev console — run mvn vidocq:dev, which adds the console itself (see the dev tools entry below): Vidocq dev console: http://127.0.0.1:8888/ opens a read-only page, on a loopback listener of its own, with the startup report of the running boot, its anomalies first, then one tab per extension, and the JVM last. The Mansart pools show live: the active, idle and waiting connections of each pool, borrows and timeouts per second, leaks, the mean borrow time. It is on in a dev launch only (vidocq.devconsole.enabled=auto|true|false), listens on vidocq.devconsole.host and vidocq.devconsole.port, 0 for a free port that the dev reloads keep, and the banner promises it: devconsole :8888. A port already taken never stops the application: the console takes a free one and says so loudly, in a boxed warning, as VIDOCQ-DEVC-004 and on the page. It answers only a request that names its own address, never shows a secret, a JVM argument or a system property, and warns when it is on outside development or off the loopback address (VIDOCQ-DEVC-001 to VIDOCQ-DEVC-005). An extension shows its section of the startup report live by implementing DevConsolePanel, from the new vidocq-runtime-devconsole-spi, in its own runtime extension: boot facts once, values sampled from memory on every poll, and the charts to draw. ExtensionContext.startupReport() gives an extension the report of its boot. The Mansart pool extension’s section also names its pools, their sizes and checks, and raises MANSART-POOL-001 for a minIdle pre-fill that opened nothing and MANSART-POOL-002 for a named pool that no @Named datasource serves. Dev console, Writing a dev console panel.

  • A langchain4j-cdi MCP server in one dependency — vidocq-runtime-langchain4j-cdi-mcp-extension brings langchain4j-cdi’s MCP server, its CDI 4.1 invoker, the Jakarta REST runtime and the server’s bean list: an application drops its two langchain4j-cdi dependencies, parsson, yasson and scanDependencies, and an IDE launch finds /mcp without a Maven step. The server stays in the boot layer in every launch shape, the application layer holds the application alone, and nine vidocq.mcp.* keys configure it. The startup report gets an mcp section (tools, prompts, resources, protocols, configuration, never the request-state secret), and five codes, VIDOCQ-MCP-001 to VIDOCQ-MCP-005, name what the extension can see going wrong. Built in the reactor and not on Maven Central while langchain4j-cdi 1.4.0 is a SNAPSHOT (Vidocq/vidocq#94). langchain4j-cdi MCP server.

  • The mcp section of the startup report is live in the dev console — vidocq-runtime-langchain4j-cdi-mcp-extension now implements DevConsolePanel too: besides the boot facts, which stay, the mcp panel samples seven values about once a second — the sessions the server holds, the SSE streams and the subscriptions/listen streams open, the requests it is still waiting a client’s answer of, and the three counters of the CDI 4.1 invoker — and plots Connections and Server-to-client requests. invoker.matches and invoker.misses count distinct methods, not calls: a server of four MCP methods reads 4 after six calls and still 4 after six hundred, which is why no chart plots them. The panel never creates a bean — it asks each bean’s Context for the instance it already has, since creating McpSessionManager would start its mcp-session-cleanup thread, once a second, on an application that has served no request — and a bean with no instance is shown absent with its reason, no request served yet, not created yet, reflection or built at the first call, never a zero that would claim a measure the panel does not have (Vidocq/vidocq#94). The mcp panel in the dev console.

  • An MCP inspector in the dev console — in a dev launch, the mcp tab lists the application’s MCP tools, prompts and resources and calls them through its own /mcp: a Monitoring sub-tab and one per kind, a list to pick the item, a form from its input schema, the result in a block of its own through a JSON viewer that colours and folds it, the exact JSON-RPC exchange, the kind’s last calls with a replay, secrets masked. Built on actions that now take a JSON argument and return a structured result (ADR 0001, amendment 1). See MCP inspector, JSON viewer and a JSON argument.

  • The dev console’s curves survive a hidden tab — the five minutes of history behind every chart moved from the page to the server, where a thread of the console’s own, vidocq-devconsole-history, fills it once a second for the life of the boot, whether or not a page is open. The page used to keep it, one point per poll, and a browser stops the timers of a hidden tab — on macOS, Chrome counts a window another application covers as hidden — so every curve had a hole covering exactly the minutes you had left the console to go and cause something, which is the interval you came back to look at. A poll now asks for what it is missing, GET /api/snapshot?since=<its newest point>, and a tab that comes back after three minutes away redraws a complete curve in one document. The holes that are true are kept: a tick where a panel wrote absent keeps its place on the axis with a null value, and a tick the server itself missed — a long pause, a suspended process, a laptop that slept — leaves no point and breaks the curve, as before. The ring is bounded and the bound is a number: 256 series of 300 points, measured full at 1,966,960 bytes, 1.88 MB, whatever the panels do and however long the run; a series arriving past the cap keeps its live value and gets no curve, and the snapshot says historyTruncated (Vidocq/vidocq#107). Where the history lives.

  • The startup report lists the REST routes — vidocq-runtime-cassini-rest-extension writes a rest section: the resource classes, routes and providers it mounted and where, then, in the detailed report, every route in match order with the class and method that handles it, each mount, and the @Provider classes grouped by contract. It covers the automatic mount and every declarative type=restful mount. The section reads the route table Cassini now publishes, CassiniStack.routes(), and creates nothing (Vidocq/vidocq#103, Vidocq/cassini#41). The rest section of the startup report.

  • The startup report prints absolute URLs — a new http section declares every listener of the configuration with the address it really bound, the real port when the configured one was 0, so the routes of the rest section read http://localhost:8081/api/tasks instead of /api/tasks, and the mcp section prints the URL of /mcp once, without the internal /mcp/_listen route. Extensions read the address with ChappeMountPoint.boundUrl(name) (Vidocq/vidocq#111). The http section.

  • A metrics panel for Dirac — vidocq-runtime-dirac-metrics-extension now writes a metrics section, Metrics (Dirac), shown live in the dev console: one card per registry, application first, with how many counters, timers, histograms and gauges it holds, every counter’s count, and every timer’s count and mean duration. Each metric gets a key derived from its name and tags, and the boot facts list every key next to the metric it stands for. The panel reads the registries of the producer the application already has and never creates it; it never calls a gauge, which is application code, and never takes a timer’s snapshot, which copies its reservoir (Vidocq/vidocq#115). Dirac metrics.

  • A CDI panel in the dev console — the console now has a second panel of its own, CDI (Vauban), between the extensions' panels and the JVM: how many beans the container holds, by scope, its interceptors and its observer methods, and three tables — the beans with their kind, scope, qualifiers and whether they are alternatives, the interceptors with their bindings and priority, the observers with their event type and qualifiers — the application’s rows first. It reads Vauban’s metadata once at boot and creates no bean; Vauban implements CDI Lite, so there are no decorators to list (Vidocq/vidocq#117). Each row also says which code generator covers it — APT, Class-File, partial, reflection — and what falls back to reflection, and every class outside the application is a library, not runtime. The CDI panel.

  • A configuration panel in the dev console — the console has a third panel of its own, Configuration, before the CDI container: every vidocq. and mp. key and every key of the application’s own sources, with the source that wins and its ordinal — the very source getValue reads, so a -D property that overrides the application’s file shows as such — its value in a dev launch, and whether the core or an extension reads it or the key audit found it unused (VIDOCQ-CFG-003). A key that names a secret, such as db.adminPassword or vidocq.mcp.requestStateSecret, reads configured; a URL loses its user:password@ and its password= parameter; outside dev, no value is shown. Everything is masked in the application’s JVM, before the page gets it (Vidocq/vidocq#116). The configuration panel.

  • A health panel for Knock — vidocq-runtime-knock-health-extension, until now a wrapper with no Java class, writes a health section, Health (Knock), shown live in the dev console: the checks of each probe, and per check its last status, when it answered, the name and data of its response, never called until a probe request runs it, with the checks UP and DOWN charted per probe and a button to /health. A health check is application code and may do I/O, so the console never calls one: Knock now remembers the last answer of each check when /health, /health/live, /health/ready or /health/started runs it, and a check that turns DOWN shows DOWN at the next probe request (Vidocq/vidocq#114). Knock health.

  • Actions in the dev console, behind an origin check and a per-boot token — in a dev launch, a panel can now offer to do something from the page: DevConsolePanel.actions() lists PanelAction`s, each a button with its string arguments, a list of values or a pattern the console checks, an optional inline confirmation, and a function that returns one line. The console runs one only for its own page: `POST /api/action/<panel>/<action>, application/json, an Origin that is the console’s own and the X-Vidocq-Console-Token of the boot, read from the snapshot; anything else is refused before it runs, and a cross-site attempt is logged as VIDOCQ-DEVC-006. One action at a time per panel, on a virtual thread, logged at INFO; a failure shows its exception class, never its message. Outside dev the console stays read-only. No panel offers one yet: the logs and migration panels will (Vidocq/vidocq#118, ADR 0001). Actions, declaring an action.

  • A logs panel in the dev console, and a log level set without a restart — in a dev launch the console has a third panel of its own, Logs: the last 500 records kept in memory by a handler on the root logger, newest first, without the credentials of a URL and never with a stack trace; the WARNING and SEVERE records since the boot as counters drawn per second; the loggers whose level is set; and a Set level button, the first action of the console, that sets a logger’s level until the next dev reload, which puts it back. When an SLF4J or Log4j bridge takes System.Logger, the panel says it cannot see the records instead of showing an empty table. logs joins the ids reserved for the console (Vidocq/vidocq#119). The logs panel.

  • Migrate, or clean and migrate, from the dev console — the migration section of vidocq-runtime-migration-extension is now a live panel: per datasource, the version its schema is at, what the last run applied, and in a dev launch the migrations applied and pending, as Flyway’s info or Liquibase’s changelog table give them. Two actions come with it in a dev launch: Migrate now applies the migrations added since the boot without a restart, and Clean and migrate drops every object of the schema and migrates it again, behind a confirmation, refused with nothing dropped unless vidocq.migration[.<name>].cleanDisabled=false. SchemaMigrator gains info and clean, default methods that a backend may leave unsupported. The datasource’s password reaches neither the page nor the log (Vidocq/vidocq#120). The migration panel and its actions.

  • Dev MCP: the dev console as read-only tools for coding agents — in a dev launch the console is also an MCP server, at POST /mcp on its own listener, so a coding agent asks the running application what it serves: claude mcp add --transport http vidocq-dev http://127.0.0.1:8888/mcp. Five tools, vidocq_report, vidocq_panels, vidocq_panel, vidocq_anomalies and vidocq_routes, answer from the very document the page polls, without the token of the boot or the actions, and change nothing. The console speaks the part of Streamable HTTP they need, JSON only and without sessions, with no new dependency, whether or not the application uses MCP; the Host, Origin, content type and Accept are checked as for an action, but no token is needed. Its URL is printed after the console’s (Vidocq/vidocq#121). Dev MCP.

  • Every dev services jar is a named module — vidocq-runtime-devservices-spi, -host, -junit, vidocq-runtime-devservice-postgres and -keycloak used to ship without a module-info.class; each is now a named module, its providers declared with provides (Vidocq/vidocq#177). The PostgreSQL provider needs Testcontainers' core only: it configures a plain container, as Testcontainers' postgresql module did, and gives the same JDBC URL. Every pull request now checks that no jar the build publishes is an automatic module (check-named-modules.sh). Every dev services jar is a named module.

  • A query editor for JDQL in the dev console — the statement of the Mansart Data panel’s JDQL tab, in Query, Update / Delete and Export CSV, is typed in the dev console’s code editor in a new query mode: keywords, functions, the entity, its attributes, strings, numbers and parameters each coloured; Ctrl+Space completes the entities after FROM, their attributes with their Java type and column in SELECT, WHERE, ORDER BY and SET — FROM written after the caret included — and a reference’s attributes after project.; an unknown entity or attribute, a path through an attribute that is no reference, an unterminated string or an unbalanced parenthesis is underlined before anything runs; Format puts each clause on its line. Its params become a JSON editor whose schema follows the statement as you type: WHERE status = :status offers status with the values of its enum and flags it missing. A panel publishes such a vocabulary with DevConsolePanel.languages() (or LivePanel’s), `PanelLanguage records the page fetches once at GET /api/language/<panel>/<id> in a dev launch, and names it from a json argument’s schema with "contentMediaType": "text/x-query", "x-language" and "x-parameters-of". Query editor, A JDQL console for Mansart Data.

  • Tables and a SQL editor for each Mansart pool, and a table of rows in the dev console — under mvn vidocq:dev, the Mansart pools panel gives each pool a tab: Tables lists its tables and views with their columns' number, Describe a table’s columns, types, keys and indexes, Preview its first rows, Query runs a statement that only reads in a transaction always rolled back, and Execute any one statement, asked first and rolled back unless committed; 30 seconds at most, :name parameters bound from params, the pool’s URL and password never shown. The SQL is typed in the query editor, which now reads SQL: aliases, joins, several tables, quoted names, comments, the columns of every table in scope completed with their table, an unknown alias flagged and an ambiguous column warned, LEFT JOIN … ON formatted on one line; its vocabulary, the language sql-<pool>, is read from DatabaseMetaData at boot. A result of rows is now a table — the type of each column under its name, NULL dimmed, Table / JSON and Copy as CSV — which the JDQL tab’s Query answers too; a panel answers one with PanelAction.ActionResult.rows(…). Tables and a SQL editor, A table of rows, SQL.

  • Dev service containers are named vidocq-dev-… — the PostgreSQL and Keycloak dev services used to leave their containers to Docker’s random names; each is now <prefix><application>-<service>[-<datasource>]-<suffix>, such as vidocq-dev-mcp-tasks-server-postgres-3f9a2c01, the prefix vidocq-dev- unless vidocq.dev.container-prefix says otherwise. The suffix is random, or for a reused container a digest of the application and the configuration, so reuse keeps finding it. Three labels, io.vidocq.dev, io.vidocq.dev.app and io.vidocq.dev.service, let docker ps --filter label=io.vidocq.dev list them. A provider of its own names its containers the same way with DevContainers.name and DevContainers.labels, from the SPI. Container names and labels.

  • The PostgreSQL dev service starts only for an application on PostgreSQL — with vidocq-runtime-devservice-postgres on the plugin, an application whose vidocq.properties says vidocq.pool.url=jdbc:h2:… (or MySQL, or any other database) no longer gets a PostgreSQL container whose URL replaced its own: the provider reads which database the application’s file names and, with no URL at all, starts a container only when org.postgresql.Driver is on the application’s class path. A PostgreSQL URL in the file (or a wrapper driver’s, such as jdbc:otel:postgresql:) still gets its container: it is the production one. An application with no URL and the driver in test scope only now needs its URL in vidocq.properties to keep the container under vidocq:dev and vidocq:run. A provider that does not start says why — not started: vidocq.pool.url is jdbc:h2, not PostgreSQL, the scheme only — in the log, the state file, the startup report and the Dev services panel. The DevServiceContext SPI gains applicationProperty(key) and onApplicationClasspath(className), and DevService gains skipReason(ctx), all default. A PostgreSQL container only for a PostgreSQL application.

  • Dev services are visible, vidocq.properties-aware, and available to vidocq:run and to tests — a dev service’s coordinates used to reach only the application’s -D arguments, invisible to the boot itself: the startup report now has a devservices section, host vidocq:dev/vidocq:run/test, image and endpoints per service, its injected keys, a secret always configured; the dev console shows the same, live, as its own panel. A vidocq.dev.* tuning key — vidocq.dev.postgres.port, vidocq.dev.reuse, vidocq.dev.devServices — is now also read from vidocq.properties/application.properties, so the stable-port recipe works without a -D on every run; an opt-out key such as vidocq.pool.url still never comes from those files. vidocq:run starts no container unless asked, with the same vidocq.dev.devServices=true. Every host reads that key the same way, first match wins: the explicit value (-D, the goal’s configuration, a system property), then the application’s files, then the host’s default (on for vidocq:dev and tests, off for vidocq:run), so -Dvidocq.dev.devServices=false overrides a true in vidocq.properties for one run. A test adds vidocq-runtime-devservices-junit and a provider, both test scope, and a LauncherSessionListener starts one set of containers for the whole run, no annotation needed. An unreadable state file is VIDOCQ-DEVS-001, which names its path; the boot goes on. The state file is the project’s, shared by every host: mvn test while vidocq:dev runs overwrites it, then marks it stopped, host test. target/vidocq-dev-services.properties still keeps a password in clear text, local to the machine, for psql/DataGrip — the JSON state file the application reads never does (Vidocq/vidocq#123). Dev services.

  • Continuous testing, in vidocq:dev and in a new vidocq:test goal — vidocq:dev now runs the application’s tests after every reload, or alone after a test-only change, without reloading the application, and the dev console’s new tests panel shows the last run: its counts, its failures with the first line of their message, a chart, and the buttons Run all tests and Rerun failed tests, which never name a test. vidocq:test runs the same loop without the application, with a summary in the terminal and r, f and q when a console is attached. A run is Surefire itself (mvn test-compile surefire:test), a new change cancels the run in flight, and the result is in target/vidocq-dev-tests.json, the output in target/vidocq-dev-tests.log; a run that fails before any test is compile-error when the compiler failed, error otherwise, such as Surefire’s provider missing offline (Vidocq/vidocq#138). The tests reuse the dev services containers, and share their database. vidocq.dev.continuousTesting=false turns it off; it is on when the project has src/test/java (Vidocq/vidocq#122). Continuous testing.

  • Dev tools only under vidocq:dev; the -dev panel modules, never packaged — the dev console and each extension’s live panel are development tools: nothing to declare any more, mvn vidocq:dev adds the console itself the moment the application has vidocq-runtime-chappe-webserver-extension, and each extension’s -dev companion module with it, so its tab is live. vidocq:run, vidocq:package, vidocq:jlink, vidocq:jpackage and vidocq:docker never contain any of it, even when a project still declares it: every dev-only jar is dropped, with a warning naming it, and vidocq:checkpom warns about the same declaration earlier, outside test scope. A test that reads the console, or a live panel, declares both at test scope. The six extensions that had a live panel of their own — Mansart pools, Knock health, Cassini REST, Dirac metrics, schema migration, the langchain4j-cdi MCP server — each move it to a new -dev module next to the runtime extension; the older, all-in-one DevConsolePanel form still works, for an extension that keeps it, and the console SPI it requires, an ordinary API jar, ships in the binary with it. vidocq:idea now writes a matching Dev, (packaged) and (debug) file for each application (Vidocq/vidocq#143). Getting it, Dev tools and binaries.

  • A code editor for JSON arguments, and a form for an entity — in the dev console, every json argument of an action, an MCP tool’s arguments as a Mansart Data repository method’s, is typed in a code editor written for the page: colours, line numbers, the first syntax error and then what the argument’s JSON Schema says at every depth (a missing required key, a wrong type, a value outside its enum, a key the schema does not list, a date that does not look like one), underlined with its message; completion of keys and values with Ctrl+Space; Format (Shift+Alt+F), which keeps numbers and strings as written; brackets and quotes closed as you type; one Ctrl+Z per edit it makes. The generated form takes one level of nesting, so TaskRepository.save(entity: Task) gets fields: the required ones starred and refused by their path when empty, the generated id marked (generated), dates with the shape they expect. Mansart Data’s entity schema now says which attributes are required and readOnly, and names each column. JSON editor, The generated form.

  • CSV export and import for Mansart Data — under mvn vidocq:dev, the Mansart Data panel’s JDQL tab gains Export CSV, which runs a query and downloads its whole result as a CSV file, and Import CSV, which saves the rows of a file you paste or pick as entities, in a transaction rolled back unless you commit it: a header of attribute names, null apart from "", , or ; as separator, at most 5000 rows, the first bad line named before anything is saved. The console gains a text/csv action result, shown with a Download button, and a Choose file input for a CSV field. CSV export and import for Mansart Data.

  • A JDQL console for Mansart Data — under mvn vidocq:dev, the Mansart Data tab ends with a JDQL tab that runs a statement you type against the application’s database: Query for a FROM, a projection, a count or an aggregate, Update / Delete for a write, asked first and rolled back unless you commit it; named parameters as a JSON object, converted to the attributes' types; the result as JSON, with the last 20 statements and a replay. Mansart gains JdqlExecutor.run for it, and a JSON argument’s form now shows a string of "format": "textarea" as a field of several lines. A JDQL console for Mansart Data.

  • Run a Mansart Data repository method from the dev console — under mvn vidocq:dev, the Mansart Data tab gets one tab per repository, which runs any of its methods against the application’s database: the arguments as JSON, converted to the method’s types, an entity built through Mansart’s own model; the result as JSON, with the last 20 calls and a replay. A write asks first and runs in a transaction rolled back by default, or committed. Running a repository method from the dev console.

  • A Mansart Data catalogue in the startup report and the dev console — vidocq-runtime-mansart-data-extension writes a mansart-data section: the application’s entities with their table and columns, as Mansart’s own model gives them, and its repositories with each declared method’s kind, @Query text, parameters and return type, plus the methods they inherit from Jakarta Data. Under mvn vidocq:dev its new companion vidocq-runtime-mansart-data-extension-dev shows it as the Mansart Data tab, one card per entity with its repositories inside. It reads names only: no connection, no query. An entity whose model Mansart cannot build keeps its place and raises MANSART-DATA-001. The Mansart Data catalogue.

  • The MCP request state is encrypted — with langchain4j-cdi’s MCP server, the REPLAY request state that carries a tool’s elicitation and sampling answers between rounds was signed but readable by anyone holding it; it is now encrypted and authenticated with AES-256-GCM, its key derived from vidocq.mcp.requestStateSecret. Only this format is read: no released version ever issued the signed one (langchain4j/langchain4j-cdi#303, langchain4j/langchain4j-cdi#305). langchain4j-cdi MCP server.

  • MicroProfile 7.2 — the runtime now targets MicroProfile 7.2: JWT Auth 2.2 (Cervantes), OpenAPI 4.2 (Grimm) and Telemetry 2.2 (Humboldt, on OpenTelemetry 1.66). The TCKs of the seven MicroProfile 7.2 component specs, and that of Metrics 5.1 (not part of the 7.2 platform), are green on the assembled runtime (1888 tests). Only the OpenAPI and Telemetry runs use release-candidate TCKs, published on Maven Central and byte-identical to the finals; the JWT Auth 2.2 TCK is final. The 7.2 platform release is still under ballot, and the two runs will be repeated on the finals once they are published. What you will notice: JWT accepts RS256 and ES256 (all RS/ES key sizes) when mp.jwt.verify.publickey.algorithm is unset, rejects an unrecognised value at startup, and enforces the algorithm family when it is set; @WithSpan spans carry code.function.name and @WithSpan(inheritContext = false) starts a new trace; OpenAPI treats unknown Schema properties as extensions, reads @Header.example/examples and method-level @ExternalDocumentation, maps Bean Validation @Digits to multipleOf/pattern, and writes decimals in plain notation. See the TCK page.

  • Buttons in the dev console, and the Swagger UI one click away — a section can now offer to open something the application serves: StartupReportSection.link(label, listener, path). The log prints the absolute URL; the console shows a button under the panel’s title that opens it in a new tab. The Swagger UI and the OpenAPI document get one in the rest panel and in the new openapi section, which also counts the operations of the document; a GET route with no template variable is a link in the route table. A panel never writes a URL: Vidocq builds it from a listener the report declared and a path, and the page only opens http and https. An extension offers a page it serves with ChappeMountPoint.advertise(listener, label, path) (Vidocq/vidocq#113). Links.

  • The rest panel is live in the dev console — one card per mount, sampled about once a second from the counters Cassini now keeps, CassiniStack.statistics(): requests per second, responses by status class, requests in flight, the share of time spent in handlers, and the mean and longest time to answer. The counters cost about 25 ns per request and allocate nothing; there is no breakdown per route (Vidocq/vidocq#103, Vidocq/cassini#42). The rest panel in the dev console.

  • A boot that fails stops what it started — when beforeStart, the container build or an onStart threw, the extensions already started stayed started: a Mansart pool kept its connections open, and under vidocq:dev a failed reload left the dev console’s live panels on the values of that boot. Every extension whose beforeStart was called now gets its onStop, in reverse order, and the container is closed; a failing onStop is logged, the next one still runs, and the exception of the boot goes through untouched. An extension’s onStop must therefore work when its onStart never ran (Vidocq/vidocq#145). When a boot fails.

  • @Transactional now covers Mansart Data writes with one dependency — vidocq-runtime-mansart-transactions-extension brings mansart-transactions-jdbc itself, the JDBC-to-JTA bridge that mansart-data-cdi declares optional: without it on the module path, a repository call took its own autocommit connection, so a @Transactional method could begin and roll back a real JTA transaction while a write it made just before throwing stayed committed, with no anomaly reported. Adding the extension is now enough (Vidocq/vidocq#97). Mansart Data writes join the transaction.

  • Schema migrations run where the application does — under vidocq:dev, vidocq:run and the launcher vidocq:package writes by default, the application boots in a module layer of its own, whose class loader lists no directory: Flyway found none of the scripts in classpath:db/migration, logged one warning, and the boot went on with an empty schema until the first query failed with Table "…" not found, and Liquibase did not find its changelog. Both backends now list classpath: locations from the application’s own modules, Flyway’s Java migrations and Liquibase’s includeAll included, and a schema history written by a flat launch stays valid. vidocq.migration.locations in vidocq.properties or as a -D no longer stops the boot with a StringIndexOutOfBoundsException. A datasource that finds no migration on an empty history is now the anomaly VIDOCQ-MIG-001, a warning in every mode. The new migration section of the startup report says what each datasource did, an up-to-date restart logs the version the schema is at instead of version=(none), vidocq.migration.failOnMissingLocations=true makes an empty Flyway location stop the boot, and a mistyped vidocq.migration.* key is reported as VIDOCQ-CFG-003. An extension reads the application layer with ApplicationLayer, new in vidocq-runtime-spi (Vidocq/vidocq#96). Schema migration, Listing the application’s files.

  • Clear errors when the application stays in the boot layer — when Vidocq.run() finds no application module to move into the Vauban layer, it cannot export the VidocqApp class’s package to itself, and the boot used to fail with a bare IllegalAccessException. The message now names the class, its package and the two fixes: an exports <package> to io.vidocq.runtime.core; in module-info.java, or a CDI bean (Vidocq/vidocq#201). When the application stays in the boot layer.

  • @Inject @RestClient works, and a Rest Client application links — the new vidocq-runtime-cyrano-rest-client-extension-codegen bundle runs Cyrano’s extension and generates each client’s proxy in the compiler: without it, @Inject @RestClient was unsatisfied at boot. vidocq:checkpom now fails an application that declares the Rest Client extension without the bundle, and prints the <path> to add; vidocq extension add cyrano-rest-client writes it. vidocq:jlink links such an application, since Cyrano no longer requires org.reactivestreams, an automatic module (Vidocq/cyrano#30). When upgrading, add the bundle to the compiler’s annotationProcessorPaths. The new vidocq-runtime-cyrano-rest-client-example shows the whole setup (Vidocq/vidocq#207). The Rest Client extension, Config and Rest Client need their codegen bundle, the example.

  • An extension brings what its brick leaves to the runtime — a brick now depends on APIs, and its Vidocq runtime extension picks the implementations (vidocq-workspace#17). The Rest Client extension brings the Jakarta REST API and Champollion’s JSON-B implementation: an application with the Rest Client and no REST server used to fail on the first JSON response, with no JSON-B provider (BUG-20261010-02). The Telemetry extension brings the Jakarta REST API, which humboldt-rest now leaves provided. Rest Client extension, Telemetry extension.

  • @Inject @ConfigProperty compiles with validation on — the new vidocq-runtime-ravel-config-extension-codegen bundle puts Ravel’s extension on the processor path, so the Vauban processor sees the beans it synthesises: an application used to get Unsatisfied dependency at compile time, and the only way out was -Avauban.validation=false. vidocq:checkpom requires the bundle wherever the Config extension is declared. When upgrading, add it to annotationProcessorPaths (Vidocq/ravel#21). The Config extension, Config and Rest Client need their codegen bundle.

  • /openapi in a sealed packaged application — the OpenAPI extension requires Grimm’s named module instead of the automatic one, which cassini:generate could fold into the application module, losing every Grimm bean: /openapi answered 404 (Vidocq/grimm#15). The OpenAPI extension.

  • Liquibase’s usage analytics stay off — Liquibase 4.30+ sends usage data to Liquibase by default for open-source users. The Liquibase backend now runs every migration, listing and clean with that option off, unless a system property, the environment or a Liquibase defaults file sets it. Liquibase analytics stay off.

Maven plugin

  • vidocq:dev passes -Dvidocq. on to the application — a -Dvidocq. of the Maven command line used to stop at Maven: mvn vidocq:dev -Dvidocq.chappe.listener.default.port=8081 still listened on the default port, silently, and -Dvidocq.devconsole.port never moved the dev console. The goal now forwards the same keys as vidocq:run, by the same rule: every vidocq.* key that is not a setting of the plugin. The properties the application sees.

  • vidocq:dev says which dev service provided a key — each key that the application gets from a dev service is marked with -Dvidocq.dev.provided.<key>=<provider id>, such as vidocq.dev.provided.vidocq.pool.audit.url=postgres, so that the application can tell a dev-service datasource from one configured by hand: the Mansart pool section of the startup report and the dev console show dev service postgres, localhost:54213. The marker names the provider, never a value, and a key that an explicit value kept is not marked. The dev services report no longer takes the user info of a JDBC URL for its host. Mansart pools in the startup report and the dev console.

  • vidocq:run — one run of the application, in a JVM forked as the production launcher does, after the goal has forked the lifecycle up to process-classes: mvn vidocq:run alone compiles, completes the bean index with vidocq:generate and starts the application. Ctrl+C stops it, its exit code is the goal’s result, -Dvidocq.run.debug=true opens a JDWP agent, and a -Dvidocq.* of the command line reaches the application. The vidocq:run goal.

  • The debugger listens on this machine only — vidocq:dev, and vidocq:run with -Dvidocq.run.debug=true, used to start the JDWP agent with address=:5005: every machine that could reach the port could attach, and a JDWP agent runs whatever code its debugger asks. The agent now listens on 127.0.0.1:5005, which a debugger on the same machine reaches as before. -Dvidocq.dev.debugHost='' (vidocq.run.debug.host for vidocq:run; quote the *, or zsh stops the command with "no matches found") opens it on every interface again, 0.0.0.0 too, and another address or host name on that interface; the goal then logs a warning that the debugger is reachable from the network. The connection hint of vidocq dev --debug prints address=127.0.0.1:5005 as well. The debug agent.

  • Maven’s colours reach the forked JVM — vidocq:run and vidocq:dev pass Maven’s own colour policy to the application as -Dvidocq.console.color. In an IDE’s Maven console, which is neither a terminal nor an IntelliJ Run console, the runtime used to drop its colours while Maven’s [INFO] lines kept theirs. An explicit vidocq.console.color and NO_COLOR still win. Colours in the forked JVM.

  • vidocq:idea (experimental) — IntelliJ IDEA shared run configurations per application of the reactor, written into .run/ from what the poms already declare. By default it is three Maven/Remote files: a run of vidocq:dev under the application’s own name (the one a developer already runs day to day), one of vidocq:run suffixed ` (packaged), and a Remote JVM Debug one suffixed ` (debug) that attaches to vidocq:dev’s own debug agent (`vidocq.idea.kind=application still writes a single Application run of the main class instead, with Make and vidocq:generate before launch, and no console or debug file of its own). Because the IDE’s own build never runs vidocq:generate, the (packaged) file is what compiles, indexes and starts the application entirely through Maven (Vidocq/vidocq#83). It never overwrites a file you wrote or edited, never touches .idea/, and -Dvidocq.idea.check=true fails a CI build when .run/ no longer matches the poms (=strict also fails on the files you wrote or edited). The vidocq:idea goal.

  • Generated code for third-party CDI jars — vidocq:generate now scans, besides <scanDependencies>, the jars an extension declares in its manifest and every CDI bean archive (vidocq.generate.autoScan, on by default), and gives each scanned jar the code the Vauban processor gives a project: _VaubanComponents, client proxies, intercepted subclasses. They ship in an enriched copy of the jar — given an open module first when it has no descriptor — that dev, run, package and jlink use in place of the original. The langchain4j-cdi MCP server’s beans move from reflection to generated code. mvn vidocq:analyze-deps explains which jars are scanned and why (Vidocq/vidocq#186). Both goals resolve the runtime-scope dependencies as well as the compile-scope ones, so a class that only a runtime-scope extension makes a bean — a JAX-RS @Provider of a scanned jar, under Cassini’s — gets generated code too (Vidocq/vidocq#188). The vidocq:generate goal.

  • vidocq:modularize is now vauban:modularize — the goal moved to io.vidocq.vauban:vauban-maven-plugin, with its properties renamed vauban.modularize.* and its output in target/vauban-modularized/, which vidocq:dev, vidocq:jlink and vidocq:package read. When upgrading, change the plugin in the pom. modularize moved to the Vauban plugin, migration.

  • The legacy vidocq:package layout starts — with vidocq.package.layer=false, the launchers passed a class name to --module and the JVM stopped on a FindException. They now launch <vidocq.mainModule>/<vidocq.mainClass>, or the runtime’s own main when the application has none, and a plain main class without vidocq.mainModule fails the build with a message that says what to set (Vidocq/vidocq#198). The legacy layout.

  • vidocq:docker checks that the image is Linux — vidocq:jlink links against the build machine’s JDK, so on macOS or Windows the image holds a Mach-O or PE bin/java that no Linux container starts. vidocq:docker reads it: it warns and still writes the Dockerfile, or, with vidocq.docker.build=true, fails before docker build (Vidocq/vidocq#199). The vidocq:docker goal.

  • vidocq:checkpom prints the <path> to add — a missing codegen bundle used to be reported as a fall back to reflection, which only Cassini does; the message now gives the <path> entry to paste into annotationProcessorPaths, with its group and version. A -dev companion module is no longer asked for the codegen bundle of the extension it reads (Vidocq/vidocq#147). Goals.

  • The dev console follows the application’s runtime version — vidocq:dev used to resolve the console at the plugin’s version, whatever runtime the application ran. It now takes the version of the application’s Chappe extension, warns when the plugin’s differs, and also looks in the plugin repositories, where a snapshot repository may be declared alone (Vidocq/vidocq#148). Getting it.

  • An application can declare its own version — vidocq-runtime-parent managed the Vidocq artifacts and the plugin at ${project.version}, which Maven reads in the child: an application with a <version> of its own looked for the plugin and every extension at that version and could not build. The parent now writes the runtime version out. Maven coordinates.

CLI

  • A scaffolded application runs the same everywhere — vidocq create now writes a @VidocqMain class whose main is Vidocq.run(args), as the examples do. The VidocqBootstrap main it wrote before booted in place: in a jlink image the application stayed in the boot layer, where the build-time generated _VaubanComponents is not seen without a provides, and Vauban fell back to reflection, so a bean with an interceptor (a @Retry method, for instance) failed with IllegalAccessException unless the package was exported. An application scaffolded with 0.3.0 can replace its main by the new one. create also refuses a name or --package that is not a valid Java package name (ft-030 gave the module io.example.ft.030, which does not compile). Getting started.

  • Scaffold against a SNAPSHOT — when the parent version is a SNAPSHOT, vidocq create writes the Central snapshot repository into the generated pom.xml, for dependencies and plugins, so --parent-version 0.4.0-SNAPSHOT, or a CLI installed with VIDOCQ_VERSION=snapshot, gives a project that builds as is. See Trying a SNAPSHOT.

  • vidocq dev and vidocq start run the project — both booted the runtime inside the CLI’s own JVM, which holds neither the project’s classes nor its extensions, so nothing listened. They now run mvn process-classes vidocq:dev and mvn vidocq:run, through ./mvnw when there is one, and behave as the goals do: live reload, dev console, debug agent. --port outranks the project’s vidocq.http.port. vidocq dev, vidocq start.

  • vidocq extension add declares the module too — besides the dependency and the codegen bundle, add writes the extension’s directives into module-info.java (its requires, and what Cassini and Mansart Data leave to the application), each marked // vidocq:<id>, and pins the parent’s version on what it adds; remove takes all of it back, the codegen path included. The catalog lists the OpenAPI, Swagger UI and migration extensions, and knows the Config and Rest Client codegen bundles. vidocq extension list, vidocq info and vidocq doctor resolve the project’s dependencies with Maven, so they see its extensions, transitive ones marked (transitive), instead of none. vidocq extension.

  • Completion for the whole command line — sub-commands, options and values: the extensions not yet in the pom after extension add, those it declares after extension remove, the keys of vidocq.properties after config get|set, all without starting a JVM. vidocq completion install sets it up in ~/.zshrc or ~/.bashrc, and an installed script follows the CLI that runs. vidocq completion.

  • vidocq update — updates the CLI itself, a release to the latest release, a SNAPSHOT to the latest SNAPSHOT build; --check only reports. A SNAPSHOT CLI also prints its build time next to its version. vidocq update, Command reference.

Other bricks

  • Vauban — client proxies for third-party produced types, without opens: the Java SE launcher places them, Vidocq.run re-layers with the same policy, and boot-time opening stays opt-in. What’s new in Vauban.

  • Cassini — an unreadable request entity answers 400, not 500, and a host reads the route table and live request figures. What’s new in Cassini.

  • Mansart — enums stored as @Enumerated says, a stricter JDQL parser, and the Jakarta Data core the dev console’s tools use. What’s new in Mansart.

  • Heisenberg — fallback methods reached on a strict module path, with no opens. What’s new in Heisenberg.