engineering-platform 2.2.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (130) hide show
  1. engineering_platform/ENGINEERING_PLATFORM_CONFIG.json +32 -0
  2. engineering_platform/ENGINEERING_PLATFORM_VERSION.json +15 -0
  3. engineering_platform/__init__.py +1 -0
  4. engineering_platform/__main__.py +7 -0
  5. engineering_platform/agent_state.py +530 -0
  6. engineering_platform/agent_trust.py +174 -0
  7. engineering_platform/assets/dashboard.css +1317 -0
  8. engineering_platform/assets/dashboard.js +8534 -0
  9. engineering_platform/assets/dashboard_locales.mjs +4049 -0
  10. engineering_platform/assets/dashboard_status_store.mjs +41 -0
  11. engineering_platform/assets/operations-console/apple-touch-icon-dark.png +0 -0
  12. engineering_platform/assets/operations-console/apple-touch-icon-light.png +0 -0
  13. engineering_platform/assets/operations-console/icon-dark.png +0 -0
  14. engineering_platform/assets/operations-console/icon-light.png +0 -0
  15. engineering_platform/assets/operations-console/icon-transparent.png +0 -0
  16. engineering_platform/assets/operations-console/manifest.webmanifest +11 -0
  17. engineering_platform/capability_preflight.py +285 -0
  18. engineering_platform/capability_review.py +261 -0
  19. engineering_platform/central_data_transfer.py +195 -0
  20. engineering_platform/central_database.py +245 -0
  21. engineering_platform/central_store_migration.py +1672 -0
  22. engineering_platform/codex_capacity.py +81 -0
  23. engineering_platform/codex_chat.py +226 -0
  24. engineering_platform/codex_observability.py +153 -0
  25. engineering_platform/component_lock.py +40 -0
  26. engineering_platform/component_logging.py +420 -0
  27. engineering_platform/console_presentation.py +14 -0
  28. engineering_platform/console_route_ownership.py +83 -0
  29. engineering_platform/contracts/__init__.py +38 -0
  30. engineering_platform/contracts/ep_consumer.py +391 -0
  31. engineering_platform/contracts/models.py +105 -0
  32. engineering_platform/contracts/projection.py +401 -0
  33. engineering_platform/dashboard_browser_validation.py +206 -0
  34. engineering_platform/dashboard_state.py +630 -0
  35. engineering_platform/dashboard_supervisor.swift +105 -0
  36. engineering_platform/dashboard_translation.py +129 -0
  37. engineering_platform/dependabot_producer.py +349 -0
  38. engineering_platform/drift_diagnostics.py +144 -0
  39. engineering_platform/emergency_recovery.py +268 -0
  40. engineering_platform/engineering_memory.py +139 -0
  41. engineering_platform/ep_consumer_credentials.py +473 -0
  42. engineering_platform/evidence_projection.py +213 -0
  43. engineering_platform/execution_activity.py +218 -0
  44. engineering_platform/execution_context.py +132 -0
  45. engineering_platform/execution_errors.py +42 -0
  46. engineering_platform/execution_evidence.py +24 -0
  47. engineering_platform/execution_executor.py +730 -0
  48. engineering_platform/execution_finalization.py +44 -0
  49. engineering_platform/execution_host.py +3306 -0
  50. engineering_platform/execution_lease.py +365 -0
  51. engineering_platform/execution_lifecycle.py +447 -0
  52. engineering_platform/execution_models.py +43 -0
  53. engineering_platform/execution_readiness.py +166 -0
  54. engineering_platform/execution_reporting.py +1607 -0
  55. engineering_platform/execution_repository.py +253 -0
  56. engineering_platform/execution_timeout_policy.py +56 -0
  57. engineering_platform/execution_timing.py +440 -0
  58. engineering_platform/execution_transaction.py +28 -0
  59. engineering_platform/external_producer_binding.py +235 -0
  60. engineering_platform/file_inbox.py +249 -0
  61. engineering_platform/forensic_attribution.py +338 -0
  62. engineering_platform/forensic_attribution_v2.py +134 -0
  63. engineering_platform/forensic_delta.py +299 -0
  64. engineering_platform/golden_scenario.py +63 -0
  65. engineering_platform/historical_dashboard_configuration.py +171 -0
  66. engineering_platform/host_admin.py +199 -0
  67. engineering_platform/host_preflight.py +231 -0
  68. engineering_platform/installation_relocation.py +122 -0
  69. engineering_platform/investigation_ledger.py +89 -0
  70. engineering_platform/legacy_inbox_migration.py +79 -0
  71. engineering_platform/lifecycle_worker.py +223 -0
  72. engineering_platform/live_status.py +267 -0
  73. engineering_platform/local_api.py +209 -0
  74. engineering_platform/local_api_keychain.py +51 -0
  75. engineering_platform/local_repository_binding.py +138 -0
  76. engineering_platform/managed_autonomy.py +509 -0
  77. engineering_platform/managed_codex_runtime.py +105 -0
  78. engineering_platform/parity_context.py +203 -0
  79. engineering_platform/parity_lifecycle_dispatcher.py +488 -0
  80. engineering_platform/platform_admin.py +13 -0
  81. engineering_platform/platform_api.py +428 -0
  82. engineering_platform/platform_bootstrap.py +385 -0
  83. engineering_platform/platform_components.py +65 -0
  84. engineering_platform/platform_version.py +171 -0
  85. engineering_platform/pr_check_repair.py +276 -0
  86. engineering_platform/pr_evidence_backfill.py +278 -0
  87. engineering_platform/producer.py +209 -0
  88. engineering_platform/project_agent.py +366 -0
  89. engineering_platform/project_agent_service.py +244 -0
  90. engineering_platform/project_topology.py +126 -0
  91. engineering_platform/prompt_history.py +591 -0
  92. engineering_platform/provider_context.py +136 -0
  93. engineering_platform/provider_context_benchmark.py +41 -0
  94. engineering_platform/provider_context_scope.py +90 -0
  95. engineering_platform/provider_interruption.py +168 -0
  96. engineering_platform/provider_process_identity.py +80 -0
  97. engineering_platform/provider_readiness.py +138 -0
  98. engineering_platform/provider_recovery.py +647 -0
  99. engineering_platform/provider_usage.py +497 -0
  100. engineering_platform/providers.py +471 -0
  101. engineering_platform/qualification.py +220 -0
  102. engineering_platform/recommendation_handoff.py +238 -0
  103. engineering_platform/report_analysis.py +193 -0
  104. engineering_platform/repository_attachment.py +171 -0
  105. engineering_platform/repository_handoff.py +95 -0
  106. engineering_platform/resources.py +38 -0
  107. engineering_platform/reviewer_evidence.py +70 -0
  108. engineering_platform/schemas/repository-attachment.schema.json +61 -0
  109. engineering_platform/server.py +3679 -0
  110. engineering_platform/server_console_services.py +2024 -0
  111. engineering_platform/server_relay.py +172 -0
  112. engineering_platform/server_service.py +122 -0
  113. engineering_platform/status_model.py +135 -0
  114. engineering_platform/status_reconciliation.py +34 -0
  115. engineering_platform/storage.py +2440 -0
  116. engineering_platform/submission_cli.py +77 -0
  117. engineering_platform/submission_intake.py +45 -0
  118. engineering_platform/submission_service.py +317 -0
  119. engineering_platform/telemetry.py +951 -0
  120. engineering_platform/templates/workspace-config.json +25 -0
  121. engineering_platform/validation_identity.py +50 -0
  122. engineering_platform/validation_profile.py +211 -0
  123. engineering_platform/workspace_preflight.py +263 -0
  124. engineering_platform/worktree_provenance.py +147 -0
  125. engineering_platform/worktree_tooling.py +18 -0
  126. engineering_platform-2.2.0.dist-info/METADATA +18 -0
  127. engineering_platform-2.2.0.dist-info/RECORD +130 -0
  128. engineering_platform-2.2.0.dist-info/WHEEL +5 -0
  129. engineering_platform-2.2.0.dist-info/entry_points.txt +6 -0
  130. engineering_platform-2.2.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,195 @@
