diff --git a/apps/control-station/src/components/system/ComputeContourSettingsWindow.tsx b/apps/control-station/src/components/system/ComputeContourSettingsWindow.tsx
index 7e22e43..e712bf6 100644
--- a/apps/control-station/src/components/system/ComputeContourSettingsWindow.tsx
+++ b/apps/control-station/src/components/system/ComputeContourSettingsWindow.tsx
@@ -478,7 +478,7 @@ export function ComputeContourSettingsWindow({
{network?.broker.bind_address ?? "—"}
-
Worker 006
+ {contour?.display_name ?? "Worker"}
{network?.worker.reachable
? `${network.worker.mqtt_host ?? "—"}:${network.worker.mqtt_port ?? "—"}`
: "Недоступен"}
diff --git a/apps/control-station/src/components/system/useComputeContourSettings.tsx b/apps/control-station/src/components/system/useComputeContourSettings.tsx
index 7c12406..a1592fa 100644
--- a/apps/control-station/src/components/system/useComputeContourSettings.tsx
+++ b/apps/control-station/src/components/system/useComputeContourSettings.tsx
@@ -28,7 +28,7 @@ export function useComputeContourSettings(): ComputeContourSettingsController {
{
label: "Обновить телеметрию выбранного контура",
icon: "refresh",
- disabled: contours.selectedContour?.contour_id !== "worker-006",
+ disabled: contours.selectedContour === null,
onClick: contours.refreshTelemetry,
},
{
diff --git a/apps/control-station/src/core/system/useWorkerTelemetry.ts b/apps/control-station/src/core/system/useWorkerTelemetry.ts
index 2f32f55..6047289 100644
--- a/apps/control-station/src/core/system/useWorkerTelemetry.ts
+++ b/apps/control-station/src/core/system/useWorkerTelemetry.ts
@@ -48,7 +48,11 @@ export function useWorkerTelemetry(
setError(null);
} catch (reason: unknown) {
if (controller.signal.aborted) return;
- setError(reason instanceof Error ? reason.message : "Worker 006 не ответил.");
+ setError(
+ reason instanceof Error
+ ? reason.message
+ : "Вычислительный контур не ответил.",
+ );
} finally {
if (!controller.signal.aborted) setLoading(false);
}
diff --git a/apps/control-station/src/workspaces/system/ComputeModulesWorkspace.tsx b/apps/control-station/src/workspaces/system/ComputeModulesWorkspace.tsx
index 94aa2f7..33753f5 100644
--- a/apps/control-station/src/workspaces/system/ComputeModulesWorkspace.tsx
+++ b/apps/control-station/src/workspaces/system/ComputeModulesWorkspace.tsx
@@ -62,8 +62,7 @@ export function ComputeModulesWorkspace() {
selectedContour,
telemetryRefreshGeneration,
} = useComputeContours();
- const legacyDiagnostic = selectedContour?.contour_id === "worker-006"
- && selectedContour.telemetry_mode === "legacy-ssh";
+ const legacyDiagnostic = selectedContour?.telemetry_mode === "legacy-ssh";
const { telemetry, error } = useWorkerTelemetry(
selectedContour?.contour_id ?? null,
(selectedContour?.telemetry_poll_interval_seconds ?? 3) * 1_000,
diff --git a/apps/control-station/src/workspaces/system/NetworkWorkspace.tsx b/apps/control-station/src/workspaces/system/NetworkWorkspace.tsx
index a023add..4a42d22 100644
--- a/apps/control-station/src/workspaces/system/NetworkWorkspace.tsx
+++ b/apps/control-station/src/workspaces/system/NetworkWorkspace.tsx
@@ -20,8 +20,7 @@ export function NetworkWorkspace() {
selectedContour,
telemetryRefreshGeneration,
} = useComputeContours();
- const legacyDiagnostic = selectedContour?.contour_id === "worker-006"
- && selectedContour.telemetry_mode === "legacy-ssh";
+ const legacyDiagnostic = selectedContour?.telemetry_mode === "legacy-ssh";
const { telemetry, error } = useWorkerTelemetry(
selectedContour?.contour_id ?? null,
(selectedContour?.telemetry_poll_interval_seconds ?? 3) * 1_000,
diff --git a/apps/control-station/test/systemTelemetry.test.mjs b/apps/control-station/test/systemTelemetry.test.mjs
index 58c1ccc..2a9d44c 100644
--- a/apps/control-station/test/systemTelemetry.test.mjs
+++ b/apps/control-station/test/systemTelemetry.test.mjs
@@ -9,7 +9,7 @@ async function read(relativePath) {
return readFile(new URL(relativePath, sourceRoot), "utf8");
}
-test("Worker 006 telemetry remains a bounded system feature slice", async () => {
+test("Compute-contour telemetry remains a bounded system feature slice", async () => {
const [
productModel,
workspaceHub,
@@ -88,6 +88,18 @@ test("Worker 006 telemetry remains a bounded system feature slice", async () =>
assert.match(panelActions, /actions\.push\(\.\.\.systemUtilityActions\)/);
assert.match(contourSettingsHook, /icon: "refresh"/);
assert.match(contourSettingsHook, /icon: "settings"/);
+ assert.doesNotMatch(contourSettingsHook, /contour_id\s*!==\s*"worker-006"/);
+ assert.doesNotMatch(
+ computeWorkspace,
+ /contour_id\s*===\s*"worker-006"[\s\S]*telemetry_mode/,
+ );
+ assert.doesNotMatch(
+ networkWorkspace,
+ /contour_id\s*===\s*"worker-006"[\s\S]*telemetry_mode/,
+ );
+ assert.doesNotMatch(contourSettings, /Worker 006<\/dt>/);
+ assert.match(contourSettings, /contour\?\.display_name/);
+ assert.match(telemetryPolling, /Вычислительный контур не ответил\./);
assert.match(contourSettings, /FieldFrame label="Операционная система"/);
assert.match(contourSettings, /label="Обновление интерфейса"/);
assert.match(contourSettings, /label="Интервал MQTT агента"/);
diff --git a/docs/15_LABORATORY_RUN_CANON.md b/docs/15_LABORATORY_RUN_CANON.md
index ae6d063..ae9b0f8 100644
--- a/docs/15_LABORATORY_RUN_CANON.md
+++ b/docs/15_LABORATORY_RUN_CANON.md
@@ -216,6 +216,29 @@ A LAB run may be shown as diagnostic evidence with `legacy-partial` method
metadata. It may be called reproducible or promoted only when the method is
`complete` and its experiment-specific acceptance gates pass.
+## Lifecycle classes and bounded canonicalization
+
+ADR 0039 separates the value of laboratory evidence from the maturity of its
+execution architecture:
+
+- `canonical` is the ordinary reusable path through versioned source, provider,
+ graph and run contracts;
+- `experimental` is an admitted bounded adapter or package for a novel stack or
+ method which the current graph cannot honestly express;
+- `legacy` is immutable historical evidence retained through its existing
+ validator/projector without retroactive provenance.
+
+An experimental LAB is not rejected merely because it does not fit the current
+canonical stack. It still must retain real source/configuration/runtime/artifact
+identities, visual evidence, terminal outcome, limitations and forbidden
+authority. It may not silently become a core dependency. Promotion occurs only
+after repeated value justifies a reusable provider or graph contract.
+
+Mission Core does not bulk-migrate the archive. E33, E35 and E46J are the bounded
+execution canaries. Once they prove one automatic run-to-evidence path, the LAB
+canonicalization slice closes and remaining historical work stays `legacy` unless
+an integrity or product need justifies a targeted migration.
+
## Planning discipline
A planned LAB number is not a placeholder page. Before execution it must name:
diff --git a/docs/16_ARCHITECTURE_AUDIT_EXECUTION_ROADMAP.md b/docs/16_ARCHITECTURE_AUDIT_EXECUTION_ROADMAP.md
index ea67711..3073b91 100644
--- a/docs/16_ARCHITECTURE_AUDIT_EXECUTION_ROADMAP.md
+++ b/docs/16_ARCHITECTURE_AUDIT_EXECUTION_ROADMAP.md
@@ -102,6 +102,30 @@ real authority.
4. Add MCAP only as a normalized P1/P2 sidecar; retain MQTT/fMP4 P0 evidence.
5. Require an independent safety gate before any real actuator authority.
+## Architecture stabilization gate before the next main CV increment
+
+The evidence/report layer is now generic and proof-bound, but historical LAB
+execution remains heterogeneous. ADR 0039 limits the remaining stabilization
+work instead of turning it into a bulk archive migration.
+
+The gate is:
+
+1. Preserve all existing historical LABs as immutable `legacy` evidence.
+2. Admit genuinely novel stacks as bounded `experimental` adapters rather than
+ discarding them or importing them directly into core.
+3. Prove one source/provider/graph/run path on E33, E35 and E46J.
+4. Ensure a new ordinary `canonical` LAB does not add a new branch to the
+ application shell, advanced dispatcher or generic workspace.
+5. Close local telemetry operational debt: multi-contour credential enrollment,
+ executable agent bundle, journal rotation, restart/reconnect/duplicate/stale/
+ identity-mismatch acceptance and run correlation.
+6. Record production/fleet, second-source, Quick Connect, physical mount and
+ live-authority gaps as intentional non-blocking debt with explicit triggers.
+
+A9/E36 remains a later scientific transfer gate. It is not a precondition for
+this architecture stabilization and cannot be executed without a qualified
+second mounted source.
+
## Current implementation slice
- [x] Preserve E29 as the accepted immutable diagnostic baseline.
diff --git a/docs/18_APPLICATION_COMPONENT_ARCHITECTURE.md b/docs/18_APPLICATION_COMPONENT_ARCHITECTURE.md
index 126c9e7..fe2ae4e 100644
--- a/docs/18_APPLICATION_COMPONENT_ARCHITECTURE.md
+++ b/docs/18_APPLICATION_COMPONENT_ARCHITECTURE.md
@@ -179,6 +179,15 @@ Legacy/integrated diagnostic viewers may keep a result interaction inside the
evidence slot only where that viewer is already the admitted result instrument.
This exception does not apply automatically to a new LAB.
+ADR 0039 adds a lifecycle distinction to this code shape. A `canonical` LAB
+enters through the reusable registry and must not add another experiment-named
+branch to `App.tsx`, the generic workspace or the advanced dispatcher. An
+`experimental` LAB may have a bounded adapter or renderer when the current graph
+cannot express its evidence, but the adapter remains feature-local, declares its
+classification and cannot change generic product behavior. A `legacy` LAB keeps
+its existing read-only projector and is not rewritten solely to satisfy the
+latest code shape.
+
## Local ontology decision
Mission Core already has three semantic mechanisms:
@@ -216,6 +225,11 @@ vocabulary and executable contracts. They do not create a second runtime model.
`test/laboratoryProductUi.test.mjs` additionally enforces the versioned LAB
report fields and shared result component across bounded LAB modules.
+The next executable laboratory architecture gate extends these tests to reject
+new unclassified experiment branches while allowing a declared bounded
+experimental adapter. The gate protects core composition; it does not forbid a
+novel research stack.
+
The line limits are ratchets, not quality targets. When a file reaches a limit,
split a feature; do not raise the limit to accommodate unrelated behavior.
diff --git a/docs/adr/0039-bounded-laboratory-canonicalization-and-intentional-debt.md b/docs/adr/0039-bounded-laboratory-canonicalization-and-intentional-debt.md
new file mode 100644
index 0000000..f0c7dab
--- /dev/null
+++ b/docs/adr/0039-bounded-laboratory-canonicalization-and-intentional-debt.md
@@ -0,0 +1,116 @@
+# ADR 0039: Bounded laboratory canonicalization and intentional debt
+
+Date: 2026-08-05
+Status: accepted; executable canary migration remains open
+
+## Context
+
+Mission Core accumulated valuable immutable laboratory evidence while the
+perception contracts, worker boundary and product presentation were still being
+discovered. Rewriting every historical experiment would consume the same effort
+as new product work, risk changing preserved evidence and falsely imply that old
+runs were produced by contracts which did not exist at the time.
+
+The opposite extreme is also unsafe. If every new experiment adds another core
+router, application branch and presentation component, the product composition
+will keep growing with the research history. A novel detector, runtime or
+evaluation method must remain possible without either being discarded or being
+promoted directly into the stable product architecture.
+
+ADR 0036 made standard evidence discovery configurative. ADR 0038 made the
+selected immutable evidence report generic and proof-bound. Neither decision
+made the historical experiment implementations reusable execution graphs.
+
+## Decision
+
+Mission Core admits three laboratory lifecycle classes.
+
+### Canonical
+
+A canonical LAB uses versioned source, provider, graph and run contracts. Its
+runner automatically publishes identity, configuration, method, runtime,
+resources, metrics, gates, artifacts, visual evidence, limitations and retained
+authority. Adding an ordinary canonical LAB must not add an experiment-specific
+branch to the application shell or product composition root.
+
+The first executable canaries are deliberately limited to:
+
+- E33 worker shadow, for source pacing, queues, worker identity and telemetry;
+- E35 degradation/recovery, for deterministic graph variants and safe terminal
+ accounting;
+- E46J raw-fisheye realtime, for model/runtime configuration, GPU capacity and
+ full visual evidence.
+
+Passing those three canaries closes the laboratory canonicalization slice. It
+does not authorize a bulk migration of historical runs.
+
+### Experimental
+
+An experimental LAB may use a stack or execution shape which the current
+canonical graph cannot express. It is retained through a bounded adapter or
+separate experiment package instead of being deleted or hidden.
+
+It must still freeze:
+
+- the immutable source and derivative inputs;
+- code, configuration, model/runtime and provider identities which are known;
+- real artifacts, visual evidence and resource observations;
+- terminal outcome, limitations and forbidden authority.
+
+An experimental adapter may not silently change generic product behavior,
+grant command/navigation/safety authority or require a new visual language.
+Repeated demonstrated value is the trigger for promotion into a versioned
+provider or graph contract. Novelty by itself is not a failure and is not a
+reason to discard evidence.
+
+### Legacy
+
+Legacy LABs remain immutable and read-only through their existing validators and
+bounded projectors. Missing historical provenance remains explicit. Legacy code
+is changed only for integrity, security, compatibility or preservation defects;
+it is not rewritten for cosmetic uniformity.
+
+Legacy evidence may be compared or reused as an input reference, but it does not
+become canonical execution evidence retroactively.
+
+## Core admission rule
+
+Architecture tests must prevent uncontrolled core growth, not experimental
+work itself. A failure is one of the following:
+
+- a canonical LAB needs a new experiment-named branch in `app.py`, the advanced
+ LAB dispatcher or the generic workspace;
+- evidence publication bypasses identity, artifact or authority validation;
+- reusable provider or graph behavior is copied into another experiment;
+- an experimental implementation is imported by core without an explicit
+ bounded adapter and lifecycle classification.
+
+A self-contained experimental package with honest evidence is allowed even when
+its stack cannot yet satisfy the canonical graph.
+
+## Worker and telemetry boundary
+
+The compute-contour catalog, selector, generic MQTT topic namespace and telemetry
+query path already support more than one contour. They do not constitute worker
+provisioning. Credential enrollment, a digest-bound agent bundle and an accepted
+second agent remain open. Installing GPU drivers, Docker/WSL, Triton, models and
+the perception runtime is a separate deployment-profile problem and is not folded
+into a universal one-click installer.
+
+Local telemetry stabilization is part of the architecture freeze: multi-contour
+credential issuance, executable agent installation, bounded journal rotation,
+restart/reconnect/duplicate handling, stale and identity-mismatch behavior, and
+run correlation must be accepted. Fleet TLS, Linux acceptance, HA and production
+storage/deployment remain explicit intentional debt until their deployment trigger
+exists.
+
+## Consequences
+
+- The historical archive remains useful without dictating current code shape.
+- Three representative LABs, not the whole archive, prove the executable canon.
+- A new research stack has a legitimate bounded path and is not forced into a
+ premature universal abstraction.
+- The product core grows only when repeated value justifies a reusable contract.
+- Debt is not treated as a binary pass/fail list: every deferred item names the
+ claim it blocks and the event which returns it to the critical path.
+