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. +