1
+ """Validated export and deferred import of durable platform data.
2
+
3
+ An export intentionally contains *state*, not the installed Python runtime or
4
+ live process files. This lets a clean installation on another Mac retain its
5
+ own qualified runtime while receiving the complete CENTRAL state.
6
+ """
7
+ from __future__ import annotations
8
+
9
+ from datetime import datetime, timezone
10
+ import hashlib
11
+ import json
12
+ import os
13
+ from pathlib import Path, PurePosixPath
14
+ import shutil
15
+ import tempfile
16
+ from uuid import uuid4
17
+ import zipfile
18
+
19
+ from . import central_database
20
+
21
+
22
+ FORMAT_VERSION = 1
23
+ MANIFEST_NAME = "central-data-manifest.json"
24
+ EXCLUDED_TOP_LEVEL = frozenset({"runtime"})
25
+ MAX_ARCHIVE_BYTES = 2 * 1024 * 1024 * 1024
26
+ MAX_ARCHIVE_MEMBERS = 100_000
27
+
28
+
29
+ class CentralDataTransferError(ValueError):
30
+ """An archive is malformed, unsafe, or unavailable for transfer."""
31
+
32
+
33
+ def _sha256(content: bytes) -> str:
34
+ return hashlib.sha256(content).hexdigest()
35
+
36
+
37
+ def _safe_member(name: str) -> PurePosixPath:
38
+ path = PurePosixPath(name)
39
+ if not name or path.is_absolute() or ".." in path.parts or path.parts[0] in EXCLUDED_TOP_LEVEL:
40
+ raise CentralDataTransferError("CENTRAL_ARCHIVE_MEMBER_INVALID")
41
+ return path
42
+
43
+
44
+ def _durable_files(data_root: Path) -> list[tuple[PurePosixPath, Path]]:
45
+ """List durable files by logical path, dereferencing legacy local links."""
46
+ root = data_root.resolve()
47
+ files: list[tuple[PurePosixPath, Path]] = []
48
+ for top_level in sorted(root.iterdir(), key=lambda item: item.name):
49
+ if top_level.name in EXCLUDED_TOP_LEVEL:
50
+ continue
51
+ logical = PurePosixPath(top_level.name)
52
+ resolved = top_level.resolve()
53
+ if resolved.is_file():
54
+ files.append((logical, resolved))
55
+ elif resolved.is_dir():
56
+ for item in sorted(resolved.rglob("*")):
57
+ if item.is_file():
58
+ files.append((logical / item.relative_to(resolved).as_posix(), item.resolve()))
59
+ return files
60
+
61
+
62
+ def export_snapshot(data_root: Path) -> tuple[str, bytes]:
63
+ """Create a portable immutable archive while Server writers are quiesced."""
64
+ root = data_root.resolve()
65
+ database = central_database.snapshot(root)
66
+ if database is None:
67
+ raise CentralDataTransferError("CENTRAL_DATABASE_UNAVAILABLE")
68
+ entries: list[dict[str, object]] = []
69
+ with tempfile.SpooledTemporaryFile(max_size=32 * 1024 * 1024, mode="w+b") as output:
70
+ with zipfile.ZipFile(output, "w", compression=zipfile.ZIP_DEFLATED, compresslevel=9) as archive:
71
+ for logical, source in _durable_files(root):
72
+ content = database if logical.as_posix() == "engineering.db" else source.read_bytes()
73
+ archive.writestr(logical.as_posix(), content)
74
+ entries.append({"path": logical.as_posix(), "sha256": _sha256(content), "size": len(content)})
75
+ manifest = {
76
+ "format_version": FORMAT_VERSION,
77
+ "created_at": datetime.now(timezone.utc).isoformat(),
78
+ "kind": "engineering-platform-central-data",
79
+ "entries": entries,
80
+ }
81
+ archive.writestr(MANIFEST_NAME, json.dumps(manifest, sort_keys=True, separators=(",", ":")))
82
+ output.seek(0)
83
+ content = output.read()
84
+ filename = f"engineering-platform-central-data-{datetime.now(timezone.utc).strftime('%Y%m%dT%H%M%SZ')}.zip"
85
+ return filename, content
86
+
87
+
88
+ def inspect_archive(path: Path) -> dict[str, object]:
89
+ """Validate an archive completely before it can become a pending import."""
90
+ if not path.is_file() or path.stat().st_size > MAX_ARCHIVE_BYTES:
91
+ raise CentralDataTransferError("CENTRAL_ARCHIVE_SIZE_INVALID")
92
+ try:
93
+ with zipfile.ZipFile(path) as archive:
94
+ infos = archive.infolist()
95
+ if len(infos) > MAX_ARCHIVE_MEMBERS or any(item.is_dir() for item in infos):
96
+ raise CentralDataTransferError("CENTRAL_ARCHIVE_MEMBER_INVALID")
97
+ names = {item.filename for item in infos}
98
+ if MANIFEST_NAME not in names or len(names) != len(infos):
99
+ raise CentralDataTransferError("CENTRAL_ARCHIVE_MANIFEST_INVALID")
100
+ manifest = json.loads(archive.read(MANIFEST_NAME).decode("utf-8"))
101
+ if not isinstance(manifest, dict) or manifest.get("format_version") != FORMAT_VERSION or manifest.get("kind") != "engineering-platform-central-data":
102
+ raise CentralDataTransferError("CENTRAL_ARCHIVE_MANIFEST_INVALID")
103
+ entries = manifest.get("entries")
104
+ if not isinstance(entries, list) or not entries:
105
+ raise CentralDataTransferError("CENTRAL_ARCHIVE_MANIFEST_INVALID")
106
+ expected: dict[str, dict[str, object]] = {}
107
+ for entry in entries:
108
+ if not isinstance(entry, dict) or not isinstance(entry.get("path"), str) or not isinstance(entry.get("sha256"), str) or not isinstance(entry.get("size"), int):
109
+ raise CentralDataTransferError("CENTRAL_ARCHIVE_MANIFEST_INVALID")
110
+ name = _safe_member(entry["path"]).as_posix()
111
+ expected[name] = entry
112
+ if set(expected) != names - {MANIFEST_NAME} or "engineering.db" not in expected:
113
+ raise CentralDataTransferError("CENTRAL_ARCHIVE_MANIFEST_INVALID")
114
+ for name, entry in expected.items():
115
+ content = archive.read(name)
116
+ if len(content) != entry["size"] or _sha256(content) != entry["sha256"]:
117
+ raise CentralDataTransferError("CENTRAL_ARCHIVE_INTEGRITY_INVALID")
118
+ except (OSError, zipfile.BadZipFile, UnicodeDecodeError, json.JSONDecodeError) as error:
119
+ raise CentralDataTransferError("CENTRAL_ARCHIVE_INVALID") from error
120
+ return {"entries": len(expected), "size_bytes": path.stat().st_size}
121
+
122
+
123
+ def stage_import(data_root: Path, source: Path) -> dict[str, object]:
124
+ """Copy a validated upload into runtime-owned staging for a clean restart."""
125
+ root = data_root.resolve()
126
+ imports = root / "runtime" / "central-data-imports"
127
+ imports.mkdir(mode=0o700, parents=True, exist_ok=True)
128
+ target = imports / f"{uuid4().hex}.zip"
129
+ shutil.copyfile(source, target)
130
+ target.chmod(0o600)
131
+ details = inspect_archive(target)
132
+ pending = root / "runtime" / "pending-central-data-import.json"
133
+ if pending.exists():
134
+ target.unlink(missing_ok=True)
135
+ raise CentralDataTransferError("CENTRAL_IMPORT_ALREADY_PENDING")
136
+ temporary = pending.with_name(f".{pending.name}.{uuid4().hex}")
137
+ try:
138
+ temporary.write_text(json.dumps({"archive": str(target)}, sort_keys=True), encoding="utf-8")
139
+ temporary.chmod(0o600)
140
+ os.replace(temporary, pending)
141
+ finally:
142
+ temporary.unlink(missing_ok=True)
143
+ return details
144
+
145
+
146
+ def apply_pending_import(data_root: Path) -> dict[str, object] | None:
147
+ """Replace durable state before initialization starts any writer.
148
+
149
+ The current runtime stays in place, which is essential when restoring on a
150
+ clean host with its own compatible EP installation.
151
+ """
152
+ root = data_root.resolve()
153
+ pending = root / "runtime" / "pending-central-data-import.json"
154
+ if not pending.exists():
155
+ return None
156
+ try:
157
+ payload = json.loads(pending.read_text(encoding="utf-8"))
158
+ archive_path = Path(payload["archive"])
159
+ imports = (root / "runtime" / "central-data-imports").resolve()
160
+ if imports not in archive_path.resolve().parents:
161
+ raise CentralDataTransferError("CENTRAL_IMPORT_REQUEST_INVALID")
162
+ details = inspect_archive(archive_path)
163
+ staging = Path(tempfile.mkdtemp(prefix=".central-data-import-", dir=root.parent))
164
+ retired = root.with_name(f".{root.name}.pre-import-{uuid4().hex}")
165
+ try:
166
+ with zipfile.ZipFile(archive_path) as archive:
167
+ manifest = json.loads(archive.read(MANIFEST_NAME).decode("utf-8"))
168
+ for entry in manifest["entries"]:
169
+ logical = _safe_member(entry["path"])
170
+ target = staging.joinpath(*logical.parts)
171
+ target.parent.mkdir(mode=0o700, parents=True, exist_ok=True)
172
+ target.write_bytes(archive.read(logical.as_posix()))
173
+ target.chmod(0o600)
174
+ # Switch a fully prepared replacement directory, then transplant
175
+ # only the target host's installed runtime. A failure restores the
176
+ # original root rather than leaving a partially copied state.
177
+ os.replace(root, retired)
178
+ try:
179
+ os.replace(staging, root)
180
+ os.replace(retired / "runtime", root / "runtime")
181
+ except Exception:
182
+ if root.exists():
183
+ if (root / "runtime").exists() and not (retired / "runtime").exists():
184
+ os.replace(root / "runtime", retired / "runtime")
185
+ shutil.rmtree(root)
186
+ os.replace(retired, root)
187
+ raise
188
+ shutil.rmtree(retired)
189
+ finally:
190
+ shutil.rmtree(staging, ignore_errors=True)
191
+ except (KeyError, TypeError, json.JSONDecodeError) as error:
192
+ raise CentralDataTransferError("CENTRAL_IMPORT_REQUEST_INVALID") from error
193
+ pending.unlink(missing_ok=True)
194
+ archive_path.unlink(missing_ok=True)
195
+ return details
@@ -0,0 +1,245 @@
1
+ """Installation-owned CENTRAL database inspection, backup, and maintenance."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from datetime import datetime, timedelta, timezone
6
+ import json
7
+ import os
8
+ from pathlib import Path
9
+ import sqlite3
10
+ import tempfile
11
+
12
+
13
+ DATABASE_FILENAME = "engineering.db"
14
+ MAINTENANCE_INTERVAL_KEY = "central_database.maintenance_interval_seconds"
15
+ MAINTENANCE_LAST_ATTEMPT_KEY = "central_database.maintenance_last_attempt_at"
16
+ PROVIDER_CAPACITY_HISTORY_KEY = "ep.provider_capacity_history.v1"
17
+ CODEX_CAPACITY_RESERVE_KEY = "ep.codex_capacity_reserve_percent"
18
+ DEFAULT_MAINTENANCE_INTERVAL_SECONDS = 60 * 60
19
+ MAINTENANCE_INTERVAL_OPTIONS = frozenset({60, 60 * 60, 24 * 60 * 60, 7 * 24 * 60 * 60})
20
+ CODEX_CAPACITY_RESERVE_OPTIONS = frozenset({0, 5, 10, 15, 20, 25, 50, 75})
21
+ CONSOLE_CONFIGURATION_OPTIONS = {
22
+ "log_retention_days": frozenset({30, 60, 90, 120, 180, 360}), "telemetry_retention_days": frozenset({30, 60, 90, 120, 180, 360}),
23
+ "log_level": frozenset({"INFO", "DEBUG"}), "inbox_scan_interval_seconds": frozenset({5, 15, 30, 60}),
24
+ "open_pr_check_interval_seconds": frozenset({30, 60}), "dashboard_stream_interval_seconds": frozenset(range(1, 11)),
25
+ "provider_readiness_refresh_seconds": frozenset({60, 300, 600}), "platform_health_refresh_seconds": frozenset({5, 15, 30, 60}),
26
+ "component_details_refresh_seconds": frozenset({5, 15, 30, 60}),
27
+ }
28
+ CONSOLE_CONFIGURATION_DEFAULTS = {"log_retention_days":30,"telemetry_retention_days":90,"log_level":"INFO","inbox_scan_interval_seconds":15,"open_pr_check_interval_seconds":30,"dashboard_stream_interval_seconds":1,"provider_readiness_refresh_seconds":300,"platform_health_refresh_seconds":15,"component_details_refresh_seconds":5}
29
+
30
+
31
+ def path(data_root: Path) -> Path:
32
+ return data_root.resolve() / DATABASE_FILENAME
33
+
34
+
35
+ def _schema_version(connection: sqlite3.Connection) -> int:
36
+ row = connection.execute("SELECT MAX(version) FROM engineering_schema_migrations").fetchone()
37
+ return int(row[0]) if row and row[0] is not None else 0
38
+
39
+
40
+ def details(data_root: Path) -> dict[str, object]:
41
+ """Read CENTRAL identity facts without creating or mutating it."""
42
+ database = path(data_root)
43
+ result: dict[str, object] = {"path": str(database), "size_bytes": 0, "schema_version": 0, "integrity": "UNAVAILABLE"}
44
+ try:
45
+ result["size_bytes"] = database.stat().st_size
46
+ with sqlite3.connect(f"file:{database}?mode=ro", uri=True) as connection:
47
+ result["schema_version"] = _schema_version(connection)
48
+ result["integrity"] = "PASS" if [str(row[0]) for row in connection.execute("PRAGMA integrity_check")] == ["ok"] else "FAILED"
49
+ except (OSError, sqlite3.DatabaseError):
50
+ pass
51
+ return result
52
+
53
+
54
+ def snapshot(data_root: Path) -> bytes | None:
55
+ """Return a consistent, read-only backup of the one CENTRAL database."""
56
+ database = path(data_root)
57
+ if not database.is_file():
58
+ return None
59
+ temporary_path: Path | None = None
60
+ try:
61
+ with tempfile.NamedTemporaryFile(prefix="ep-central-backup-", suffix=".db", delete=False) as temporary:
62
+ temporary_path = Path(temporary.name)
63
+ with sqlite3.connect(f"file:{database}?mode=ro", uri=True) as source, sqlite3.connect(temporary_path) as backup:
64
+ source.backup(backup)
65
+ return temporary_path.read_bytes()
66
+ except (OSError, sqlite3.DatabaseError):
67
+ return None
68
+ finally:
69
+ if temporary_path is not None:
70
+ temporary_path.unlink(missing_ok=True)
71
+
72
+
73
+ def maintenance_configuration(data_root: Path) -> dict[str, int]:
74
+ try:
75
+ with sqlite3.connect(f"file:{path(data_root)}?mode=ro", uri=True) as connection:
76
+ row = connection.execute("SELECT value FROM engineering_metadata WHERE key=?", (MAINTENANCE_INTERVAL_KEY,)).fetchone()
77
+ except (OSError, sqlite3.DatabaseError):
78
+ return {"interval_seconds": DEFAULT_MAINTENANCE_INTERVAL_SECONDS}
79
+ try:
80
+ value = int(json.loads(row[0])) if row else DEFAULT_MAINTENANCE_INTERVAL_SECONDS
81
+ except (TypeError, ValueError, json.JSONDecodeError):
82
+ value = DEFAULT_MAINTENANCE_INTERVAL_SECONDS
83
+ return {"interval_seconds": value if value in MAINTENANCE_INTERVAL_OPTIONS else DEFAULT_MAINTENANCE_INTERVAL_SECONDS}
84
+
85
+
86
+ def update_maintenance_configuration(data_root: Path, interval_seconds: object) -> dict[str, int]:
87
+ if not isinstance(interval_seconds, int) or isinstance(interval_seconds, bool) or interval_seconds not in MAINTENANCE_INTERVAL_OPTIONS:
88
+ raise ValueError("CENTRAL_DATABASE_MAINTENANCE_INTERVAL_INVALID")
89
+ with sqlite3.connect(path(data_root)) as connection:
90
+ previous = maintenance_configuration(data_root)["interval_seconds"]
91
+ connection.execute("INSERT INTO engineering_metadata(key,value) VALUES(?,?) ON CONFLICT(key) DO UPDATE SET value=excluded.value", (MAINTENANCE_INTERVAL_KEY, json.dumps(interval_seconds)))
92
+ return {"previous": previous, "interval_seconds": interval_seconds}
93
+
94
+
95
+ def capacity_configuration(data_root: Path) -> dict[str, int]:
96
+ """Return the one installation-wide admission reserve for Codex capacity."""
97
+ try:
98
+ with sqlite3.connect(f"file:{path(data_root)}?mode=ro", uri=True) as connection:
99
+ row = connection.execute("SELECT value FROM engineering_metadata WHERE key=?", (CODEX_CAPACITY_RESERVE_KEY,)).fetchone()
100
+ value = int(json.loads(row[0])) if row else 0
101
+ except (OSError, sqlite3.DatabaseError, TypeError, ValueError, json.JSONDecodeError):
102
+ value = 0
103
+ return {"codex_capacity_reserve_percent": value if value in CODEX_CAPACITY_RESERVE_OPTIONS else 0}
104
+
105
+
106
+ def capacity_reserve_from_environment() -> int:
107
+ """Resolve the active Server's platform policy for worker-side admission."""
108
+ configured_root = os.environ.get("EP_SERVER_DATA_ROOT")
109
+ if not configured_root:
110
+ return 0
111
+ return capacity_configuration(Path(configured_root))["codex_capacity_reserve_percent"]
112
+
113
+
114
+ def update_capacity_configuration(data_root: Path, reserve_percent: object) -> dict[str, int]:
115
+ """Persist an EP-owned reserve; projects cannot choose different limits."""
116
+ if not isinstance(reserve_percent, int) or isinstance(reserve_percent, bool) or reserve_percent not in CODEX_CAPACITY_RESERVE_OPTIONS:
117
+ raise ValueError("CODEX_CAPACITY_RESERVE_INVALID")
118
+ previous = capacity_configuration(data_root)["codex_capacity_reserve_percent"]
119
+ with sqlite3.connect(path(data_root)) as connection:
120
+ connection.execute(
121
+ "INSERT INTO engineering_metadata(key,value) VALUES(?,?) ON CONFLICT(key) DO UPDATE SET value=excluded.value",
122
+ (CODEX_CAPACITY_RESERVE_KEY, json.dumps(reserve_percent)),
123
+ )
124
+ return {"previous": previous, "codex_capacity_reserve_percent": reserve_percent}
125
+
126
+
127
+ def console_interval_configuration(data_root: Path) -> dict[str, object]:
128
+ result = dict(CONSOLE_CONFIGURATION_DEFAULTS)
129
+ try:
130
+ with sqlite3.connect(f"file:{path(data_root)}?mode=ro", uri=True) as connection:
131
+ for key, value in connection.execute("SELECT key,value FROM engineering_metadata WHERE key LIKE 'console.%'"):
132
+ name = str(key).removeprefix("console.")
133
+ parsed = json.loads(value)
134
+ if name in result and parsed in CONSOLE_CONFIGURATION_OPTIONS[name]: result[name] = parsed
135
+ except (OSError, sqlite3.DatabaseError, TypeError, ValueError, json.JSONDecodeError):
136
+ pass
137
+ return result
138
+
139
+
140
+ def update_console_interval_configuration(data_root: Path, key: object, value: object) -> dict[str, object]:
141
+ if not isinstance(key, str) or key not in CONSOLE_CONFIGURATION_OPTIONS or value not in CONSOLE_CONFIGURATION_OPTIONS[key]:
142
+ raise ValueError("CONSOLE_CONFIGURATION_INVALID")
143
+ previous = console_interval_configuration(data_root)[key]
144
+ with sqlite3.connect(path(data_root)) as connection:
145
+ connection.execute("INSERT INTO engineering_metadata(key,value) VALUES(?,?) ON CONFLICT(key) DO UPDATE SET value=excluded.value", ("console." + key, json.dumps(value)))
146
+ return {"key": key, "previous": previous, "value": value}
147
+
148
+
149
+ def record_provider_capacity(
150
+ data_root: Path, *, provider: str, remaining_percent: float, observed_at: datetime | None = None,
151
+ ) -> list[dict[str, object]]:
152
+ """Store a bounded, account-wide two-hour capacity series in CENTRAL."""
153
+ provider = provider.strip()[:120]
154
+ try:
155
+ remaining = float(remaining_percent)
156
+ except (TypeError, ValueError):
157
+ return []
158
+ if not provider or not 0 <= remaining <= 100:
159
+ return []
160
+ timestamp = (observed_at or datetime.now(timezone.utc)).astimezone(timezone.utc)
161
+ bucket = timestamp.replace(hour=timestamp.hour - timestamp.hour % 2, minute=0, second=0, microsecond=0)
162
+ cutoff = bucket - timedelta(days=7)
163
+ try:
164
+ with sqlite3.connect(path(data_root)) as connection:
165
+ row = connection.execute("SELECT value FROM engineering_metadata WHERE key=?", (PROVIDER_CAPACITY_HISTORY_KEY,)).fetchone()
166
+ try:
167
+ payload = json.loads(row[0]) if row else {}
168
+ except (TypeError, ValueError, json.JSONDecodeError):
169
+ payload = {}
170
+ providers = payload.get("providers") if isinstance(payload, dict) else None
171
+ providers = providers if isinstance(providers, dict) else {}
172
+ samples = providers.get(provider)
173
+ samples = samples if isinstance(samples, dict) else {}
174
+ key = bucket.isoformat()
175
+ current = samples.get(key)
176
+ if isinstance(current, (int, float)) and not isinstance(current, bool):
177
+ remaining = min(remaining, float(current))
178
+ samples[key] = remaining
179
+ filtered: dict[str, float] = {}
180
+ for sample_at, sample_value in samples.items():
181
+ try:
182
+ parsed = datetime.fromisoformat(str(sample_at)).astimezone(timezone.utc)
183
+ except ValueError:
184
+ continue
185
+ if parsed >= cutoff and isinstance(sample_value, (int, float)) and not isinstance(sample_value, bool) and 0 <= float(sample_value) <= 100:
186
+ filtered[str(sample_at)] = float(sample_value)
187
+ providers[provider] = filtered
188
+ connection.execute(
189
+ "INSERT INTO engineering_metadata(key,value) VALUES(?,?) ON CONFLICT(key) DO UPDATE SET value=excluded.value",
190
+ (PROVIDER_CAPACITY_HISTORY_KEY, json.dumps({"providers": providers}, separators=(",", ":"))),
191
+ )
192
+ except (OSError, sqlite3.DatabaseError):
193
+ return []
194
+ return provider_capacity_history(data_root, provider=provider)
195
+
196
+
197
+ def provider_capacity_history(data_root: Path, *, provider: str, hours: int = 168) -> list[dict[str, object]]:
198
+ """Read CENTRAL-only provider capacity evidence; it is never project data."""
199
+ provider = provider.strip()[:120]
200
+ if not provider or hours < 1:
201
+ return []
202
+ try:
203
+ with sqlite3.connect(f"file:{path(data_root)}?mode=ro", uri=True) as connection:
204
+ row = connection.execute("SELECT value FROM engineering_metadata WHERE key=?", (PROVIDER_CAPACITY_HISTORY_KEY,)).fetchone()
205
+ payload = json.loads(row[0]) if row else {}
206
+ except (OSError, sqlite3.DatabaseError, TypeError, ValueError, json.JSONDecodeError):
207
+ return []
208
+ samples = payload.get("providers", {}).get(provider, {}) if isinstance(payload, dict) and isinstance(payload.get("providers"), dict) else {}
209
+ cutoff = datetime.now(timezone.utc) - timedelta(hours=hours)
210
+ result: list[dict[str, object]] = []
211
+ if not isinstance(samples, dict):
212
+ return result
213
+ for observed_at, remaining in sorted(samples.items()):
214
+ try:
215
+ parsed = datetime.fromisoformat(str(observed_at)).astimezone(timezone.utc)
216
+ except ValueError:
217
+ continue
218
+ if parsed >= cutoff and isinstance(remaining, (int, float)) and not isinstance(remaining, bool) and 0 <= float(remaining) <= 100:
219
+ result.append({"at": str(observed_at), "remaining_percent": float(remaining)})
220
+ return result
221
+
222
+
223
+ def run_periodic_maintenance(data_root: Path, *, now: datetime | None = None) -> dict[str, object]:
224
+ """Compact CENTRAL only while no lifecycle is active; never touch project stores."""
225
+ moment = (now or datetime.now(timezone.utc)).astimezone(timezone.utc)
226
+ interval = maintenance_configuration(data_root)["interval_seconds"]
227
+ try:
228
+ with sqlite3.connect(path(data_root)) as connection:
229
+ row = connection.execute("SELECT value FROM engineering_metadata WHERE key=?", (MAINTENANCE_LAST_ATTEMPT_KEY,)).fetchone()
230
+ try:
231
+ previous = datetime.fromisoformat(json.loads(row[0]).replace("Z", "+00:00")) if row else None
232
+ except (TypeError, ValueError, json.JSONDecodeError):
233
+ previous = None
234
+ if previous is not None and moment - previous.astimezone(timezone.utc) < timedelta(seconds=interval):
235
+ return {"state": "NOT_DUE"}
236
+ active = connection.execute("SELECT 1 FROM ep_parity_lifecycle_dispatches WHERE state IN ('CLAIMED','RUNNING') LIMIT 1").fetchone()
237
+ if active:
238
+ return {"state": "SKIPPED_ACTIVE_RUN"}
239
+ connection.execute("PRAGMA busy_timeout=1000")
240
+ connection.execute("PRAGMA optimize")
241
+ connection.execute("VACUUM")
242
+ connection.execute("INSERT INTO engineering_metadata(key,value) VALUES(?,?) ON CONFLICT(key) DO UPDATE SET value=excluded.value", (MAINTENANCE_LAST_ATTEMPT_KEY, json.dumps(moment.isoformat())))
243
+ return {"state": "COMPACTED"}
244
+ except (OSError, sqlite3.DatabaseError):
245
+ return {"state": "DEFERRED"}