diff --git a/package.json b/package.json index 20f00ae..2056a5a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@govoplan/ops-webui", - "version": "0.1.21", + "version": "0.1.22", "private": true, "type": "module", "main": "webui/src/index.ts", @@ -17,7 +17,7 @@ "README.md" ], "peerDependencies": { - "@govoplan/core-webui": "^0.1.18", + "@govoplan/core-webui": "^0.1.45", "lucide-react": "^1.23.0", "react": ">=19.2.7 <20", "react-dom": ">=19.2.7 <20", diff --git a/pyproject.toml b/pyproject.toml index bd6d725..8b61589 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,13 +4,13 @@ build-backend = "setuptools.build_meta" [project] name = "govoplan-ops" -version = "0.1.21" +version = "0.1.22" description = "GovOPlaN operations module for health, deployment profile, and sizing visibility." readme = "README.md" requires-python = ">=3.12" authors = [{ name = "GovOPlaN" }] dependencies = [ - "govoplan-core>=0.1.42", + "govoplan-core>=0.1.45", "govoplan-access>=0.1.18", ] diff --git a/src/govoplan_ops/__init__.py b/src/govoplan_ops/__init__.py index 0061df3..23984a5 100644 --- a/src/govoplan_ops/__init__.py +++ b/src/govoplan_ops/__init__.py @@ -2,4 +2,4 @@ __all__ = ["__version__"] -__version__ = "0.1.21" +__version__ = "0.1.22" diff --git a/src/govoplan_ops/backend/manifest.py b/src/govoplan_ops/backend/manifest.py index 245699a..378c4c2 100644 --- a/src/govoplan_ops/backend/manifest.py +++ b/src/govoplan_ops/backend/manifest.py @@ -100,7 +100,7 @@ def _route_factory(context: ModuleContext): manifest = ModuleManifest( id="ops", name="Ops", - version="0.1.21", + version="0.1.22", required_capabilities=( CAPABILITY_AUTH_PRINCIPAL_RESOLVER, CAPABILITY_AUTH_PERMISSION_EVALUATOR, @@ -209,7 +209,7 @@ manifest = ModuleManifest( DocumentationTopic( id="ops.runtime-coordination-and-recovery", title="Drain runtime nodes and inspect recovery evidence", - summary="Ops projects shared runtime heartbeats, replica gaps, drain controls, and recovery states that require operator attention.", + summary="Ops projects shared runtime heartbeats, replica gaps, drain controls, and recovery states that require operator attention. Node controls remain pinned at the right edge of horizontally scrolled runtime tables.", body="Use the runtime table to identify stale or composition-skewed API and worker replicas. Drain before replacement so API readiness closes and workers stop taking new queue work; cancellation is available while the node is still draining. The recovery table reports durable Core recovery operations. A rejected operation is a verified provider rejection and needs no recovery; outcome-unknown and recovery-required operations still require reconciliation through the owning module. Core module-lifecycle entries block every later install or live graph change: use the installer run id to verify package, backup, migration, and health evidence before rollback or forward repair. Mail SMTP and IMAP APPEND entries use stable attempt identifiers and digest-only evidence: reconcile the Mail command from provider evidence, never by replaying the original effect from Ops. Files blob writes, hard purge, reference-checked garbage collection, and conditional S3 connector writes record Core recovery evidence. For a Files connector outcome, inspect the provider request/content markers and revision before allowing another write to the fenced path; for blob GC, recheck FileVersion references and exact object absence. Development SQLite can show only handled-rollback reconstruction for caller-transaction blob uploads; after a hard SQLite process loss, run the owning Files integrity scan because an orphan may have no Ops ledger row. Dataflow database-only runs are atomic, while published-output runs use forward recovery: reconcile the recorded output digest and sink idempotency key before allowing another publication. Backup status separately projects only the sanitized deployment verification receipt: a verified status identifies a coordinated recovery point and isolated restore drill, while absent, expired, or invalid evidence blocks a release-changing migration.", documentation_types=("admin", "user"), audience=("operator", "system_admin"), @@ -223,7 +223,7 @@ manifest = ModuleManifest( translations={ "de": { "title": "Laufzeitknoten leeren und Wiederherstellungsnachweise prüfen", - "summary": "Ops projiziert gemeinsame Laufzeit-Heartbeats, Replikatlücken, Leerungssteuerung und Wiederherstellungszustände, die betriebliche Aufmerksamkeit erfordern.", + "summary": "Ops projiziert gemeinsame Laufzeit-Heartbeats, Replikatlücken, Leerungssteuerung und Wiederherstellungszustände, die betriebliche Aufmerksamkeit erfordern. Die Knotensteuerung bleibt am rechten Rand horizontal gescrollter Laufzeittabellen angeheftet.", "body": ( "Verwenden Sie die Laufzeittabelle, um veraltete API- und Worker-Replikate oder Replikate mit abweichender Modulzusammensetzung zu erkennen. " "Leeren Sie einen Knoten vor dem Austausch, damit seine API-Bereitschaft geschlossen wird und Worker keine neue Warteschlangenarbeit annehmen; solange der Knoten noch geleert wird, kann der Vorgang abgebrochen werden. " diff --git a/webui/package.json b/webui/package.json index 9d4ee5a..6e06df0 100644 --- a/webui/package.json +++ b/webui/package.json @@ -1,6 +1,6 @@ { "name": "@govoplan/ops-webui", - "version": "0.1.21", + "version": "0.1.22", "private": true, "type": "module", "main": "src/index.ts", @@ -13,10 +13,11 @@ } }, "scripts": { + "test:action-layout": "node scripts/test-action-layout.mjs", "test:runtime-status": "rm -rf .runtime-status-test-build && mkdir -p .runtime-status-test-build && printf '{\"type\":\"commonjs\"}\\n' > .runtime-status-test-build/package.json && ../../govoplan-core/webui/node_modules/.bin/tsc -p tsconfig.runtime-status-tests.json && node .runtime-status-test-build/tests/runtime-status.test.js" }, "peerDependencies": { - "@govoplan/core-webui": "^0.1.18", + "@govoplan/core-webui": "^0.1.45", "lucide-react": "^1.23.0", "react": ">=19.2.7 <20", "react-dom": ">=19.2.7 <20", diff --git a/webui/scripts/test-action-layout.mjs b/webui/scripts/test-action-layout.mjs new file mode 100644 index 0000000..1851ec7 --- /dev/null +++ b/webui/scripts/test-action-layout.mjs @@ -0,0 +1,7 @@ +import assert from "node:assert/strict"; +import { readFileSync } from "node:fs"; + +const page = readFileSync(new URL("../src/features/ops/OpsPage.tsx", import.meta.url), "utf8"); +assert.match(page, /id: "actions",\s*header: "",\s*width: 72,\s*sticky: "end",\s*resizable: false,\s*align: "right"/); +assert.match(page, / node.state, render: (node) => { const busy = busyNodeId === node.node_id;