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,77 @@
1
+ """Installed HTTP consumer CLI for canonical submissions."""
2
+ from __future__ import annotations
3
+
4
+ import argparse
5
+ import json
6
+ import os
7
+ from pathlib import Path
8
+ from urllib.error import HTTPError, URLError
9
+ from urllib.request import Request, urlopen
10
+
11
+
12
+ def main(argv: list[str] | None = None) -> int:
13
+ parser = argparse.ArgumentParser(prog="engineering-platform")
14
+ parser.add_argument("command", choices=("submit",))
15
+ parser.add_argument("--server", required=True, help="CENTRAL base URL")
16
+ parser.add_argument("--project", required=True)
17
+ parser.add_argument("--repository", required=True)
18
+ parser.add_argument("--producer-id", default="cli")
19
+ parser.add_argument("--producer-type", default="HUMAN")
20
+ parser.add_argument("--producer-version")
21
+ parser.add_argument("--prompt-file", required=True, type=Path)
22
+ parser.add_argument("--idempotency-key")
23
+ parser.add_argument("--correlation-id")
24
+ parser.add_argument("--mission-id")
25
+ parser.add_argument("--engineering-action-id")
26
+ parser.add_argument("--constraints-file", type=Path, help="JSON object for canonical submission constraints")
27
+ parser.add_argument("--credential-env", default="EP_CONSUMER_TOKEN")
28
+ args = parser.parse_args(argv)
29
+ token = os.environ.get(args.credential_env)
30
+ if not token:
31
+ parser.error(f"{args.credential_env} is required")
32
+ try:
33
+ prompt = args.prompt_file.read_text(encoding="utf-8")
34
+ except OSError as error:
35
+ parser.error(str(error))
36
+ constraints: object | None = None
37
+ if args.constraints_file is not None:
38
+ try:
39
+ constraints = json.loads(args.constraints_file.read_text(encoding="utf-8"))
40
+ except (OSError, UnicodeDecodeError, json.JSONDecodeError) as error:
41
+ parser.error(f"invalid constraints file: {error}")
42
+ if not isinstance(constraints, dict):
43
+ parser.error("constraints file must contain a JSON object")
44
+ payload: dict[str, object] = {"repository_id": args.repository, "producer": {"id": args.producer_id, "type": args.producer_type}, "prompt": prompt}
45
+ if args.producer_version:
46
+ payload["producer"] = {**payload["producer"], "version": args.producer_version} # type: ignore[arg-type]
47
+ if args.idempotency_key:
48
+ payload["idempotency_key"] = args.idempotency_key
49
+ for field in ("correlation_id", "mission_id", "engineering_action_id"):
50
+ value = getattr(args, field)
51
+ if value:
52
+ payload[field] = value
53
+ if constraints is not None:
54
+ payload["constraints"] = constraints
55
+ request = Request(
56
+ args.server.rstrip("/") + f"/v1/projects/{args.project}/submissions",
57
+ data=json.dumps(payload).encode("utf-8"), method="POST",
58
+ headers={
59
+ "Content-Type": "application/json",
60
+ "Authorization": f"Bearer {token}",
61
+ "EP-Submission-Transport": "CLI",
62
+ },
63
+ )
64
+ try:
65
+ with urlopen(request, timeout=15) as response: # nosec B310 -- operator supplied loopback CENTRAL URL
66
+ print(response.read().decode("utf-8"))
67
+ except HTTPError as error:
68
+ print(error.read().decode("utf-8"))
69
+ return 1
70
+ except URLError as error:
71
+ print(json.dumps({"error": "CENTRAL_UNAVAILABLE"}))
72
+ return 1
73
+ return 0
74
+
75
+
76
+ if __name__ == "__main__":
77
+ raise SystemExit(main())
@@ -0,0 +1,45 @@
1
+ """Deterministic, transport-neutral Human Intent normalization.
2
+
3
+ This module parses explicit metadata only. It never selects a repository,
4
+ opens storage, dispatches a run, or interprets product intent with an AI.
5
+ """
6
+ from __future__ import annotations
7
+
8
+ from pathlib import Path
9
+
10
+
11
+ class SubmissionIntakeError(ValueError):
12
+ pass
13
+
14
+
15
+ def normalize_human_file(path: Path) -> dict[str, object]:
16
+ try:
17
+ text = path.read_text(encoding="utf-8")
18
+ except UnicodeDecodeError as error:
19
+ raise SubmissionIntakeError("UNSUPPORTED_ENCODING") from error
20
+ if not text.strip() or not text.startswith("---\n"):
21
+ raise SubmissionIntakeError("HUMAN_METADATA_REQUIRED")
22
+ try:
23
+ header, body = text[4:].split("\n---\n", 1)
24
+ except ValueError as error:
25
+ raise SubmissionIntakeError("MALFORMED_HUMAN_METADATA") from error
26
+ metadata: dict[str, str] = {}
27
+ for line in header.splitlines():
28
+ if ":" not in line:
29
+ raise SubmissionIntakeError("MALFORMED_HUMAN_METADATA")
30
+ key, value = (part.strip() for part in line.split(":", 1))
31
+ if key in metadata or key not in {"project", "repository", "mode", "target"} or not value:
32
+ raise SubmissionIntakeError("MALFORMED_HUMAN_METADATA")
33
+ metadata[key] = value
34
+ if set(metadata) - {"project", "repository", "mode", "target"} or not {"project", "repository", "mode"} <= set(metadata):
35
+ raise SubmissionIntakeError("HUMAN_METADATA_REQUIRED")
36
+ mode = metadata["mode"].upper()
37
+ if mode not in {"MANAGED", "GENESIS"} or (mode == "GENESIS") != ("target" in metadata):
38
+ raise SubmissionIntakeError("INVALID_HUMAN_MODE_OR_TARGET")
39
+ if not body.strip():
40
+ raise SubmissionIntakeError("EMPTY_HUMAN_INTENT")
41
+ prompt = f"Execution Mode: {mode}\n"
42
+ if mode == "GENESIS":
43
+ prompt += f"Target repository: {metadata['target']}\n"
44
+ prompt += "\n" + body.strip() + "\n"
45
+ return {"project_id": metadata["project"], "submission": {"repository_id": metadata["repository"], "producer": {"id": "file-human", "type": "HUMAN", "version": "submission-intake-v1"}, "prompt": prompt, "constraints": {"normalization": "submission-intake-v1", "mode": mode}}}
@@ -0,0 +1,317 @@
1
+ """Canonical, transport-neutral CENTRAL submission intake.
2
+
3
+ Adapters only turn their input into :class:`SubmissionRequest`; this module is
4
+ the single owner of project resolution, durable acceptance, idempotency and
5
+ the initial admission/queue projection. It intentionally does *not* execute
6
+ a provider.
7
+ """
8
+ from __future__ import annotations
9
+
10
+ from dataclasses import dataclass
11
+ from datetime import datetime, timezone
12
+ import hashlib
13
+ import json
14
+ import secrets
15
+ import sqlite3
16
+ from pathlib import Path
17
+ from typing import Any, Mapping
18
+
19
+
20
+ MAX_PROMPT_BYTES = 65536
21
+ MAX_FIELD_LENGTH = 128
22
+ MAX_CONSTRAINT_BYTES = 8192
23
+ VALID_TRANSPORTS = frozenset({"HTTP", "CLI", "FILE_INBOX", "DEPENDABOT", "LEGACY_FILE"})
24
+ VALID_EXECUTION_MODES = frozenset({"MANAGED", "GENESIS"})
25
+
26
+ # This is the complete B8D lifecycle. The final value deliberately says what
27
+ # CENTRAL has *not* done: admission makes a submission eligible for a later
28
+ # execution protocol, but it never invokes a provider or a local Codex binary.
29
+ SUBMISSION_LIFECYCLE_VERSION = "1"
30
+ SUBMISSION_ACCEPTED = "ACCEPTED"
31
+ ADMISSION_GRANTED = "ADMITTED"
32
+ EXECUTION_NOT_DISPATCHED = "NOT_DISPATCHED"
33
+ _LIFECYCLE_EVENT_KINDS = (
34
+ "SUBMISSION_ACCEPTED",
35
+ "ADMISSION_GRANTED",
36
+ "EXECUTION_NOT_DISPATCHED",
37
+ )
38
+
39
+
40
+ class SubmissionError(ValueError):
41
+ """A stable, safe submission rejection."""
42
+ def __init__(self, code: str, status: int = 400) -> None:
43
+ super().__init__(code)
44
+ self.code, self.status = code, status
45
+
46
+
47
+ def _lifecycle_payload(*, transport: str, producer_id: str) -> dict[str, str]:
48
+ """Return the one durable lifecycle meaning shared by every adapter."""
49
+ return {
50
+ "version": SUBMISSION_LIFECYCLE_VERSION,
51
+ "submission": SUBMISSION_ACCEPTED,
52
+ "admission": ADMISSION_GRANTED,
53
+ "execution": EXECUTION_NOT_DISPATCHED,
54
+ "transport": transport,
55
+ "producer_id": producer_id,
56
+ }
57
+
58
+
59
+ @dataclass(frozen=True)
60
+ class SubmissionRequest:
61
+ project_id: str
62
+ repository_id: str
63
+ producer_id: str
64
+ producer_type: str
65
+ producer_version: str | None
66
+ prompt: str
67
+ transport: str
68
+ idempotency_key: str | None = None
69
+ correlation_id: str | None = None
70
+ mission_id: str | None = None
71
+ engineering_action_id: str | None = None
72
+ constraints: Mapping[str, object] | None = None
73
+ transport_receipt_id: str | None = None
74
+ transport_received_at: str | None = None
75
+
76
+
77
+ @dataclass(frozen=True)
78
+ class SubmissionResult:
79
+ submission_id: str
80
+ project_id: str
81
+ repository_id: str
82
+ state: str
83
+ created_at: str
84
+ admission: str
85
+ transport: str
86
+ producer_id: str
87
+ duplicate: bool = False
88
+
89
+ def to_dict(self) -> dict[str, object]:
90
+ return {"submission_id": self.submission_id, "project_id": self.project_id,
91
+ "repository_id": self.repository_id, "state": self.state,
92
+ "created_at": self.created_at, "admission": self.admission,
93
+ "transport": self.transport, "duplicate": self.duplicate,
94
+ "lifecycle": _lifecycle_payload(
95
+ transport=self.transport,
96
+ producer_id=self.producer_id,
97
+ )}
98
+
99
+
100
+ def _now() -> str:
101
+ return datetime.now(timezone.utc).isoformat()
102
+
103
+
104
+ def _token(value: object, field: str, *, optional: bool = False) -> str | None:
105
+ if value is None and optional:
106
+ return None
107
+ if not isinstance(value, str) or not value or len(value) > MAX_FIELD_LENGTH or "\x00" in value:
108
+ raise SubmissionError(f"INVALID_{field.upper()}")
109
+ return value
110
+
111
+
112
+ def _json_object(value: object) -> dict[str, object]:
113
+ if value is None:
114
+ return {}
115
+ if not isinstance(value, Mapping):
116
+ raise SubmissionError("INVALID_CONSTRAINTS")
117
+ try:
118
+ encoded = json.dumps(value, sort_keys=True, separators=(",", ":"), ensure_ascii=False, allow_nan=False).encode("utf-8")
119
+ except (TypeError, ValueError):
120
+ raise SubmissionError("INVALID_CONSTRAINTS") from None
121
+ if len(encoded) > MAX_CONSTRAINT_BYTES or b"\0" in encoded:
122
+ raise SubmissionError("INVALID_CONSTRAINTS")
123
+ return dict(value)
124
+
125
+
126
+ def _transport(value: object) -> str:
127
+ if not isinstance(value, str) or value not in VALID_TRANSPORTS:
128
+ raise SubmissionError("INVALID_TRANSPORT")
129
+ return value
130
+
131
+
132
+ def _validate_execution_mode(request: SubmissionRequest) -> None:
133
+ """Reject invalid mode declarations before CENTRAL writes admission state."""
134
+ declarations = {
135
+ line.split(":", 1)[1].strip().upper()
136
+ for line in request.prompt.splitlines()
137
+ if line.strip().lower().startswith("execution mode:")
138
+ }
139
+ if declarations and (not declarations <= VALID_EXECUTION_MODES or len(declarations) != 1):
140
+ raise SubmissionError("INVALID_EXECUTION_MODE")
141
+ if declarations == {"GENESIS"}:
142
+ targets = [line.split(":", 1)[1].strip() for line in request.prompt.splitlines() if line.strip().lower().startswith("target repository:")]
143
+ if len(set(targets)) != 1 or not targets[0] or not Path(targets[0]).is_absolute():
144
+ raise SubmissionError("INVALID_GENESIS_TARGET")
145
+
146
+
147
+ def request_from_mapping(project_id: str, payload: object, *, transport: str) -> SubmissionRequest:
148
+ if not isinstance(payload, Mapping):
149
+ raise SubmissionError("MALFORMED_REQUEST")
150
+ allowed = {"repository_id", "producer", "prompt", "idempotency_key", "correlation_id", "mission_id", "engineering_action_id", "constraints", "transport_receipt_id", "transport_received_at"}
151
+ if set(payload) - allowed:
152
+ raise SubmissionError("UNKNOWN_FIELD")
153
+ producer = payload.get("producer")
154
+ if not isinstance(producer, Mapping) or set(producer) - {"id", "type", "version"}:
155
+ raise SubmissionError("INVALID_PRODUCER")
156
+ prompt = payload.get("prompt")
157
+ if not isinstance(prompt, str) or not prompt.strip() or "\x00" in prompt or len(prompt.encode("utf-8")) > MAX_PROMPT_BYTES:
158
+ raise SubmissionError("INVALID_PROMPT")
159
+ return SubmissionRequest(
160
+ project_id=_token(project_id, "project_id") or "",
161
+ repository_id=_token(payload.get("repository_id"), "repository_id") or "",
162
+ producer_id=_token(producer.get("id"), "producer_id") or "",
163
+ producer_type=_token(producer.get("type"), "producer_type") or "",
164
+ producer_version=_token(producer.get("version"), "producer_version", optional=True),
165
+ prompt=prompt.replace("\r\n", "\n").replace("\r", "\n"), transport=_transport(transport),
166
+ idempotency_key=_token(payload.get("idempotency_key"), "idempotency_key", optional=True),
167
+ correlation_id=_token(payload.get("correlation_id"), "correlation_id", optional=True),
168
+ mission_id=_token(payload.get("mission_id"), "mission_id", optional=True),
169
+ engineering_action_id=_token(payload.get("engineering_action_id"), "engineering_action_id", optional=True),
170
+ constraints=_json_object(payload.get("constraints")),
171
+ transport_receipt_id=_token(payload.get("transport_receipt_id"), "transport_receipt_id", optional=True),
172
+ transport_received_at=_token(payload.get("transport_received_at"), "transport_received_at", optional=True),
173
+ )
174
+
175
+
176
+ def _same_idempotent_request(row: tuple[object, ...], request: SubmissionRequest) -> bool:
177
+ """Ensure an idempotency key cannot silently alias another submission."""
178
+ stored = {
179
+ "repository_id": row[0], "producer_id": row[1], "producer_type": row[2],
180
+ "producer_version": row[3], "prompt": row[4], "constraints": row[5],
181
+ "correlation_id": row[6], "mission_id": row[7],
182
+ "engineering_action_id": row[8], "transport_receipt_id": row[9],
183
+ }
184
+ requested = {
185
+ "repository_id": request.repository_id, "producer_id": request.producer_id,
186
+ "producer_type": request.producer_type, "producer_version": request.producer_version,
187
+ "prompt": request.prompt,
188
+ "constraints": json.dumps(request.constraints or {}, sort_keys=True),
189
+ "correlation_id": request.correlation_id, "mission_id": request.mission_id,
190
+ "engineering_action_id": request.engineering_action_id,
191
+ "transport_receipt_id": request.transport_receipt_id,
192
+ }
193
+ return stored == requested
194
+
195
+
196
+ def _persist_lifecycle_events(
197
+ connection: sqlite3.Connection,
198
+ *, submission_id: str,
199
+ transport: str,
200
+ producer_id: str,
201
+ recorded_at: str,
202
+ ) -> None:
203
+ """Persist all three lifecycle boundaries in their canonical order."""
204
+ payload = json.dumps(
205
+ _lifecycle_payload(transport=transport, producer_id=producer_id), sort_keys=True,
206
+ )
207
+ for event_kind in _LIFECYCLE_EVENT_KINDS:
208
+ connection.execute(
209
+ "INSERT INTO ep_submission_events(submission_id,event_kind,payload,recorded_at) VALUES(?,?,?,?)",
210
+ (submission_id, event_kind, payload, recorded_at),
211
+ )
212
+
213
+
214
+ def lifecycle(connection: sqlite3.Connection, submission_id: str) -> dict[str, str]:
215
+ """Read and validate the durable submission/admission/execution boundary.
216
+
217
+ This is an observation helper. It never performs dispatch and rejects a
218
+ partial or reordered event history rather than inventing lifecycle state.
219
+ """
220
+ row = connection.execute(
221
+ "SELECT producer_id,transport,state,admission FROM ep_submissions WHERE submission_id=?",
222
+ (submission_id,),
223
+ ).fetchone()
224
+ if row is None:
225
+ raise SubmissionError("UNKNOWN_SUBMISSION", 404)
226
+ events = [
227
+ str(event[0]) for event in connection.execute(
228
+ "SELECT event_kind FROM ep_submission_events WHERE submission_id=? ORDER BY event_id",
229
+ (submission_id,),
230
+ )
231
+ ]
232
+ if tuple(events) != _LIFECYCLE_EVENT_KINDS or row[2:] != ("QUEUED", ADMISSION_GRANTED):
233
+ raise SubmissionError("SUBMISSION_LIFECYCLE_INCOMPLETE", 500)
234
+ return _lifecycle_payload(transport=str(row[1]), producer_id=str(row[0]))
235
+
236
+
237
+ def submit(connection: sqlite3.Connection, request: SubmissionRequest) -> SubmissionResult:
238
+ """Persist and admit one request; no provider or Agent is selected here."""
239
+ _transport(request.transport)
240
+ _validate_execution_mode(request)
241
+ project = connection.execute("SELECT status FROM ep_project_registrations WHERE project_id=?", (request.project_id,)).fetchone()
242
+ if project is None:
243
+ raise SubmissionError("UNKNOWN_PROJECT", 404)
244
+ if project[0] != "ACTIVE":
245
+ raise SubmissionError("PROJECT_NOT_ACTIVE", 409)
246
+ repository = connection.execute("SELECT project_id FROM ep_repository_registrations WHERE repository_id=?", (request.repository_id,)).fetchone()
247
+ if repository is None:
248
+ raise SubmissionError("UNKNOWN_REPOSITORY", 404)
249
+ if repository[0] != request.project_id:
250
+ raise SubmissionError("REPOSITORY_PROJECT_CONFLICT", 409)
251
+ if request.idempotency_key:
252
+ duplicate = connection.execute(
253
+ "SELECT submission_id,created_at,state,admission,repository_id,producer_id,producer_type,producer_version,prompt,constraints,correlation_id,mission_id,engineering_action_id,transport_receipt_id,transport "
254
+ "FROM ep_submissions WHERE project_id=? AND idempotency_key=?",
255
+ (request.project_id, request.idempotency_key),
256
+ ).fetchone()
257
+ if duplicate is not None:
258
+ if not _same_idempotent_request(tuple(duplicate[4:14]), request):
259
+ raise SubmissionError("IDEMPOTENCY_CONFLICT", 409)
260
+ lifecycle(connection, str(duplicate[0]))
261
+ return SubmissionResult(
262
+ str(duplicate[0]), request.project_id, str(duplicate[4]), str(duplicate[2]),
263
+ str(duplicate[1]), str(duplicate[3]), str(duplicate[14]), str(duplicate[5]), True,
264
+ )
265
+ submission_id, created_at = "sub-" + secrets.token_hex(16), _now()
266
+ # Admission intentionally validates CENTRAL topology only at submission
267
+ # time. Agent selection, leases and provider execution remain downstream.
268
+ admission, state = "ADMITTED", "QUEUED"
269
+ prompt_digest = hashlib.sha256(request.prompt.encode("utf-8")).hexdigest()
270
+ connection.execute("""INSERT INTO ep_submissions(submission_id,project_id,repository_id,producer_id,producer_type,producer_version,transport,prompt,prompt_digest,constraints,idempotency_key,correlation_id,mission_id,engineering_action_id,transport_receipt_id,transport_received_at,state,admission,created_at)
271
+ VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)""", (submission_id, request.project_id, request.repository_id, request.producer_id, request.producer_type, request.producer_version, request.transport, request.prompt, prompt_digest, json.dumps(request.constraints or {}, sort_keys=True), request.idempotency_key, request.correlation_id, request.mission_id, request.engineering_action_id, request.transport_receipt_id, request.transport_received_at, state, admission, created_at))
272
+ _persist_lifecycle_events(
273
+ connection, submission_id=submission_id, transport=request.transport,
274
+ producer_id=request.producer_id, recorded_at=created_at,
275
+ )
276
+ connection.execute("INSERT INTO ep_submission_prompt_history(submission_id,prompt_digest,recorded_at) VALUES(?,?,?)", (submission_id, prompt_digest, created_at))
277
+ lifecycle(connection, submission_id)
278
+ return SubmissionResult(
279
+ submission_id, request.project_id, request.repository_id, state, created_at,
280
+ admission, request.transport, request.producer_id,
281
+ )
282
+
283
+
284
+ def register_consumer(connection: sqlite3.Connection, *, consumer_id: str, project_id: str) -> None:
285
+ """Explicitly grant a consumer one project scope in CENTRAL."""
286
+ consumer_id = _token(consumer_id, "consumer_id") or ""
287
+ project_id = _token(project_id, "project_id") or ""
288
+ if connection.execute("SELECT 1 FROM ep_project_registrations WHERE project_id=? AND status='ACTIVE'", (project_id,)).fetchone() is None:
289
+ raise SubmissionError("UNKNOWN_PROJECT", 404)
290
+ now = _now()
291
+ connection.execute("""INSERT INTO ep_consumer_registrations(consumer_id,project_id,status,created_at,updated_at,audit_metadata)
292
+ VALUES(?,?,'ACTIVE',?,?,?) ON CONFLICT(consumer_id,project_id) DO UPDATE SET status='ACTIVE',updated_at=excluded.updated_at""", (consumer_id, project_id, now, now, json.dumps({"action": "SUBMISSION_CONSUMER_REGISTER"}, sort_keys=True)))
293
+
294
+
295
+ def issue_consumer_credential(connection: sqlite3.Connection, *, consumer_id: str, project_id: str) -> dict[str, str]:
296
+ """Issue a scoped bearer token once; only its verifier is retained."""
297
+ register_consumer(connection, consumer_id=consumer_id, project_id=project_id)
298
+ from .ep_consumer_credentials import fingerprint, verifier
299
+ token, credential_id, now = secrets.token_urlsafe(32), "production-" + secrets.token_hex(16), _now()
300
+ connection.execute("INSERT INTO ep_consumer_credentials(credential_id,consumer_id,project_id,verifier,fingerprint,issued_at) VALUES(?,?,?,?,?,?)", (credential_id, consumer_id, project_id, verifier(token), fingerprint(token), now))
301
+ return {"credential_id": credential_id, "consumer_id": consumer_id, "project_id": project_id, "credential": token}
302
+
303
+
304
+ def submit_legacy_file(connection: sqlite3.Connection, path: object) -> SubmissionResult:
305
+ """Compatibility adapter: a bounded JSON envelope with explicit project scope."""
306
+ from pathlib import Path
307
+ candidate = Path(path)
308
+ raw = candidate.read_bytes()
309
+ if len(raw) > 131072 or b"\0" in raw:
310
+ raise SubmissionError("MALFORMED_LEGACY_FILE")
311
+ try:
312
+ envelope = json.loads(raw.decode("utf-8"))
313
+ except (UnicodeDecodeError, json.JSONDecodeError):
314
+ raise SubmissionError("MALFORMED_LEGACY_FILE") from None
315
+ if not isinstance(envelope, Mapping) or set(envelope) != {"project_id", "submission"}:
316
+ raise SubmissionError("MALFORMED_LEGACY_FILE")
317
+ return submit(connection, request_from_mapping(str(envelope["project_id"]), envelope["submission"], transport="LEGACY_FILE"))