simjecture 0.1.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 (75) hide show
  1. conjecture_solver/__init__.py +10 -0
  2. conjecture_solver/__main__.py +4 -0
  3. conjecture_solver/action_handlers.py +459 -0
  4. conjecture_solver/adapters/__init__.py +7 -0
  5. conjecture_solver/adapters/base.py +96 -0
  6. conjecture_solver/adapters/fake.py +155 -0
  7. conjecture_solver/adapters/pic.py +196 -0
  8. conjecture_solver/adapters/warpx.py +1375 -0
  9. conjecture_solver/autonomous_research.py +1071 -0
  10. conjecture_solver/benchmarks/__init__.py +6 -0
  11. conjecture_solver/benchmarks/electrostatic_pic.py +547 -0
  12. conjecture_solver/benchmarks/kinetic_sufficiency.py +318 -0
  13. conjecture_solver/builtin_skills/warpx/SKILL.md +151 -0
  14. conjecture_solver/builtin_skills/warpx/agents/openai.yaml +4 -0
  15. conjecture_solver/builtin_skills/warpx/examples/implicit_em_smoke.py +74 -0
  16. conjecture_solver/builtin_skills/warpx/examples/minimal_smoke.py +54 -0
  17. conjecture_solver/builtin_skills/warpx/examples/openpmd_field_smoke.py +87 -0
  18. conjecture_solver/builtin_skills/warpx/manifest.json +11 -0
  19. conjecture_solver/builtin_skills/warpx/references/2d-xz-commissioning.md +145 -0
  20. conjecture_solver/builtin_skills/warpx/references/cpu-launch-tuning.md +54 -0
  21. conjecture_solver/builtin_skills/warpx/references/diagnostics.md +139 -0
  22. conjecture_solver/builtin_skills/warpx/references/gpu-launch-tuning.md +79 -0
  23. conjecture_solver/builtin_skills/warpx/references/local-cuda-deployment.md +87 -0
  24. conjecture_solver/builtin_skills/warpx/references/numerical-risks.md +32 -0
  25. conjecture_solver/builtin_skills/warpx/references/picmi-interface.md +118 -0
  26. conjecture_solver/builtin_skills/warpx/references/resource-scaling.md +24 -0
  27. conjecture_solver/builtin_skills/warpx/references/time-integration.md +58 -0
  28. conjecture_solver/builtin_skills/warpx/scripts/benchmark_cpu_threads.py +207 -0
  29. conjecture_solver/builtin_skills/warpx/scripts/benchmark_gpu.py +242 -0
  30. conjecture_solver/builtin_skills/warpx/scripts/bootstrap_local_cuda.sh +82 -0
  31. conjecture_solver/builtin_skills/warpx/scripts/probe_local_cuda.py +110 -0
  32. conjecture_solver/builtin_skills/warpx/scripts/reduced_energy_budget.py +195 -0
  33. conjecture_solver/builtin_skills/warpx/scripts/run_local_cuda.sh +39 -0
  34. conjecture_solver/campaign.py +406 -0
  35. conjecture_solver/cli.py +963 -0
  36. conjecture_solver/confirmation.py +287 -0
  37. conjecture_solver/control.py +234 -0
  38. conjecture_solver/deployment.py +874 -0
  39. conjecture_solver/discovery.py +68 -0
  40. conjecture_solver/domains/__init__.py +40 -0
  41. conjecture_solver/domains/base.py +157 -0
  42. conjecture_solver/domains/kinetic_sufficiency.py +521 -0
  43. conjecture_solver/ledger.py +238 -0
  44. conjecture_solver/lifecycle.py +131 -0
  45. conjecture_solver/literature.py +494 -0
  46. conjecture_solver/llm.py +228 -0
  47. conjecture_solver/models.py +279 -0
  48. conjecture_solver/mvp_agent.py +3212 -0
  49. conjecture_solver/mvp_claims.py +1279 -0
  50. conjecture_solver/mvp_control.py +322 -0
  51. conjecture_solver/mvp_guidance.py +172 -0
  52. conjecture_solver/mvp_launch.py +1296 -0
  53. conjecture_solver/mvp_monitor.py +1487 -0
  54. conjecture_solver/mvp_skills.py +538 -0
  55. conjecture_solver/orchestration.py +578 -0
  56. conjecture_solver/outbox.py +526 -0
  57. conjecture_solver/parameters.py +394 -0
  58. conjecture_solver/proposals.py +427 -0
  59. conjecture_solver/research_tools.py +278 -0
  60. conjecture_solver/schema_export.py +246 -0
  61. conjecture_solver/search.py +1047 -0
  62. conjecture_solver/semantics.py +146 -0
  63. conjecture_solver/tui/__init__.py +7 -0
  64. conjecture_solver/tui/app.py +202 -0
  65. conjecture_solver/tui/claim_views.py +213 -0
  66. conjecture_solver/tui/screens.py +1014 -0
  67. conjecture_solver/warpx_analysis.py +184 -0
  68. conjecture_solver/warpx_campaign.py +148 -0
  69. conjecture_solver/warpx_confirmation.py +617 -0
  70. simjecture-0.1.0.dist-info/METADATA +400 -0
  71. simjecture-0.1.0.dist-info/RECORD +75 -0
  72. simjecture-0.1.0.dist-info/WHEEL +4 -0
  73. simjecture-0.1.0.dist-info/entry_points.txt +4 -0
  74. simjecture-0.1.0.dist-info/licenses/LICENSE +202 -0
  75. simjecture-0.1.0.dist-info/licenses/NOTICE +6 -0
