The dc3-db aggregate mixed two naming axes and one axis-free module:
infrastructure used dc3-db-r2dbc-{core,runtime,postgres} (with "postgres"
sitting in a layer slot), the domain adapters used dc3-db-postgres-{domain}
(no "r2dbc" at all), and tck had no axis. PostgreSQL-only and R2DBC-only
are properties of the whole aggregate (dc3-db README, docs/db-dialects.md),
so the per-module prefixes carried no discriminating information — the
65 store classes already say R2dbc in their names.
New layout, one dc3-db-{role} pattern, aligned with the common-{domain} /
center-{domain} pairs they serve:
dc3-db-r2dbc-core -> dc3-db-core
dc3-db-r2dbc-runtime -> dc3-db-runtime (absorbs the dialect)
dc3-db-r2dbc-postgres -> (merged into dc3-db-runtime)
dc3-db-postgres-auth -> dc3-db-auth
dc3-db-postgres-manager -> dc3-db-manager
dc3-db-postgres-data -> dc3-db-data
dc3-db-postgres-agentic -> dc3-db-agentic
dc3-db-tck -> unchanged
The dialect module held a single 12-line AutoConfiguration registering
StandardR2dbcDialect("postgres", ...) plus the r2dbc-postgresql driver;
both move into dc3-db-runtime (package io.github.pnoker.db.runtime.dialect,
registered in the runtime AutoConfiguration.imports, driver dependency
transferred). Its six dependents now rely on the runtime they already
pulled in. Java packages rename in step — io.github.pnoker.db.r2dbc.core
-> io.github.pnoker.db.core, io.github.pnoker.db.r2dbc.runtime ->
io.github.pnoker.db.runtime, io.github.pnoker.db.postgres.{auth,manager,
data,agentic} -> io.github.pnoker.db.{auth,manager,data,agentic} — so no
trace of the old axis survives outside git history.
Also fixes the stale module layout in docs/design/relational-r2dbc.md §6
(it still described stores living in dc3-common-*/repository, the
pre-7fde11134 arrangement) and ships migrations/02-schema-fingerprint-
resync.sql for existing volumes still carrying the pre-documentation
fingerprint marker.
Verified: full-reactor compile, dc3-db aggregate tests (-am), all six
service images rebuilt through the multi-stage Dockerfile (container
Maven package), WSL stack recreated with every center healthy, and an
end-to-end login + manager list smoke test through the gateway (JWT
cookie, offset/limit/sort paging).
Co-Authored-By: Claude Code <noreply@anthropic.com>
Bring 07-observability and 08-runtime up to the comment grammar the
other seed files (02-06) already follow — DDL text itself is untouched
(verified: comment- and whitespace-stripped comparison against HEAD is
identical for both files):
- 08: every table gets the "-- Table structure for" section banner, a
file-level map of the runtime coordination blocks, per-column inline
comments aligned within each table (mirroring the COMMENT ON
statements), and one-line purpose comments above each index (claim
scans, tenant timelines, sweepers). The stray compose-role note moves
into the file header where it belongs.
- 07: the 1-hour cagg index and realtime ALTER get the same comments
their 1-minute siblings already carry.
The fingerprint hash covers these files verbatim, comments included,
so dc3/bin/schema_fingerprint.py --sync rolls the embedded ddl_hash
and the four distributed defaults to the new canonical value
(8297d4b5… → 51468116…).
Co-Authored-By: Claude Code <noreply@anthropic.com>
Adopt the dc3.site canonical slogan across every surface: the platform
is an open-source Industrial IoT Runtime for Physical AI. Hero taglines
and tech-tag strips in all seven language banners regenerate from the
new copy (MCP tool gateway / safe / controllable / traceable replace
the edge-cloud and SENSE-DECIDE-ACT strips); READMEs follow — badge
line gains the live demo and industry-demo links, the AI section
rewrites around the MCP tool gateway loop (OAuth client registration,
per-tool authorization, audit trail), and the intro names the core
decoupling: drivers push normalized point data to the bus, applications
consume through one unified API. AGENTS.md carries the same one-paragraph
positioning for AI assistants.
Co-Authored-By: Claude Code <noreply@anthropic.com>
The footer's two clusters both carried flex: 1 1 280px, which starved
wider button rows (device = Add + Import + Search + Reset) and wrapped
the last button even when the whole footer row still had room. The
button cluster now takes exactly what its buttons need (flex 0 1 auto,
nowrap — a truly narrow viewport scrolls the row horizontally instead,
same pattern as the segmented filters above) and the pager cluster
stretches over the rest.
Co-Authored-By: Claude Code <noreply@anthropic.com>
The shared table-to-card renderer (settings tables on phones) joins the
entity-icon-tile family: cardIcon/cardTone props mirror
EntityMobileCard's mobileCardIcon/mobileCardTone, the header tile +
hairline follow the ThingsCardHeader anatomy, titles adopt the card
title weight, and actions dock right stretching edge-to-edge on phones
— floating-action inset dropped like the entity cards. userConfig is
the first consumer (User glyph, blue tone).
Co-Authored-By: Claude Code <noreply@anthropic.com>
Now that every token migration has landed, lock them in:
- radius-contract.test.ts: every border-radius in src/ must resolve to
the --dc3-radius-* scale (directly or through an Element Plus radius
variable). Geometric circles (50%/100%), inherit and single-corner 0
resets stay literal — hand-rolled px values are the exact drift this
cleared once.
- spacing-contract.test.ts: macro spacing (page edges, card walls,
section rhythm) must resolve to --dc3-space-* / --dc3-page-padding /
--dc3-gutter. Semantic tokens defined exactly once in theme.scss; a
whitelist of layout-bearing containers may only declare margin/
padding/gap with tokens; clamp() in spacing and el-row :gutter
values other than 8 are banned repo-wide (the JS prop cannot read
CSS vars, so the literal must mirror --dc3-gutter's desktop value);
a // spacing-exempt marker opts out line-by-line. Intra-component
micro spacing stays out of scope this round.
Document the v8 spacing-system decision in
docs/design/frontend-three-terminal-ux.md and add the A5 rules to
dc3/agents/frontend.md so agents follow the scales.
Co-Authored-By: Claude Code <noreply@anthropic.com>
The app shell scrolls through el-scrollbar (overlay bars), but every
other overflow container — card bodies, tables, textareas, asides —
still rendered the raw browser bar. Give those one slim, quiet,
theme-aware appearance (6px track, border-token thumb, Firefox
scrollbar-width thin); el-scrollbar's own hiding rules keep their
higher specificity, so custom bars are unaffected.
Co-Authored-By: Claude Code <noreply@anthropic.com>
The backend list_tree runs a ~1s full scan whenever its server cache
misses, and the tree only changes on registry sync or an explicit edit
— far rarer than page visits. Serve the cached tree instantly and
revalidate in the background (60s fresh window, single-flight promises
so concurrent visitors share one request); mutations through
resourceConfig invalidate the cache. At a few thousand nodes the right
next step is lazy loading (list_tree already filters by
parentResourceId), not more caching.
Co-Authored-By: Claude Code <noreply@anthropic.com>
Three contract mismatches:
- Page orders sent snake_case columns (create_time, group_index) while
the R2DBC layer keys orders by the camelCase DTO field — default
sortColumns and the shared usePagedList/useEntityListPage fallbacks
move to createTime/groupIndex. The API store does not support
createTime sorting at all, so apiConfig pins defaultOrderColumn to
id for a stable default order.
- listDeviceStatus/listDriverStatus posted a full page object with
orders copied in; the status endpoints take plain offset/limit.
Compute the window from the current page instead.
- Bulk lookups requested limit: 1000 but the backend caps at
PageRequest.MAX_LIMIT (200) — the resource/api name resolution and
the MCP service-account picker now stay inside the cap (comments
document the 200-definition completeness bound).
Co-Authored-By: Claude Code <noreply@anthropic.com>
The backend dictionary contract requires a parent device
(requireParent → "Parent ID is required"), so asking for points without
one is a guaranteed 4xx. Disable the point select until a device is
chosen (placeholder explains why), and short-circuit programmatic
dictionary triggers the same way. The range segmented also drops its
stale `select` prop.
Co-Authored-By: Claude Code <noreply@anthropic.com>
The point-value detail was the system's only outlier: a drawer showing
raw JSON while every other entity (device/driver/point/profile) has a
routed detail page. Rebuild it on the shared contract — /point_value/
detail route (ROUTE_MENU_ALIASES maps it back to the pointValue nav
node), base-card shell, error/skeleton/empty states, structured
el-descriptions fields. PointValue.vue drops its detailData plumbing
and routes on card "detail".
Cursor-paged ToolCard footers get their own labels (common.nextPage/
prevPage) — "Next/Previous" read as wizard steps, but cursor-paged
history walks pages.
Co-Authored-By: Claude Code <noreply@anthropic.com>
The assistant panel gains an expanded mode: a full-screen workspace
with a permanent conversations column on the left — the same list the
history dropdown shows, one tap to switch sessions, rename/delete/new
actions docked in the rail header (rename moves into an anchored
popover). The resize handle hides while expanded, the panel transition
animates, and the brand mark renders on the shared icon-tile grammar.
Copy renames the surface from "DC3 Assistant" to "Agent"
(headerExpand/headerCollapse/sessionsTitle keys included).
Co-Authored-By: Claude Code <noreply@anthropic.com>
Settings sidebar:
- new SettingsMenuSearch — expanded rail gets an inline input filling
the toolbar next to the collapse toggle; collapsed rail degrades to
an equal-sized icon button whose popover carries the same input and
results. Picking a result navigates WITHOUT expanding the rail
- the item mapping moves to useSettingsSidebarItems so the sidebar and
the search read one source; the sidebar no longer force-refetches
the menu tree on every settings visit (menuConfig mutations now
invalidate the pinia cache instead — one ~1.5s list_tree saved per
entry), and grows an expandAll mode that unfolds every group
- the tablet icon rail keeps a persisted "stay expanded" override
(settingsRailExpanded, mirrors theme/density persistence)
Three-terminal navigation (A3):
- phones drop the hamburger + nav drawer entirely: the horizontal menu
stays in the capsule as an icon-compact strip, primary navigation
never leaves the header
- the header settings button doubles as the phone settings nav — it
bubbles up the fully-expanded menu popover instead of navigating;
the floating corner toggle and its drawer are gone
- language/theme fold behind one "..." chip popover on phones
- breadcrumbs with a nav payload (settings root, mid-level groups)
open a layered picker popover listing the sibling pages — same
bubble pattern — instead of navigating blind
Icon-only nav chips become circles to match the capsule's round
utility buttons. Tests follow: layout.test gains the phone-tier
assertions (no hamburger/drawer, folded preferences, popover nav),
the e2e responsive spec walks the new phone flow, and the ElPopover
stub renders its reference slot so popover triggers stay findable.
Co-Authored-By: Claude Code <noreply@anthropic.com>
Card footers rendered their actions in whatever order each page happened
to grow them (delete/enable/disable first, browse last on several).
Declare the ladder once and align every card: detail (browse) → edit →
enable/disable (state) → delete (destructive last, popconfirm-guarded).
ThingsCardActions documents the contract in-template; DriverCard and
PointValueCard (detail → write) follow.
Co-Authored-By: Claude Code <noreply@anthropic.com>
Entity card headers rendered a zoo of legacy PNGs (device.png,
driver.png, point.png, ...) at fixed sizes with no tone. ThingsCardHeader
now takes an Element Plus icon name (legacy image URLs still work when
the value contains "/") drawn on a 44px tone-tinted tile — the same
glyph-block family as StatCard on Home — with a five-tone accent
palette: device purple, driver blue, point green, profile orange,
command blue, event red, point-value success.
The shared .entity-icon-tile class lands in global.scss for the mobile
record cards too: EntityMobileCard grows mobileCardIcon/mobileCardTone
config (EntityListConfig) so settings record lists render the same
tile anatomy, replacing the old selected/unselected point-info PNG
pair with an accent flip. AgenticAssistant's mark joins the family.
The nine common PNGs are deleted — nothing references them anymore.
Status chips normalize their enum codes to uppercase before comparing
(driver/device status maps carry lowercase values, so FAULT/ONLINE tags
never matched), and mobile action rows drop the floating-action inset
now that buttons dock edge-to-edge on phones.
Co-Authored-By: Claude Code <noreply@anthropic.com>
Entity list pages (device/driver/profile/point/point-value/command/
event) nested el-row/el-col breakpoints inside a BlankCard wrapper,
which capped cards at lg/xl 4-up rungs, padded the wall with the
shell's own margins, and split the spacing mechanism between el-row
gutters and hand margins.
The wall is now a plain CSS grid (.entity-card-wall in global.scss):
repeat(auto-fill, minmax(min(380px, 100%), 1fr)) — the viewport earns a
column only once every card can stay at least 380px wide, so columns
step continuously (1→2→3→4→…) instead of snapping rungs, cards always
fill their track, and the wall's gap owns both axes through
--dc3-gutter. Empty/load-failed blocks span the whole wall.
.things-card and SkeletonCard drop their per-card margins so the
skeleton wall renders the exact geometry of the real one — no layout
shift when loading resolves; skeleton corners join the radius scale.
entity-page-error joins --dc3-gutter at the same time.
Co-Authored-By: Claude Code <noreply@anthropic.com>
theme.scss gains the semantic layer: --dc3-page-padding (16/12/8 stepped
tiers, deliberately not clamp() — a fluid edge never lands on a tier
value) and --dc3-gutter (8px card/section rhythm, 12px on phones), plus
the Element Plus radius mapping (--el-border-radius-base/small/round and
--el-popover-border-radius) defined once on :root so controls, nested
panels and every popper shell resolve through the --dc3-radius-* scale.
Sweep the hand-rolled values onto the tokens: home rows and error bars,
settings alarm overview, entity list pages, login chips, glass capsule,
chart blocks, AppPreferences — plus the redundant per-component radius
overrides dropped from global.scss's settings-main block (the :root
mapping now wins). ToolCard's margin joins --dc3-gutter and its card
body padding matches the CardShell family. el-row :gutter anchors get
sync comments since the JS prop cannot read CSS vars.
The radius-contract / spacing-contract guardrail tests and the A5 docs
land at the end of this series, after every token migration is in.
Co-Authored-By: Claude Code <noreply@anthropic.com>
The file only styled .home-statistic, which nothing renders anymore;
grep finds no remaining reference in src/ or tests/.
Co-Authored-By: Claude Code <noreply@anthropic.com>
The footer is no longer opt-in per card: DashboardCard now records a
data-freshness stamp ("Updated at HH:mm:ss") every time `loading`
settles without `error` — manual refresh, autoRefresh polls and
tab/range reloads all funnel through the same loading cycle, so every
trigger stamps correctly and consumers drop their own lastRefreshed
plumbing. The right side carries an explanatory line via the
`footerMeta` prop or `footer-meta` slot (ranking rules, window hints,
count digests); `:footer="false"` drops the strip, the legacy `footer`
slot still replaces both sides.
Migrate the home dashboard to the contract: Trend/Latency/Activity/
Alert cards pass footerMeta strings (new home.*.footer keys), the
AnalyticsTabs caption moves from between-tab-and-chart into the
footer's meta side (ellipsized, one line), and TopologySankey/
LiveDataFeed delete their hand-rolled "updated at" clones — the shared
stamp replaces them. common.updatedAt replaces home.liveFeed.updatedAt
(deduplicated into common) and StatCard gains the same hover-revealed
freshness line on the home stat tiles.
Co-Authored-By: Claude Code <noreply@anthropic.com>
Roll the canonical clean-DDL fingerprint (713c2637… → 8297d4b5…) in the
runtime seed table and every deploy surface that pins it — docker-compose.yml,
docker-compose-dev.yml, env/dev.env, env/dev.env.sh — so startup
validation matches the batch of schema updates just landed (icon seed
re-mapping, whitespace/column-order normalization; 08-runtime also picks
up the alignment pass, no semantic DDL change).
Co-Authored-By: Claude Code <noreply@anthropic.com>
Twelve settings menus carried icons borrowed from unrelated domains
(Lock for roles, Discount for menus, ChatDotRound for model config,
Document duplicated across three history pages...). Re-map each to the
glyph that names its job — UserFilled/Medal/Paperclip/Box/Menu/
MagicStick/Shop/Postcard/Platform/Lightning/Position/Memo — unique per
group with no parent/child duplication.
Single source of truth, kept identical across four copies:
- initdb/02-iot-dc3-auth.sql seed (also drops the dead nav.home
entry-point icons from route meta in views.ts — nav icons come from
the dc3_menu tree, never from route records)
- new migrations/01-menu-icon-backfill.sql: initdb seeds only run on
FIRST container start, so databases initialized before this change
need the idempotent UPDATE backfill to converge
- settingsNav.ts frontend fallback + menuTree.ts static mock
- settings-menu-consistency.test.ts now parses menu_ext icons from the
seed and enforces seed == fallback == mock, that every icon is a real
@element-plus/icons-vue export, and per-group uniqueness
02-auth also picks up the whitespace normalization from the previous
commit's pass over the other seed files.
Co-Authored-By: Claude Code <noreply@anthropic.com>
Column-type alignment, SET ( vs SET(, and trailing whitespace across
extensions/data/manager/history/agentic/observability seeds — zero
semantic change (verified: git diff -w is empty). 02-auth and
08-runtime carry the same reformatting alongside their content changes
in the follow-up commits.
Co-Authored-By: Claude Code <noreply@anthropic.com>
mariadb/mysql move off 2026.6, postgres/rabbitmq off 2026.5, so all
four dependency images follow the same 2026.10 base release line built
in dc3-docker. docker-compose-db.yml passes the new tag through.
Co-Authored-By: Claude Code <noreply@anthropic.com>
Two independent container-build breakages:
- Root Dockerfile: with BuildKit cache mounts the Maven resolver's
named lock factory fails on overlay-backed filesystems ("sync context
factory" errors) even though only one Maven process runs per build.
Switch the sync context factory to noop for the package step.
- dc3-web/Dockerfile: pnpm 12 dropped bin/pnpm.cjs while the corepack
shim bundled with Node 22 still expects it, so `corepack prepare`
died at the first pnpm invocation. Install pnpm directly via npm -g
so the bin entry resolves.
Co-Authored-By: Claude Code <noreply@anthropic.com>
R2dbcEventHistoryStore.findByPage started from DatabaseClient.sql("")
and chained .bind() calls while folding filters into a shared predicate
string. sql("") fails eagerly on the R2DBC SPI, so any code path that
touched the spec before the real SQL was attached blew up at runtime.
Fold every optional filter into the predicate first, then build the
COUNT and page specs separately, binding each parameter on the spec that
actually references it. Behavior (query shape, paging bounds, ordering)
is unchanged.
Co-Authored-By: Claude Code <noreply@anthropic.com>
The forced re-download proved the javaagent failure is not a corrupted
cache: freshly downloaded jars fail identically, so the diagnostic step
served its purpose and the investigation moves to an issue.
The e2e suite runs the verify lifecycle too, so its shallow checkout
also needs the origin/main baseline for spotless ratchetFrom. Unit,
integration and coverage jobs additionally restore a maven cache that
contains a truncated mockito jar from an earlier failed run; the
restore-key fallback keeps resurrecting it and -U never re-downloads a
file that already exists, so drop implausibly small jars before the
build.
The mixed 'initialize spotless:check' direct-goal invocation resolves
inter-module jars from remote repositories instead of the reactor and
fails within seconds on a clean cache — Backend CI on main has been red
since early September because of it. Both checks already bind to the
verify phase, so a plain skipped-tests verify run executes them with
normal reactor resolution (verified against an empty local repository).
ratchetFrom origin/main (38204bf7d) needs an origin/main ref that the
default depth-1 PR checkout does not provide, so compile, Unit tests
and Integration tests all failed within seconds on the first PR since.
Fetch the main tip right after checkout in every affected job; the
ratchet only diffs the working tree against that one commit.
docs/README.md still advertised a three-dialect selection guide that
db-dialects.md no longer contains, tsdb-abstraction.md documented a
MySQL upsert branch that no longer exists, and the r2dbc-postgres
README pointed at the removed dc3.db.type activation property.
dependencies/mysql and dependencies/mariadb kept placeholder Dockerfiles
after the flag-day removed their seed scripts; no compose, helm, k8s or
CI build context referenced either image. The empty dc3-db-r2dbc-mysql
and dc3-db-r2dbc-mariadb module skeletons go with them.
JDBC_AUTO_PROFILE, DB_TYPE, MYSQL_AUTO_PROFILE, POSTGRES_AUTO_PROFILE,
JDBC_PROFILE, POSTGRES_PROFILE, MYSQL_PROFILE and MARIADB_PROFILE had
zero references since the flag-day storage unification; the runtime
selects nothing anymore.
docs/db-dialects.md and the flag-day design already declared
dc3.db.type removed, but the property still gated the only dialect
adapter. PostgresR2dbcDialectConfiguration now registers
unconditionally, dc3-center ships dc3-db-r2dbc-postgres as a plain
runtime dependency instead of a self-activating Maven profile, and
the cardinality guard keeps rejecting any count other than exactly
one R2dbcDialect bean.
Store json() helpers swallowed parse failures as silent nulls, so a
corrupted or shape-drifted extension column was invisible. JsonUtil
gains parseObjectQuietly(text, type, context): empty stays null, and
an undecodable value logs a warning with the column context and
returns null, keeping one bad row from failing an entire query. All
fourteen store helpers now delegate to it and pass their column name.
Every store decoded timestamp columns through a private time(Object)
helper that silently returned null for unexpected driver types, hiding
decode breakage as missing values. The helpers now delegate to
DatabaseInstant.toLocalDateTimeUtc, which keeps null for null columns
and throws IllegalStateException on any non-null value of an
unsupported type, matching the R2dbcOperationRepository.instant
precedent.
R2dbcPageExecutor and R2dbcCursorExecutor had zero production
adopters: every store executes count/items and keyset paging inline
inside SpringR2dbcPageTransaction snapshots. Dropping the dead
executors leaves one pagination implementation instead of two,
and the migration handoff checklist no longer lists them.
The flag-day migration to reactive PostgreSQL left dead mysql/mariadb
arms behind dialect.name() switches in the per-domain postgres stores.
Every branch now collapses to its PostgreSQL arm: ON DUPLICATE KEY
upserts, DATE_FORMAT/TIMESTAMPDIFF analytics buckets, JSON_EXTRACT
expressions, INSERT IGNORE locks, and the UUID-to-BINARY(16) codec.
R2dbcPointValueLatestStore no longer silently falls back to MySQL
syntax for any non-postgres dialect name, and stores whose dialect
field became unused lose it (TCK constructor updated accordingly).
Mirror the backend checkstyle javadoc gate in eslint: exported
functions, interfaces, types and classes must carry jsdoc with
matching @param/@returns tags and descriptions. Backfill the api,
config, utils, store, mock and composable layers so the gate starts
green.
Mirror the backend checkstyle javadoc gate in eslint: exported
functions, interfaces, types and classes must carry jsdoc with
matching @param/@returns tags and descriptions. Backfill the full
cli surface so the gate starts green.
java headers are generated and enforced by spotless licenseHeader from
the root copyright file; the typescript surfaces had no such control -
dc3-cli carried zero headers across 32 files and dc3-web was missing
one hand-written file:
- add scripts/add-license-header.mjs (shared by both packages) that
reads the same root copyright and emits block comments for ts and
html-comment headers for vue, with a --check mode for ci
- wire headers / headers:check npm scripts; unplugin-generated ambient
d.ts files are exempt (the generator owns their file head)
- backfill all 32 dc3-cli sources and operation.ts / type.d.ts
verified: headers:check green on both packages, cli 67/67 tests +
build + lint, web 687/687 tests + lint + format
five drivers carried private bytesToHex copies and three carried
hexToBytes variants, while mbus/dlt645 frames each implemented the same
sum checksum:
- codecutil.bytesToHex now uses hexformat uppercase, matching what all
five private copies already emitted (zero display change)
- codecutil.hexToBytes added: whitespace-tolerant, blank-to-empty,
mixed-case input
- codecutil.sumChecksum added as the shared mbus/dlt645 frame checksum
and both frame classes delegate to it
- dlt645's inline hex loop replaced by codecutil.hexTobytes
- fins' private bcd copy kept deliberately: codecutil's bcd variant
strips leading zeros, a different protocol semantic
gitattributes only covered five file types; on a windows clone
(core.autocrlf=true) every other text file checks out as crlf, which
spotless's origin/main ratchet then treats as whole-repo changes and
the format gate fails everywhere. declare lf as the repository-wide
text convention with explicit crlf exceptions for bat/cmd/ps1, mark
the binary asset families, and renormalize (no content changes - the
tree was already lf)
- springdoc: declare api-docs/swagger-ui enabled explicitly; the boot
reminder only fires on unset properties, so the intent-declaring
values keep behavior identical and quiet all six services
- netty: add the macos dns native resolver to the gateway so the
resolver does not log an error at boot on mac dev machines (the
no-classifier jar bundles every arch and is inert on linux)
- hibernate-validator HV000271: spring-cloud-gateway 4.3.0 still uses
container-level @Valid on its List properties. logger-level rules
cannot suppress it because spring boot's logging re-init installs a
second loggercontext that resets them, so drop the exact message
with a turbo filter attached to every context loading this config;
the class documents the upstream trigger for removal
live verification: five services plus gateway boot with zero warn and
zero error lines, virtual driver included; salt/login probe 200