Discover dashboard filters, datasets and metrics from a parsed reference URL; capture and review baseline candidates with source provenance. Improve scenario DAG and run result views, add isolated browser coverage, and align contracts and ADRs.
G-BSL-CATALOG: consume/materialization appends the server-owned CatalogRevision under the catalog lock and rolls it back with the YAML on any failure (no phantom revision); the publish settle mirrors the outcome onto the digest-matched head without demoting a landed commit.
G-BSL-DERIVED: a closed period that is not the launch-declared reporting_period blocks the start typed (BaselinePeriodStale = BASELINE_STALE) with a facts-only rebaseline proposal, no automatic rebaseline, and REPORTING_PERIOD_INVALID for a malformed declaration.
G-LLM-SECURITY: the evaluation prompt declares evidence-as-data, coerces the verdict to the closed enum, drops findings for undeclared criteria and refuses provider-owned identity; 7 injection vectors run through the submit seam with no live LLM.
INV_7: publication/baseline/start/evaluation modules split under 400 lines and CC<=10; moved contracts tombstoned with @REPLACED_BY. Matrix, 037/044 traceability and WORKSTATE accounting.
- start_run.py: classify_start_error — one canonical mapper for ENVIRONMENT_NOT_CONFIGURED,
every BASELINE_* code, IDEMPOTENCY_KEY_REUSED and existing UPPER_SNAKE typed codes; unknown
ValueErrors collapse to RUN_START_CONFLICT. REST and MCP now share it, so the same failure
can no longer present a transport-dependent error vocabulary (the divergence the matrix caught:
MCP returned raw exception text while REST collapsed to RUN_START_CONFLICT).
- Parity matrix (12+ vectors): env/baseline/conflict/disabled-action/disabled-automation/
investigation-RBAC/unauthenticated, plus service-principal human-gate denial.
- Base-drift adaptations during integration: the investigation-case ACL vector now asserts the
real no-bypass invariant (foreign case collapses to not_found via MCP, no content leak) since
the investigation MCP tools exist on this release; terminal-transition atomicity expectations
now include the queue_created receipt and pin replay idempotency (2 receipts, no growth).
- 17 parity tests; union wave-3 verification: 1845 passed.
- locking.py: transaction-scoped pg_advisory_xact_lock per fingerprint (no-op on SQLite);
serializes queue/case/episode writers without new migrations (D11).
- investigation.py: queue_signal/open_case lock then SELECT FOR UPDATE + populate_existing;
set_disposition validates case CAS then performs case status/version, queue closure,
AgentAction and episode resolution inside one savepoint (zero partial projection), and
emits the idempotent case_dispositioned receipt after the savepoint commits.
- recurring.py: record_occurrence locked + FOR UPDATE (one open episode).
- context.py: AnalyticsContextKey and assess_comparability — same environment with a differing
baseline family/release/RLS/principal is typed ineligible/mismatch; eligible-sequence
classification separates regression/flaky from model variance.
- comparison.py: compatibility carries comparable = same_scenario AND exact-context + context ids.
- Tests: 22 unit (13 new) + 7 PostgreSQL Testcontainers concurrency vectors (--run-integration).
- Merged with the notification wiring on analytics/investigation.py (both changes kept).
- sql_generator: normalize ISO/epoch values only against a resolved target type
(Nullable/LowCardinality unwrapped); untyped epoch-looking strings are preserved and
unparseable temporal values raise instead of coercing. Removes the heuristic that
wrote sentinel dates (2106-02-07 = UInt32 max) into business date columns.
- sql_generator: ClickHouse string-literal escaping — backslash, quote and control
characters escaped; ANSI '' doubling alone silently rewrote paths and tabs on insert.
- service_target_schema: one fetch_target_column_types resolver for both direct_db and
SQL Lab insert paths (300s cache) plus a single column-catalog SQL builder; EXPLORE
trace when the catalog is unreachable/empty instead of a silent untyped fallback.
- _lang_detect: script-conflict downgrade no longer depends on the target language list;
letterless text becomes und; short/number-heavy Cyrillic cannot survive as a Latin
language; sanitize_detected_language validates the LLM arbitration answer by script.
- _batch_proc/_utils: und rows actually reach LLM arbitration (same-language, cache and
preview short-circuits blocked) and trust a cache entry only when its stored source
language is confirmed; detector version bumped to invalidate the v2 cache.
- _key_mapping: single source->target key mapping; MERGE with target_language_column
absent from target_key_cols rejected in preflight before any fetch/LLM; lang_code is a
target-only discriminator (no fictitious source key). Removes 5 divergent copies.
- frontend: global run indicator elapsed timer (1s tick, server started_at, cleanup)
reusing formatDuration; MERGE-key inline validation with en/ru i18n keys; UX contract
and decision-memory updates.
- tests: real ClickHouse testcontainers coverage on dm.debt_comment_translations
(Date32/DateTime transfer, epoch/millis/sentinel/boundaries, NULL semantics, text
escaping round-trip, MERGE language discriminator, chunked batch, production
projection); unit coverage for typed normalization, escaping, key mapping and the
arbitration cache. Existing integration module had a stale untyped-epoch expectation.
- ProviderEventLoop.stop(): close_all_sessions fan-out on the live loop before detach +
bounded pending-task drain before loop.close() (zero 'Task was destroyed' live-proven);
close_all_sessions weak-registry shutdown fan-out in browser_session_managers.
- SCEX-FR-038 independent mutation readback: browser_readback.py (same-session fresh
client_id SELECT, cleanup-aware expectation, canonical 6dp float normalization);
transport wiring adds readback_verified checkpoint; BrowserTransportReadbackMismatch ->
reconciliation_required/effect unknown/retry blocked; INV_7 kept (browser.py 395 LOC
via factory-helpers extraction).
- Live canaries: actions/mutation (readonly-canary-20260918T174659/174740/184343Z) and
native filters (filters-canary-20260918T195946Z: values/clear/replay/chart PASS; date
vector absent on stand — external prerequisite).
- Robustness fixes from live flake: pre-apply Escape (stale overlay in reused sessions)
and bounded antd dropdown settle in browser_native_filter.
- Root-cause of the 82.75/82.74 divergence: mid-step post-SELECT vs post-restore
lifecycle skew, not a cache defect.