@@ -0,0 +1,238 @@
1
+ """Append-only event ledger used by the deterministic development slice."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import hashlib
6
+ import json
7
+ import sqlite3
8
+ from dataclasses import dataclass
9
+ from datetime import UTC, datetime
10
+ from pathlib import Path
11
+ from typing import Any
12
+ from uuid import uuid4
13
+
14
+
15
+ @dataclass(frozen=True)
16
+ class StoredEvent:
17
+ sequence: int
18
+ campaign_id: str
19
+ event_id: str
20
+ event_type: str
21
+ aggregate_type: str
22
+ aggregate_id: str
23
+ idempotency_key: str | None
24
+ payload: dict[str, Any]
25
+ created_at: str
26
+ previous_hash: str
27
+ event_hash: str
28
+
29
+
30
+ @dataclass(frozen=True)
31
+ class AppendResult:
32
+ event: StoredEvent
33
+ inserted: bool
34
+
35
+
36
+ class IdempotencyConflict(ValueError):
37
+ pass
38
+
39
+
40
+ class SQLiteEventLedger:
41
+ """SQLite test implementation.
42
+
43
+ Production will implement the same contract in PostgreSQL.
44
+ """
45
+
46
+ def __init__(self, path: str | Path = ":memory:") -> None:
47
+ self._connection = sqlite3.connect(str(path))
48
+ self._connection.row_factory = sqlite3.Row
49
+ self._connection.execute("PRAGMA foreign_keys = ON")
50
+ self._connection.execute("PRAGMA journal_mode = WAL")
51
+ self._connection.execute(
52
+ """
53
+ CREATE TABLE IF NOT EXISTS campaign_events (
54
+ sequence INTEGER PRIMARY KEY AUTOINCREMENT,
55
+ campaign_id TEXT NOT NULL,
56
+ event_id TEXT NOT NULL UNIQUE,
57
+ event_type TEXT NOT NULL,
58
+ aggregate_type TEXT NOT NULL,
59
+ aggregate_id TEXT NOT NULL,
60
+ idempotency_key TEXT,
61
+ payload_json TEXT NOT NULL,
62
+ created_at TEXT NOT NULL,
63
+ previous_hash TEXT NOT NULL,
64
+ event_hash TEXT NOT NULL UNIQUE,
65
+ UNIQUE(campaign_id, idempotency_key)
66
+ )
67
+ """
68
+ )
69
+ self._connection.commit()
70
+
71
+ def close(self) -> None:
72
+ self._connection.close()
73
+
74
+ def __enter__(self) -> SQLiteEventLedger:
75
+ return self
76
+
77
+ def __exit__(self, *_: object) -> None:
78
+ self.close()
79
+
80
+ @staticmethod
81
+ def _from_row(row: sqlite3.Row) -> StoredEvent:
82
+ return StoredEvent(
83
+ sequence=row["sequence"],
84
+ campaign_id=row["campaign_id"],
85
+ event_id=row["event_id"],
86
+ event_type=row["event_type"],
87
+ aggregate_type=row["aggregate_type"],
88
+ aggregate_id=row["aggregate_id"],
89
+ idempotency_key=row["idempotency_key"],
90
+ payload=json.loads(row["payload_json"]),
91
+ created_at=row["created_at"],
92
+ previous_hash=row["previous_hash"],
93
+ event_hash=row["event_hash"],
94
+ )
95
+
96
+ @staticmethod
97
+ def _hash_event(
98
+ *,
99
+ campaign_id: str,
100
+ event_id: str,
101
+ event_type: str,
102
+ aggregate_type: str,
103
+ aggregate_id: str,
104
+ idempotency_key: str | None,
105
+ payload_json: str,
106
+ created_at: str,
107
+ previous_hash: str,
108
+ ) -> str:
109
+ canonical = json.dumps(
110
+ {
111
+ "aggregate_id": aggregate_id,
112
+ "aggregate_type": aggregate_type,
113
+ "campaign_id": campaign_id,
114
+ "created_at": created_at,
115
+ "event_id": event_id,
116
+ "event_type": event_type,
117
+ "idempotency_key": idempotency_key,
118
+ "payload_json": payload_json,
119
+ "previous_hash": previous_hash,
120
+ },
121
+ sort_keys=True,
122
+ separators=(",", ":"),
123
+ )
124
+ return hashlib.sha256(canonical.encode()).hexdigest()
125
+
126
+ def append(
127
+ self,
128
+ *,
129
+ campaign_id: str,
130
+ event_type: str,
131
+ aggregate_type: str,
132
+ aggregate_id: str,
133
+ payload: dict[str, Any],
134
+ idempotency_key: str | None = None,
135
+ event_id: str | None = None,
136
+ created_at: datetime | None = None,
137
+ ) -> AppendResult:
138
+ payload_json = json.dumps(payload, sort_keys=True, separators=(",", ":"))
139
+ if idempotency_key is not None:
140
+ existing = self._connection.execute(
141
+ """
142
+ SELECT * FROM campaign_events
143
+ WHERE campaign_id = ? AND idempotency_key = ?
144
+ """,
145
+ (campaign_id, idempotency_key),
146
+ ).fetchone()
147
+ if existing is not None:
148
+ stored = self._from_row(existing)
149
+ if (
150
+ stored.event_type != event_type
151
+ or stored.aggregate_type != aggregate_type
152
+ or stored.aggregate_id != aggregate_id
153
+ or stored.payload != payload
154
+ ):
155
+ raise IdempotencyConflict(
156
+ "an idempotency key cannot be reused for a different logical event"
157
+ )
158
+ return AppendResult(event=stored, inserted=False)
159
+
160
+ prior = self._connection.execute(
161
+ """
162
+ SELECT event_hash FROM campaign_events
163
+ WHERE campaign_id = ? ORDER BY sequence DESC LIMIT 1
164
+ """,
165
+ (campaign_id,),
166
+ ).fetchone()
167
+ previous_hash = prior["event_hash"] if prior else "GENESIS"
168
+ actual_event_id = event_id or f"event_{uuid4().hex}"
169
+ actual_created_at = (created_at or datetime.now(UTC)).isoformat()
170
+ event_hash = self._hash_event(
171
+ campaign_id=campaign_id,
172
+ event_id=actual_event_id,
173
+ event_type=event_type,
174
+ aggregate_type=aggregate_type,
175
+ aggregate_id=aggregate_id,
176
+ idempotency_key=idempotency_key,
177
+ payload_json=payload_json,
178
+ created_at=actual_created_at,
179
+ previous_hash=previous_hash,
180
+ )
181
+ cursor = self._connection.execute(
182
+ """
183
+ INSERT INTO campaign_events (
184
+ campaign_id, event_id, event_type, aggregate_type, aggregate_id,
185
+ idempotency_key, payload_json, created_at, previous_hash, event_hash
186
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
187
+ """,
188
+ (
189
+ campaign_id,
190
+ actual_event_id,
191
+ event_type,
192
+ aggregate_type,
193
+ aggregate_id,
194
+ idempotency_key,
195
+ payload_json,
196
+ actual_created_at,
197
+ previous_hash,
198
+ event_hash,
199
+ ),
200
+ )
201
+ self._connection.commit()
202
+ row = self._connection.execute(
203
+ "SELECT * FROM campaign_events WHERE sequence = ?",
204
+ (cursor.lastrowid,),
205
+ ).fetchone()
206
+ assert row is not None
207
+ return AppendResult(event=self._from_row(row), inserted=True)
208
+
209
+ def load(self, campaign_id: str, *, after_sequence: int = 0) -> tuple[StoredEvent, ...]:
210
+ rows = self._connection.execute(
211
+ """
212
+ SELECT * FROM campaign_events
213
+ WHERE campaign_id = ? AND sequence > ?
214
+ ORDER BY sequence
215
+ """,
216
+ (campaign_id, after_sequence),
217
+ ).fetchall()
218
+ return tuple(self._from_row(row) for row in rows)
219
+
220
+ def verify_chain(self, campaign_id: str) -> bool:
221
+ previous_hash = "GENESIS"
222
+ for event in self.load(campaign_id):
223
+ payload_json = json.dumps(event.payload, sort_keys=True, separators=(",", ":"))
224
+ expected = self._hash_event(
225
+ campaign_id=event.campaign_id,
226
+ event_id=event.event_id,
227
+ event_type=event.event_type,
228
+ aggregate_type=event.aggregate_type,
229
+ aggregate_id=event.aggregate_id,
230
+ idempotency_key=event.idempotency_key,
231
+ payload_json=payload_json,
232
+ created_at=event.created_at,
233
+ previous_hash=previous_hash,
234
+ )
235
+ if event.previous_hash != previous_hash or event.event_hash != expected:
236
+ return False
237
+ previous_hash = event.event_hash
238
+ return True
@@ -0,0 +1,131 @@
1
+ """Pure campaign lifecycle transitions."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from enum import StrEnum
6
+ from typing import Any
7
+
8
+ from pydantic import Field
9
+
10
+ from .models import CampaignStatus, StrictModel
11
+
12
+
13
+ class LifecycleEventType(StrEnum):
14
+ REQUEST_PAUSE = "request_pause"
15
+ BEGIN_QUIESCING = "begin_quiescing"
16
+ MARK_PAUSED = "mark_paused"
17
+ REQUEST_RESUME = "request_resume"
18
+ COMMIT_RESUME = "commit_resume"
19
+ DETECT_CRASH = "detect_crash"
20
+ COMPLETE_RECOVERY = "complete_recovery"
21
+ EMERGENCY_STOP = "emergency_stop"
22
+ REQUIRE_HUMAN_REVIEW = "require_human_review"
23
+ TERMINATE = "terminate"
24
+ ATTEMPT_INTERRUPTED = "attempt_interrupted"
25
+
26
+
27
+ class LifecycleEvent(StrictModel):
28
+ event_type: LifecycleEventType
29
+ payload: dict[str, Any] = Field(default_factory=dict)
30
+
31
+
32
+ class CampaignState(StrictModel):
33
+ campaign_id: str
34
+ status: CampaignStatus = CampaignStatus.ACTIVE
35
+ revision: int = Field(default=0, ge=0)
36
+ interrupted_attempt_ids: tuple[str, ...] = ()
37
+
38
+
39
+ class InvalidTransition(ValueError):
40
+ pass
41
+
42
+
43
+ def _require(state: CampaignState, allowed: set[CampaignStatus], event: LifecycleEvent) -> None:
44
+ if state.status not in allowed:
45
+ allowed_names = ", ".join(sorted(item.value for item in allowed))
46
+ raise InvalidTransition(
47
+ f"{event.event_type.value} requires one of [{allowed_names}], got {state.status.value}"
48
+ )
49
+
50
+
51
+ def apply_lifecycle_event(state: CampaignState, event: LifecycleEvent) -> CampaignState:
52
+ status = state.status
53
+ interrupted = state.interrupted_attempt_ids
54
+
55
+ match event.event_type:
56
+ case LifecycleEventType.REQUEST_PAUSE:
57
+ _require(state, {CampaignStatus.ACTIVE}, event)
58
+ status = CampaignStatus.PAUSE_REQUESTED
59
+ case LifecycleEventType.BEGIN_QUIESCING:
60
+ _require(state, {CampaignStatus.PAUSE_REQUESTED}, event)
61
+ status = CampaignStatus.QUIESCING
62
+ case LifecycleEventType.MARK_PAUSED:
63
+ _require(state, {CampaignStatus.QUIESCING}, event)
64
+ status = CampaignStatus.PAUSED
65
+ case LifecycleEventType.REQUEST_RESUME:
66
+ _require(state, {CampaignStatus.PAUSED}, event)
67
+ status = CampaignStatus.RESUMING
68
+ case LifecycleEventType.COMMIT_RESUME:
69
+ _require(state, {CampaignStatus.RESUMING}, event)
70
+ status = CampaignStatus.ACTIVE
71
+ case LifecycleEventType.DETECT_CRASH:
72
+ _require(
73
+ state,
74
+ {
75
+ CampaignStatus.ACTIVE,
76
+ CampaignStatus.PAUSE_REQUESTED,
77
+ CampaignStatus.QUIESCING,
78
+ CampaignStatus.RESUMING,
79
+ },
80
+ event,
81
+ )
82
+ status = CampaignStatus.RECOVERING
83
+ case LifecycleEventType.COMPLETE_RECOVERY:
84
+ _require(state, {CampaignStatus.RECOVERING}, event)
85
+ target = event.payload.get("target", CampaignStatus.ACTIVE.value)
86
+ if target not in {CampaignStatus.ACTIVE.value, CampaignStatus.PAUSED.value}:
87
+ raise InvalidTransition("recovery target must be active or paused")
88
+ status = CampaignStatus(target)
89
+ case LifecycleEventType.EMERGENCY_STOP:
90
+ _require(
91
+ state,
92
+ {
93
+ CampaignStatus.ACTIVE,
94
+ CampaignStatus.PAUSE_REQUESTED,
95
+ CampaignStatus.QUIESCING,
96
+ CampaignStatus.RESUMING,
97
+ CampaignStatus.RECOVERING,
98
+ CampaignStatus.PAUSED,
99
+ },
100
+ event,
101
+ )
102
+ status = CampaignStatus.EMERGENCY_STOPPED
103
+ case LifecycleEventType.REQUIRE_HUMAN_REVIEW:
104
+ _require(state, {CampaignStatus.EMERGENCY_STOPPED}, event)
105
+ status = CampaignStatus.HUMAN_REVIEW
106
+ case LifecycleEventType.TERMINATE:
107
+ _require(
108
+ state,
109
+ {
110
+ CampaignStatus.ACTIVE,
111
+ CampaignStatus.PAUSED,
112
+ CampaignStatus.HUMAN_REVIEW,
113
+ CampaignStatus.EMERGENCY_STOPPED,
114
+ },
115
+ event,
116
+ )
117
+ status = CampaignStatus.TERMINATED
118
+ case LifecycleEventType.ATTEMPT_INTERRUPTED:
119
+ attempt_id = event.payload.get("attempt_id")
120
+ if not isinstance(attempt_id, str) or not attempt_id:
121
+ raise InvalidTransition("attempt_interrupted requires attempt_id")
122
+ if attempt_id not in interrupted:
123
+ interrupted = (*interrupted, attempt_id)
124
+
125
+ return state.model_copy(
126
+ update={
127
+ "status": status,
128
+ "revision": state.revision + 1,
129
+ "interrupted_attempt_ids": interrupted,
130
+ }
131
+ )