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,253 @@
1
+ """Repository and GitHub coordination behind canonical providers."""
2
+ from __future__ import annotations
3
+
4
+ import json
5
+ from pathlib import Path
6
+ import re
7
+ import time
8
+ from typing import Protocol
9
+
10
+ from .execution_errors import RunnerError
11
+ from .execution_models import PullRequestEvidence, RepositoryEvidence
12
+ from .providers import GitProvider, GitHubProvider
13
+
14
+
15
+ GIT_SYNC_LOCK_RETRY_ATTEMPTS = 3
16
+ GIT_SYNC_LOCK_RETRY_DELAY_SECONDS = 0.5
17
+ _TRANSIENT_INDEX_LOCK_CONFLICT = re.compile(
18
+ r"(?:index\.lock.*(?:file exists|already exists)|another git process)",
19
+ re.IGNORECASE,
20
+ )
21
+
22
+
23
+ def github_repository_slug(remote: str) -> str:
24
+ """Return the canonical owner/repository slug for GitHub origin forms."""
25
+ value = remote.strip().removesuffix(".git")
26
+ if value.startswith("git@github.com:"):
27
+ return value.removeprefix("git@github.com:")
28
+ if value.startswith("https://github.com/"):
29
+ return value.removeprefix("https://github.com/")
30
+ return value
31
+
32
+
33
+ class RepositoryClient(Protocol):
34
+ def inspect(self, root: Path) -> RepositoryEvidence: ...
35
+ def main_contains(self, root: Path, sha: str) -> bool: ...
36
+ def refresh_main_reference(self, root: Path) -> None: ...
37
+ def remote_main_contains(self, root: Path, sha: str) -> bool: ...
38
+ def synchronize_main(self, root: Path) -> None: ...
39
+
40
+
41
+ class GitHubClient(Protocol):
42
+ def pull_request(self, number: int) -> PullRequestEvidence: ...
43
+ def pull_request_for_head_branch(self, branch: str) -> PullRequestEvidence | None: ...
44
+ def ready(self, number: int) -> None: ...
45
+ def normalize_markdown_body(self, number: int) -> bool: ...
46
+ def merge(self, number: int) -> None: ...
47
+
48
+
49
+ class SubprocessRepositoryClient:
50
+ """Repository workflow implementation; process ownership remains GitProvider."""
51
+ def __init__(self, provider: GitProvider | None = None) -> None:
52
+ self.provider = provider or GitProvider()
53
+
54
+ def _run(self, root: Path, *args: str) -> str:
55
+ try: return self.provider.command(root, *args)
56
+ except RuntimeError as error: raise RunnerError(str(error)) from error
57
+
58
+ def inspect(self, root: Path) -> RepositoryEvidence:
59
+ if not (root / "BOOTSTRAP.md").is_file() or not (root / ".git").exists():
60
+ raise RunnerError("this is not a repository with canonical BOOTSTRAP.md")
61
+ remote = self._run(root, "git", "remote", "get-url", "origin")
62
+ repository = github_repository_slug(remote)
63
+ branch, head_sha = self._run(root, "git", "branch", "--show-current"), self._run(root, "git", "rev-parse", "HEAD")
64
+ clean = not self._run(root, "git", "status", "--porcelain", "--untracked-files=all")
65
+ contained = self.provider.execute(root, "git", "merge-base", "--is-ancestor", head_sha, "main").returncode == 0
66
+ return RepositoryEvidence(repository, branch, head_sha, clean, contained)
67
+
68
+ def main_contains(self, root: Path, sha: str) -> bool:
69
+ return self.provider.execute(root, "git", "merge-base", "--is-ancestor", sha, "main").returncode == 0
70
+
71
+ def refresh_main_reference(self, root: Path) -> None:
72
+ """Refresh remote main evidence without changing the shared checkout."""
73
+ self._synchronize_command(root, "git", "fetch", "origin", "main")
74
+
75
+ def remote_main_contains(self, root: Path, sha: str) -> bool:
76
+ return self.provider.execute(root, "git", "merge-base", "--is-ancestor", sha, "origin/main").returncode == 0
77
+
78
+ @staticmethod
79
+ def _is_transient_index_lock_conflict(error: RuntimeError) -> bool:
80
+ """Recognize only Git's contention signal, never a permission failure."""
81
+ return bool(_TRANSIENT_INDEX_LOCK_CONFLICT.search(str(error)))
82
+
83
+ def _synchronize_command(self, root: Path, *args: str) -> None:
84
+ """Run one synchronization step with bounded, non-destructive lock retries."""
85
+ for attempt in range(1, GIT_SYNC_LOCK_RETRY_ATTEMPTS + 1):
86
+ try:
87
+ self._run(root, *args)
88
+ return
89
+ except RunnerError as error:
90
+ if (
91
+ attempt == GIT_SYNC_LOCK_RETRY_ATTEMPTS
92
+ or not self._is_transient_index_lock_conflict(error)
93
+ ):
94
+ raise
95
+ time.sleep(GIT_SYNC_LOCK_RETRY_DELAY_SECONDS * attempt)
96
+
97
+ def _managed_sync_diagnostics(self, root: Path) -> str:
98
+ """Return bounded, read-only context for a failed managed synchronization."""
99
+ execute = getattr(self.provider, "execute", None)
100
+ if not callable(execute):
101
+ return (
102
+ "target_branch=main authoritative_ref=origin/main local_sha=unavailable "
103
+ "remote_sha=unavailable configured_upstream=unavailable fast_forward_state=unknown"
104
+ )
105
+
106
+ def value(*args: str) -> str:
107
+ try:
108
+ completed = execute(root, "git", *args)
109
+ except (OSError, RuntimeError):
110
+ return "unavailable"
111
+ if completed.returncode:
112
+ return "unavailable"
113
+ candidate = completed.stdout.strip()
114
+ return candidate if re.fullmatch(r"[0-9a-f]{40}|[A-Za-z0-9_./@{}-]+", candidate) else "unavailable"
115
+
116
+ local_sha = value("rev-parse", "--verify", "main")
117
+ remote_sha = value("rev-parse", "--verify", "origin/main")
118
+ upstream = value("rev-parse", "--abbrev-ref", "--symbolic-full-name", "main@{upstream}")
119
+ state = "unknown"
120
+ if local_sha != "unavailable" and remote_sha != "unavailable":
121
+ try:
122
+ local_is_ancestor = execute(
123
+ root, "git", "merge-base", "--is-ancestor", "main", "origin/main"
124
+ ).returncode == 0
125
+ remote_is_ancestor = execute(
126
+ root, "git", "merge-base", "--is-ancestor", "origin/main", "main"
127
+ ).returncode == 0
128
+ except (OSError, RuntimeError):
129
+ pass
130
+ else:
131
+ state = (
132
+ "synchronized" if local_is_ancestor and remote_is_ancestor
133
+ else "fast_forward_possible" if local_is_ancestor
134
+ else "local_ahead" if remote_is_ancestor
135
+ else "diverged"
136
+ )
137
+ return (
138
+ "target_branch=main authoritative_ref=origin/main "
139
+ f"local_sha={local_sha} remote_sha={remote_sha} configured_upstream={upstream} "
140
+ f"fast_forward_state={state}"
141
+ )
142
+
143
+ @staticmethod
144
+ def _managed_sync_error_detail(error: RunnerError) -> str:
145
+ """Keep a Git failure useful without persisting unbounded process output."""
146
+ return re.sub(r"\s+", " ", str(error)).strip()[:512]
147
+
148
+ def synchronize_main(self, root: Path) -> None:
149
+ self._synchronize_command(root, "git", "switch", "main")
150
+ # Managed synchronization has one authority. Do not let any local
151
+ # branch.*.merge, pull.*, or upstream configuration select its source.
152
+ try:
153
+ self._synchronize_command(root, "git", "fetch", "origin", "main")
154
+ except RunnerError as error:
155
+ raise RunnerError(
156
+ "MANAGED_MAIN_FETCH_FAILED: operation=git-fetch-origin-main "
157
+ f"{self._managed_sync_diagnostics(root)}; {self._managed_sync_error_detail(error)}"
158
+ ) from error
159
+ try:
160
+ self._synchronize_command(root, "git", "merge", "--ff-only", "origin/main")
161
+ except RunnerError as error:
162
+ raise RunnerError(
163
+ "MANAGED_MAIN_FAST_FORWARD_FAILED: operation=git-merge-ff-only-origin-main "
164
+ f"{self._managed_sync_diagnostics(root)}; {self._managed_sync_error_detail(error)}"
165
+ ) from error
166
+
167
+ def cleanup_transaction(self, root: Path, branches: tuple[str | None, ...]) -> str:
168
+ self._run(root, "git", "fetch", "--prune"); self.synchronize_main(root)
169
+ if not self.inspect(root).clean: raise RunnerError("Cleanup blocked: workspace is not clean.")
170
+ removed: list[str] = []; squash_reconciled: list[str] = []
171
+ for branch in dict.fromkeys(branch for branch in branches if branch):
172
+ if branch == "main": raise RunnerError("Cleanup blocked: transaction branch resolves to main.")
173
+ if self.provider.execute(root, "git", "show-ref", "--verify", "--quiet", f"refs/heads/{branch}").returncode != 0: continue
174
+ if self.provider.execute(root, "git", "branch", "-d", branch).returncode:
175
+ if self.provider.execute(root, "git", "branch", "-D", branch).returncode:
176
+ raise RunnerError(f"Cleanup blocked: transaction branch {branch} could not be safely removed.")
177
+ squash_reconciled.append(branch)
178
+ removed.append(branch)
179
+ evidence = self.inspect(root)
180
+ if evidence.branch != "main" or not evidence.clean or not evidence.main_contains_head:
181
+ raise RunnerError("Cleanup blocked: main synchronization or workspace verification failed.")
182
+ squash = f"; squash-reconciled={','.join(squash_reconciled)}" if squash_reconciled else ""
183
+ return f"fetched/pruned; main synchronized; removed={','.join(removed) or 'already-absent'}{squash}"
184
+
185
+
186
+ class GhCliClient:
187
+ def __init__(self, provider: GitHubProvider | None = None, repository: str | None = None) -> None:
188
+ self.provider = provider or GitHubProvider()
189
+ self.repository = repository
190
+
191
+ def _github(self, *args: str) -> str:
192
+ scoped = (*args, "--repo", self.repository) if self.repository else args
193
+ return self.provider.github(*scoped)
194
+
195
+ def pull_request(self, number: int) -> PullRequestEvidence:
196
+ try: raw = json.loads(self._github("pr", "view", str(number), "--json", "number,state,isDraft,mergeCommit,statusCheckRollup,headRefName,baseRefName,mergeStateStatus"))
197
+ except RuntimeError as error: raise RunnerError(str(error)) from error
198
+ # GitHub can append an empty rollup entry to an otherwise completed
199
+ # merged PR. It is not a check and must not keep terminal evidence in
200
+ # an endless polling loop.
201
+ checks = [
202
+ item
203
+ for item in (raw.get("statusCheckRollup") or [])
204
+ if isinstance(item, dict) and isinstance(item.get("status"), str)
205
+ ]
206
+ terminal = bool(checks) and all(item.get("status") == "COMPLETED" for item in checks)
207
+ passed = terminal and all(item.get("conclusion") in {"SUCCESS", "NEUTRAL", "SKIPPED"} for item in checks)
208
+ failed = tuple(str(item.get("name") or "unnamed check") for item in checks if item.get("status") == "COMPLETED" and item.get("conclusion") not in {"SUCCESS", "NEUTRAL", "SKIPPED"})
209
+ merge = raw.get("mergeCommit") or {}
210
+ return PullRequestEvidence(
211
+ raw["number"], raw["state"], terminal, passed, merge.get("oid"), raw["isDraft"], failed,
212
+ raw.get("headRefName"), raw.get("baseRefName"), raw.get("mergeStateStatus"),
213
+ )
214
+
215
+ def pull_request_for_head_branch(self, branch: str) -> PullRequestEvidence | None:
216
+ """Return exactly one current PR for an already-checkpointed branch."""
217
+ try:
218
+ raw = json.loads(self._github(
219
+ "pr", "list", "--head", branch, "--state", "all", "--json", "number"
220
+ ))
221
+ except RuntimeError as error:
222
+ raise RunnerError(str(error)) from error
223
+ numbers = [item.get("number") for item in raw if isinstance(item, dict) and isinstance(item.get("number"), int)]
224
+ if not numbers:
225
+ return None
226
+ if len(numbers) != 1:
227
+ raise RunnerError("Finalization recovery found more than one pull request for its checkpointed branch.")
228
+ return self.pull_request(numbers[0])
229
+ def ready(self, number: int) -> None:
230
+ try: self._github("pr", "ready", str(number))
231
+ except RuntimeError as error:
232
+ if "already ready" not in str(error).lower(): raise RunnerError(str(error)) from error
233
+
234
+ def normalize_markdown_body(self, number: int) -> bool:
235
+ """Repair only a fully escaped PR body generated by an agent."""
236
+ try:
237
+ raw = json.loads(self._github("pr", "view", str(number), "--json", "body"))
238
+ except (RuntimeError, json.JSONDecodeError) as error:
239
+ raise RunnerError("Pull request Markdown could not be inspected.") from error
240
+ body = raw.get("body") if isinstance(raw, dict) else None
241
+ # Preserve real Markdown, including code snippets that deliberately
242
+ # contain an escaped newline. Only a one-line serialized body is bad.
243
+ if not isinstance(body, str) or "\n" in body or "\\n" not in body:
244
+ return False
245
+ normalized = body.replace("\\r\\n", "\n").replace("\\n", "\n")
246
+ try:
247
+ self._github("pr", "edit", str(number), "--body", normalized)
248
+ except RuntimeError as error:
249
+ raise RunnerError("Pull request Markdown could not be normalized.") from error
250
+ return True
251
+ def merge(self, number: int) -> None:
252
+ try: self._github("pr", "merge", str(number), "--squash", "--delete-branch")
253
+ except RuntimeError as error: raise RunnerError(str(error)) from error
@@ -0,0 +1,56 @@
1
+ """Versioned, host-owned time limits for bounded provider work.
2
+
3
+ The limits are deliberately not dashboard preferences. They cap autonomous
4
+ provider authority and keep a provider process that leaves its output open
5
+ from owning a project queue indefinitely.
6
+ """
7
+ from __future__ import annotations
8
+
9
+ from dataclasses import dataclass
10
+
11
+
12
+ POLICY_VERSION = "provider-timeouts-v1"
13
+
14
+
15
+ @dataclass(frozen=True)
16
+ class ProviderTimeout:
17
+ """One named execution boundary and its immutable maximum duration."""
18
+
19
+ key: str
20
+ seconds: int
21
+
22
+
23
+ SPECIALIST_REVIEW = ProviderTimeout("specialist_review", 5 * 60)
24
+ IMPLEMENTATION = ProviderTimeout("implementation", 15 * 60)
25
+ LOCAL_REPOSITORY_VALIDATION = ProviderTimeout("local_repository_validation", 15 * 60)
26
+ AUTONOMOUS_QUALITY_CONTROL = ProviderTimeout("autonomous_quality_control", 10 * 60)
27
+ REPAIR = ProviderTimeout("repair", 15 * 60)
28
+ FINALIZATION = ProviderTimeout("finalization", 15 * 60)
29
+ END_RECONCILIATION = ProviderTimeout("end_reconciliation", 10 * 60)
30
+
31
+ WORKFLOW_TIMEOUTS = (
32
+ SPECIALIST_REVIEW,
33
+ IMPLEMENTATION,
34
+ LOCAL_REPOSITORY_VALIDATION,
35
+ AUTONOMOUS_QUALITY_CONTROL,
36
+ REPAIR,
37
+ FINALIZATION,
38
+ END_RECONCILIATION,
39
+ )
40
+
41
+
42
+ def agent_timeout(*, phase: str, repair: bool = False, quality: bool = False,
43
+ local_validation: bool = False) -> ProviderTimeout:
44
+ """Return the deadline for one primary-provider workflow action."""
45
+ if repair:
46
+ return REPAIR
47
+ if local_validation:
48
+ return LOCAL_REPOSITORY_VALIDATION
49
+ if quality:
50
+ return AUTONOMOUS_QUALITY_CONTROL
51
+ normalized = phase.upper()
52
+ if normalized == "FINALIZATION":
53
+ return FINALIZATION
54
+ if normalized in {"RECONCILE_AGENT", "END_RECONCILIATION"}:
55
+ return END_RECONCILIATION
56
+ return IMPLEMENTATION