outerloop-science 0.1.0.dev2__py3-none-any.whl → 0.1.0.dev4__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 (45) hide show
  1. outerloop/__init__.py +2 -2
  2. outerloop/appauth.py +17 -0
  3. outerloop/attempt.py +376 -101
  4. outerloop/brief.py +38 -25
  5. outerloop/cli.py +104 -6
  6. outerloop/climbboard.py +67 -22
  7. outerloop/compute.py +148 -53
  8. outerloop/contract.py +8 -0
  9. outerloop/dispatch.py +63 -18
  10. outerloop/evalcache.py +147 -0
  11. outerloop/followup.py +40 -25
  12. outerloop/github.py +67 -22
  13. outerloop/harness.py +22 -47
  14. outerloop/housekeeping.py +1 -17
  15. outerloop/image.py +0 -4
  16. outerloop/init.py +45 -2
  17. outerloop/intake.py +4 -7
  18. outerloop/launchlog.py +239 -0
  19. outerloop/maintain.py +353 -0
  20. outerloop/maintain_agent_cli.py +81 -0
  21. outerloop/maintain_post_cli.py +140 -0
  22. outerloop/measure.py +6 -0
  23. outerloop/orchestrator.py +141 -31
  24. outerloop/panel.py +3 -3
  25. outerloop/review.py +4 -0
  26. outerloop/review_agent.py +7 -7
  27. outerloop/review_agent_cli.py +2 -2
  28. outerloop/review_post_cli.py +2 -2
  29. outerloop/review_summarize_cli.py +8 -6
  30. outerloop/roles.py +27 -0
  31. outerloop/rolespec.py +3 -1
  32. outerloop/steward.py +7 -14
  33. outerloop/syscall.py +261 -47
  34. outerloop/syscall_cli.py +243 -12
  35. outerloop/tick.py +274 -313
  36. outerloop/verify_agent.py +8 -6
  37. outerloop/verify_post_cli.py +2 -2
  38. outerloop/watcher.py +203 -0
  39. {outerloop_science-0.1.0.dev2.dist-info → outerloop_science-0.1.0.dev4.dist-info}/METADATA +4 -1
  40. outerloop_science-0.1.0.dev4.dist-info/RECORD +59 -0
  41. outerloop_science-0.1.0.dev2.dist-info/RECORD +0 -53
  42. {outerloop_science-0.1.0.dev2.dist-info → outerloop_science-0.1.0.dev4.dist-info}/WHEEL +0 -0
  43. {outerloop_science-0.1.0.dev2.dist-info → outerloop_science-0.1.0.dev4.dist-info}/entry_points.txt +0 -0
  44. {outerloop_science-0.1.0.dev2.dist-info → outerloop_science-0.1.0.dev4.dist-info}/licenses/LICENSE +0 -0
  45. {outerloop_science-0.1.0.dev2.dist-info → outerloop_science-0.1.0.dev4.dist-info}/licenses/NOTICE +0 -0
outerloop/verify_agent.py CHANGED
@@ -20,7 +20,7 @@ from outerloop.contract import find_contract
20
20
  from outerloop.github import GitHubClient
21
21
  from outerloop.harness import Harness, backend_id, budget_exhausted, outage
22
22
  from outerloop.posting import EXPECTED_FAILURES, post_round, post_skip_stub
23
- from outerloop.review_agent import _emit, _pull_request
23
+ from outerloop.review_agent import emit_envelope, pull_request
24
24
  from outerloop.role_runner import run_role
25
25
  from outerloop.roles import verifier_spec, verify_result_from_role
26
26
  from outerloop.rolespec import RoleSpec
@@ -81,19 +81,21 @@ def run_agent_verify(
81
81
  # `detail` is already api-key-redacted by the harness (it owns its own
82
82
  # secret), so no secret is passed here.
83
83
  if emit_path is not None:
84
- _emit(emit_path, repo, number, kind="skip-stub", detail=detail, reviewed_by=reviewed_by)
84
+ emit_envelope(
85
+ emit_path, repo, number, kind="skip-stub", detail=detail, reviewed_by=reviewed_by
86
+ )
85
87
  else:
86
88
  post_skip_stub(client, repo, number, "verification", RuntimeError(detail))
87
89
 
88
90
  try:
89
- pr, pr_data = _pull_request(client, repo, number)
91
+ pr, pr_data = pull_request(client, repo, number)
90
92
  skip = verify_skip_reason(pr, bot_login)
91
93
  if skip is not None:
92
94
  log.info("skipping verification of %s#%s: %s", repo, number, skip)
93
95
  # a clean skip still leaves an envelope so the post job can REQUIRE
94
96
  # an artifact — a missing one then always means a broken session
95
97
  if emit_path is not None:
96
- _emit(emit_path, repo, number, kind="skip-clean", detail=skip)
98
+ emit_envelope(emit_path, repo, number, kind="skip-clean", detail=skip)
97
99
  return None
98
100
 
99
101
  contract_text = _base_contract(client, repo, pr_data)
@@ -122,11 +124,11 @@ def run_agent_verify(
122
124
  elif emit_path is not None:
123
125
  # nothing worth posting, but the post job still needs an
124
126
  # artifact so a MISSING one always means a broken session
125
- _emit(emit_path, repo, number, kind="skip-clean", detail=detail)
127
+ emit_envelope(emit_path, repo, number, kind="skip-clean", detail=detail)
126
128
  return None
127
129
 
128
130
  if emit_path is not None:
129
- _emit(
131
+ emit_envelope(
130
132
  emit_path,
131
133
  repo,
132
134
  number,
@@ -20,7 +20,7 @@ from pathlib import Path
20
20
 
21
21
  from outerloop.github import EnvTokenProvider, GitHubClient
22
22
  from outerloop.posting import EXPECTED_FAILURES, post_round, post_skip_stub
23
- from outerloop.review_agent import _pull_request
23
+ from outerloop.review_agent import pull_request
24
24
  from outerloop.verifier import (
25
25
  VERIFY_MARKER,
26
26
  format_verify_comment,
@@ -58,7 +58,7 @@ def post_from_file(
58
58
  # the write authority re-checks the bot-only rule for EVERY kind: this
59
59
  # side of the artifact boundary is the one that must never post on a
60
60
  # non-bot PR — a forged envelope is still a post
61
- pr, pr_data = _pull_request(client, repo, number)
61
+ pr, pr_data = pull_request(client, repo, number)
62
62
  skip = verify_skip_reason(pr, bot_login)
63
63
  if skip is not None:
64
64
  log.info("skipping post on %s#%s: %s", repo, number, skip)
outerloop/watcher.py ADDED
@@ -0,0 +1,203 @@
1
+ """The session watcher: a thread beside a live author session that answers its
2
+ questions through the syscall channel in seconds — `queue` (the kernel's jobs
3
+ in the cluster queue, every agent's) and `history` (this run's launches) —
4
+ where the tick would take a cadence. It changes no lifecycle state: it writes
5
+ answer files into the channel and nothing else; submitting, cancelling,
6
+ sealing and parking stay at the sleep boundary (docs/design/session-watcher.md).
7
+
8
+ Failure never reaches the session: an exception is logged and the request is
9
+ left standing, the tool times out and says so. A watcher that dies leaves the
10
+ session exactly as it is without one."""
11
+
12
+ from __future__ import annotations
13
+
14
+ import logging
15
+ import re
16
+ import threading
17
+ import time
18
+ from collections.abc import Callable
19
+ from dataclasses import dataclass, field
20
+ from pathlib import Path
21
+ from typing import Any
22
+
23
+ from outerloop.climbboard import queue_rows
24
+ from outerloop.launchlog import history, why_by_job
25
+ from outerloop.runstate import run_dir
26
+ from outerloop.syscall import MAX_WHY_CHARS, mark_done, marker_requested, write_channel_json
27
+
28
+ log = logging.getLogger(__name__)
29
+
30
+ VERBS = ("queue", "history")
31
+ POLL_S = 2.0
32
+ # one scheduler query per this many seconds however often the marker is
33
+ # rewritten: a session cannot turn the watcher into a squeue loop
34
+ QUERY_GAP_S = 5.0
35
+ MAX_QUEUE_ROWS = 500
36
+ # squeue names a pending array `<id>_[0-15%4]` and a running task `<id>_3`
37
+ _ARRAY_THROTTLE = re.compile(r"_\[[^\]]*%(\d+)\]$")
38
+ # the lane's node states move slowly; one sinfo a minute is plenty
39
+ LANE_GAP_S = 60.0
40
+ # a stopped watcher may still be inside a scheduler query (its own timeout,
41
+ # a minute at most); the attempt waits this long for it, then moves on — the
42
+ # thread is a daemon and writes nothing once stopped
43
+ JOIN_S = 15.0
44
+
45
+
46
+ @dataclass
47
+ class WatcherContext:
48
+ """What the watcher needs to answer: where the channel is, which run it
49
+ serves, and the compute backend to ask (None = no queue, local mode)."""
50
+
51
+ workspace: Path
52
+ run_root: Path
53
+ run_id: str
54
+ target: str
55
+ agent_id: str
56
+ compute: Any = None
57
+ gpu_partition: str = ""
58
+ poll_s: float = POLL_S
59
+ query_gap_s: float = QUERY_GAP_S
60
+ lane_gap_s: float = LANE_GAP_S
61
+ clock: Callable[[], float] = field(default=time.time)
62
+
63
+
64
+ def _kind(name: str) -> str:
65
+ """A short label for a kernel job that is not a launch."""
66
+ for key in ("wake", "followup", "eval", "resident", "tick"):
67
+ if key in name:
68
+ return key
69
+ return name
70
+
71
+
72
+ class SessionWatcher:
73
+ """`with SessionWatcher(ctx):` around the harness subprocess. `service()`
74
+ is one pass, public so the loop is testable without the thread."""
75
+
76
+ def __init__(self, ctx: WatcherContext) -> None:
77
+ self.ctx = ctx
78
+ self._stop = threading.Event()
79
+ self._thread: threading.Thread | None = None
80
+ self._queue_cache: dict[str, Any] | None = None
81
+ self._queue_at = 0.0
82
+ self._lane_cache: dict[str, Any] | None = None
83
+ self._lane_at = 0.0
84
+
85
+ def __enter__(self) -> SessionWatcher:
86
+ self._thread = threading.Thread(target=self._loop, name="session-watcher", daemon=True)
87
+ self._thread.start()
88
+ return self
89
+
90
+ def __exit__(self, *exc: object) -> None:
91
+ self._stop.set()
92
+ if self._thread is not None:
93
+ self._thread.join(timeout=JOIN_S)
94
+ if self._thread.is_alive():
95
+ log.warning("session watcher still inside a scheduler query at session end")
96
+
97
+ def _loop(self) -> None:
98
+ # wait() is False on timeout: poll; True once stopped
99
+ while not self._stop.wait(self.ctx.poll_s):
100
+ self.service()
101
+
102
+ def service(self) -> None:
103
+ """Answer every verb whose request marker is newer than its done marker."""
104
+ for verb in VERBS:
105
+ if self._stop.is_set():
106
+ return
107
+ try:
108
+ requested = marker_requested(self.ctx.workspace, f"{verb}-request", f"{verb}-done")
109
+ if requested is None:
110
+ continue
111
+ payload = self.queue_view() if verb == "queue" else self.history_view()
112
+ if self._stop.is_set():
113
+ return # the session ended meanwhile: nothing is written after it
114
+ write_channel_json(self.ctx.workspace, f"{verb}.json", payload)
115
+ mark_done(self.ctx.workspace, f"{verb}-done", requested)
116
+ except Exception as exc: # the request stands; the tool times out
117
+ log.warning("session watcher: %s failed: %s", verb, exc)
118
+
119
+ def history_view(self) -> dict[str, Any]:
120
+ ctx = self.ctx
121
+ return {
122
+ "at": ctx.clock(),
123
+ "run_id": ctx.run_id,
124
+ "history": history(run_dir(ctx.run_root, ctx.run_id)),
125
+ }
126
+
127
+ def queue_view(self) -> dict[str, Any]:
128
+ """The kernel's jobs as the board projects them (attributed to agents),
129
+ each launch labelled with its author's `why`, plus the GPU lane's load.
130
+ Cached for `query_gap_s` — the rate limit on scheduler queries."""
131
+ ctx = self.ctx
132
+ now = ctx.clock()
133
+ if self._queue_cache is not None and now - self._queue_at < ctx.query_gap_s:
134
+ return {**self._queue_cache, "at": now, "cached": True}
135
+ error = ""
136
+ rows: list[dict[str, Any]] = []
137
+ try:
138
+ snapshot = ctx.compute.queue_snapshot() if ctx.compute is not None else []
139
+ rows = [
140
+ dict(r) for r in queue_rows(ctx.run_root, ctx.target, snapshot)[:MAX_QUEUE_ROWS]
141
+ ]
142
+ except Exception as exc:
143
+ error = str(exc)[:200]
144
+ labels: dict[str, dict[str, Any]] = {}
145
+ for rid in {str(r.get("run_id") or "") for r in rows} - {""}:
146
+ labels.update(why_by_job(run_dir(ctx.run_root, rid)))
147
+ for r in rows:
148
+ rid = str(r.get("run_id") or "")
149
+ name = str(r.get("name") or "")
150
+ prefix = f"{rid}-launch-" if rid else ""
151
+ r["mine"] = bool(rid) and rid == ctx.run_id
152
+ job_id = str(r.get("id") or "")
153
+ throttle = _ARRAY_THROTTLE.search(job_id)
154
+ if throttle:
155
+ r["concurrency"] = int(throttle.group(1))
156
+ if prefix and name.startswith(prefix):
157
+ r["experiment"] = name[len(prefix) :]
158
+ # the ledger keys the array's id; squeue shows `<id>_<k>` or `<id>_[...]`
159
+ base = job_id.split("_", 1)[0]
160
+ label = labels.get(base, {})
161
+ r["why"] = str(label.get("why", ""))[:MAX_WHY_CHARS]
162
+ r["kind"] = "launch"
163
+ # the pace: the pending range says it; once only running tasks
164
+ # are left, the ledger does (the whole array when unset)
165
+ array = int(label.get("array") or 1)
166
+ if "concurrency" not in r and array > 1:
167
+ r["concurrency"] = int(label.get("concurrency") or array)
168
+ else:
169
+ r["experiment"] = ""
170
+ r["why"] = ""
171
+ r["kind"] = _kind(name)
172
+ view = {
173
+ "at": now,
174
+ "run_id": ctx.run_id,
175
+ "agent": ctx.agent_id,
176
+ "jobs": rows,
177
+ "lane": self._lane_load(),
178
+ "error": error,
179
+ }
180
+ self._queue_cache, self._queue_at = view, now
181
+ return view
182
+
183
+ def _lane_load(self) -> dict[str, Any]:
184
+ """The GPU lane's node states, refreshed once per `lane_gap_s` (a
185
+ second scheduler query per request would defeat the rate limit). A
186
+ failed sinfo is reported in the answer, never passed off as a lane
187
+ with no state."""
188
+ ctx = self.ctx
189
+ load = getattr(ctx.compute, "lane_load", None)
190
+ if load is None or not ctx.gpu_partition:
191
+ return {}
192
+ now = ctx.clock()
193
+ if self._lane_cache is not None and now - self._lane_at < ctx.lane_gap_s:
194
+ return self._lane_cache
195
+ try:
196
+ lane: dict[str, Any] = {
197
+ "partition": ctx.gpu_partition,
198
+ "nodes": load(ctx.gpu_partition),
199
+ }
200
+ except Exception as exc:
201
+ lane = {"partition": ctx.gpu_partition, "nodes": {}, "error": str(exc)[:200]}
202
+ self._lane_cache, self._lane_at = lane, now
203
+ return lane
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: outerloop-science
3
- Version: 0.1.0.dev2
3
+ Version: 0.1.0.dev4
4
4
  Summary: Autonomous research agents that improve the benchmark you point them at, one verified pull request at a time
5
5
  Project-URL: Homepage, https://outerloop.science
6
6
  Project-URL: Repository, https://github.com/outerloop-science/outerloop
@@ -25,6 +25,9 @@ Description-Content-Type: text/markdown
25
25
  # Outerloop
26
26
 
27
27
  [![ci](https://github.com/outerloop-science/outerloop/actions/workflows/ci.yml/badge.svg)](https://github.com/outerloop-science/outerloop/actions/workflows/ci.yml)
28
+ [![PyPI](https://img.shields.io/pypi/v/outerloop-science.svg)](https://pypi.org/project/outerloop-science/)
29
+ [![Python](https://img.shields.io/pypi/pyversions/outerloop-science.svg)](https://pypi.org/project/outerloop-science/)
30
+ [![License](https://img.shields.io/badge/license-Apache--2.0-blue.svg)](LICENSE)
28
31
 
29
32
  **Autoresearch agents that improve your benchmark.**
30
33
 
@@ -0,0 +1,59 @@
1
+ outerloop/__init__.py,sha256=-UecbET4sxCC3TMirVP82LGWLLX79jmKAtdgjj8UspQ,636
2
+ outerloop/__main__.py,sha256=Fq2ADl3rjZ4i2m1svON_KO2kJqpwUnohLhdlacAnEpM,57
3
+ outerloop/appauth.py,sha256=0FJD8JdQ3r9DgCTpY7GgOudCB7s7BDbaVqBWpdt02oU,9666
4
+ outerloop/appmanifest.py,sha256=9L4NyUqVJhgE8BlBnK6BTpMdkhpukLtAnezZQcbyBwI,8459
5
+ outerloop/attempt.py,sha256=JAuJhpWHppK8uZPeNMfLiCQ6tz-W_Lq00sfSzdHqq0Y,171201
6
+ outerloop/brief.py,sha256=XGNrO5VZUASNRu4y_T3VHcGp-PCkpHPV8ACOpzXNT4M,25206
7
+ outerloop/cli.py,sha256=VDp_-ImCFUbXaj_zmjrPE6ceEgstcJR-fi5w9dH8J0s,24952
8
+ outerloop/climbboard.py,sha256=C7jVJp91hain3kxUouL6DK8n4Xpa_j_nyDtBphMP8K0,68452
9
+ outerloop/compute.py,sha256=aC5yecD4o6C1geA_OAiA4pzts1vXsiskzvLNbPOXPe0,27926
10
+ outerloop/contract.py,sha256=wGeLhFl57wQQi57uZmJYb9M_21H8Dj5hHIOLSs06zOY,25101
11
+ outerloop/contract_cli.py,sha256=Pa-sJe7G8BuLGed5tqnlq6L75fTT1HHO4d-XWZPW8U8,1992
12
+ outerloop/disk.py,sha256=FAgR1FJVPTu1YCcfNxcJtUFxJsBFcOaenF8lYAVPL1Y,5853
13
+ outerloop/dispatch.py,sha256=hRW8K_OxFR9cmWcUg89XZSc_t3UGCY5X2AxRhuGOehk,29207
14
+ outerloop/evalcache.py,sha256=uVCcG3HjHNpmFRuQbMyQDVYE_-W1a3v-vxN3mDGZoNY,5045
15
+ outerloop/followup.py,sha256=AYD1ir-PydgK1ItSZCqA2w-rZQ1lxMn5Ymr1tkry9cg,95200
16
+ outerloop/github.py,sha256=4Z0hFjuhWcWuIgM8VBabh218AWPKmYWORYFyejnC0w4,67854
17
+ outerloop/harness.py,sha256=2UH2WZOKgQaeERVsIx3KX3muZRarYMOC4f25SLRw7qA,66092
18
+ outerloop/housekeeping.py,sha256=w1GWSIRZq3yBgxmIi__f0f_OF8TtKqaR-WtLvsWEjHI,6001
19
+ outerloop/image.py,sha256=bXCB-VqFfzJnnYVgclC0JuCN-AXJZkq938z4vV8Bvoo,15330
20
+ outerloop/init.py,sha256=IwxAEG8mvESP__Z8A_tqnPG5c2cAoMcRf6W3gFodq_g,34393
21
+ outerloop/intake.py,sha256=UxJddNYfB2IuV57mcUZ_7ShfJTJXavfUiFm5xo7g9lg,5243
22
+ outerloop/launchlog.py,sha256=oJf8ADmptkB_yz8-Dp6smkPjgn3Yl3oKpEEFAlNflN0,8975
23
+ outerloop/limits.py,sha256=ubdBqhtN8SN4r5RoDhuptoolqtq_m-1KZPdW3iXaml8,3385
24
+ outerloop/maintain.py,sha256=9_4rpOvSMBLKiO9MZnSNkPLg6nu7fv8TVJpwPIZVDB4,15525
25
+ outerloop/maintain_agent_cli.py,sha256=QRF6VUDVR05_wxXJyBgvhsUUMqm3NvSIBbwdG9Cfn3Y,3010
26
+ outerloop/maintain_post_cli.py,sha256=0c_i5fa1QEqUKauqyq9hTa6opviIF9Y3v6X5ezvZvyo,5567
27
+ outerloop/markers.py,sha256=Bi_hwWCICW6qPQ8_yB--0IrUaLLh9C3K24aDaCxHNAY,1739
28
+ outerloop/measure.py,sha256=hUCy17FcWyd6yIFhRyNrLfLfNWtSs1yasFVYJKUbVd0,22795
29
+ outerloop/orchestrator.py,sha256=I7bYVJYCY4Pus9jqlS4JkVqYiKum5lQc_d43Z0KG6Jg,89372
30
+ outerloop/panel.py,sha256=DcK3E0WdUUZPao0DWFCz3uLtm-VDmhhv1QTQHiEYcEY,7677
31
+ outerloop/paths.py,sha256=SpOt3zU2KS9_KZ6FiNEH9vs9fbQt8y1rWMYbZskwxHA,1501
32
+ outerloop/posting.py,sha256=dUi7P46HzsQmVgs5BXy5GXGOkcTNjOZDO-x_y0yAaaU,6632
33
+ outerloop/progress.py,sha256=9vCKsO8_NGrw3-hhSif5QA6rLoqNOrQ8ICIpVBqD2VI,6133
34
+ outerloop/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
35
+ outerloop/review.py,sha256=rpqdqhNjlvmbRln_zNbhrGyNmpt39Pxu5Oq7jEVcYtI,27416
36
+ outerloop/review_agent.py,sha256=sPN2-mAfe1e3uLn2ogJrppIMAvGuS2NTgiitCQHlaRw,9999
37
+ outerloop/review_agent_cli.py,sha256=qWE79-rP8HqNPLFAgyuNJxoHCtxowV-Ee6GKJib644U,8371
38
+ outerloop/review_post_cli.py,sha256=VzZunoNOzNTDIR710hDJ2n3xE2lNtLGL3KFgwV-1Bmo,6264
39
+ outerloop/review_summarize_cli.py,sha256=f1MK9tMo6P4SfrwJS0CFbwV35XaACL_WyzhZCgT53rs,6862
40
+ outerloop/role_runner.py,sha256=4l4zw0TtiFrGDmzHW8T6vZ7OLq7vVD0cN4U4BoNbgUs,9681
41
+ outerloop/roles.py,sha256=PyhWGhZm7tEr_t5sfiPJplxO4BK3JGGB5lGgQFMKIcI,12253
42
+ outerloop/rolespec.py,sha256=Y9vsUsPLtSc1ricvFSaA2qmOMkOg76TPnHsdGZOi0ME,3935
43
+ outerloop/runstate.py,sha256=5bEczCz1KTbVI1cTwNvd4Oc2RXIX-9piWD5-g_S6tMo,17976
44
+ outerloop/steward.py,sha256=iv6OUpZ-QypJbQZJaoYKz89FfpmFH6Xy53ADyCVLF5s,34107
45
+ outerloop/style.py,sha256=icZQNq0hOLjhoXStpH7rxZm24fsAh4uL8NMH72nz9S0,497
46
+ outerloop/syscall.py,sha256=XsjS0TkzvdNt2HjOSiVxgcbwf8QKIXibfLq2DbDiQcI,54317
47
+ outerloop/syscall_cli.py,sha256=srjqlNDhSmrPJI4haYRxVsGwzy4uO-dx_Uhu5fneLk0,31768
48
+ outerloop/tick.py,sha256=95gHa44SWIldM9RHSkiD4vXJCPQm6K2PTTDYaMVNtbI,154872
49
+ outerloop/verifier.py,sha256=BeizeiAqXGiuL-pmGptedtFo_GWAxfguwZhoTdAn1qg,17793
50
+ outerloop/verify_agent.py,sha256=x9OINnpmISDIjq5q5wtJ1wUD13Obpc8UXvF18vfkONY,6550
51
+ outerloop/verify_agent_cli.py,sha256=3QCQTB7MSdtN-k9x9HovLPo8q988H9o9Uus8rmaR444,3908
52
+ outerloop/verify_post_cli.py,sha256=qbQ-tT_MQUzKIKLTrJEgLn0O320JnrJESKyBqqjznU4,4599
53
+ outerloop/watcher.py,sha256=l377ldNgnmDR_jsbQyM-QrExmAwNTha0wEVGDv1TVNc,8319
54
+ outerloop_science-0.1.0.dev4.dist-info/METADATA,sha256=HMkAzSFOFiDsi4fFF2_gSw3pSCKSuCoPnuH8-6gpigA,6967
55
+ outerloop_science-0.1.0.dev4.dist-info/WHEEL,sha256=zOwg4jB6zX2kU910N-cMawjivD6tO8NEWvE12je1bVk,87
56
+ outerloop_science-0.1.0.dev4.dist-info/entry_points.txt,sha256=mLYW8Dxv8FnrYdCkKV8ekwcV3TnfzPYkdbJOkYzF5xE,49
57
+ outerloop_science-0.1.0.dev4.dist-info/licenses/LICENSE,sha256=Kqt7czAsZdfZp9B6a3WoTt9CZuy7_0sjPNMW7KHckk8,11375
58
+ outerloop_science-0.1.0.dev4.dist-info/licenses/NOTICE,sha256=VTBNz2gnaoM68CoUkQe1G7VbrV-R0WfqKHrZhYYbuqI,193
59
+ outerloop_science-0.1.0.dev4.dist-info/RECORD,,
@@ -1,53 +0,0 @@
1
- outerloop/__init__.py,sha256=sIIc5yYu2cnaaZt1u9_X2RLGOYt3UlnLPwNhoWNIMyE,633
2
- outerloop/__main__.py,sha256=Fq2ADl3rjZ4i2m1svON_KO2kJqpwUnohLhdlacAnEpM,57
3
- outerloop/appauth.py,sha256=j4XG7gF8Zeg91R-MG7A2S1IxgmJCqSQEgaGA7iwRZtY,8898
4
- outerloop/appmanifest.py,sha256=9L4NyUqVJhgE8BlBnK6BTpMdkhpukLtAnezZQcbyBwI,8459
5
- outerloop/attempt.py,sha256=Unfne2yY9DeHKXKUdqcQHbC68LruwOPDu0k5LNhoQtM,159339
6
- outerloop/brief.py,sha256=f8QeMQRhsZvyNEh28sbM684FwWZCGU-rwPL0o8lqtrw,24156
7
- outerloop/cli.py,sha256=3FJqLpxlEsLQ1RoSiJhzQSWqChVHwXKZ-EJ1WeTpwRg,20859
8
- outerloop/climbboard.py,sha256=GoucONPvtjIWHz3jH_9ewX4iSZfeh7q_PtR5atClogA,67011
9
- outerloop/compute.py,sha256=z1nG559IBAi16qH3Ux9APWmDWG-oqkWGf9Y5GjV5ljs,24589
10
- outerloop/contract.py,sha256=lj_EPz-oPNdM_LacF0M04-Jqlvs0r_qNRlr8zP1cOiI,24516
11
- outerloop/contract_cli.py,sha256=Pa-sJe7G8BuLGed5tqnlq6L75fTT1HHO4d-XWZPW8U8,1992
12
- outerloop/disk.py,sha256=FAgR1FJVPTu1YCcfNxcJtUFxJsBFcOaenF8lYAVPL1Y,5853
13
- outerloop/dispatch.py,sha256=70Of08UygVco9drBpq8Xfqi5tunMBvNk--tPYZjcpNI,27401
14
- outerloop/followup.py,sha256=dM6z2te4yOoQ2WNak2rr-npVCQ8WQtfDXjUe_BaYbFo,94680
15
- outerloop/github.py,sha256=mOR6Hp_fAVEXZZ0NWyLyOEyqlM_5CGqnpgDSX2YYqzM,65305
16
- outerloop/harness.py,sha256=0RXz-TWkSigxLhlAxnq0OcNQF0rHajJuuSraInHm5VA,67232
17
- outerloop/housekeeping.py,sha256=nlyy9kWUwTPjW0FLizVwXI99xSTZj7JJDzIdzJzDfEk,6663
18
- outerloop/image.py,sha256=nVnU2HQIWMXKgluUPw7exCcPDPv0fnKxsXBG2Sdo9os,15406
19
- outerloop/init.py,sha256=w7HEJ76p0A9PWNuFkLQ27_LnlZJ4ON0o_djJyo4I_jw,31951
20
- outerloop/intake.py,sha256=a3j1B6W7v3QpQv53CoJS0hh6vz7B3nLn_cV2G-nt6Uw,5424
21
- outerloop/limits.py,sha256=ubdBqhtN8SN4r5RoDhuptoolqtq_m-1KZPdW3iXaml8,3385
22
- outerloop/markers.py,sha256=Bi_hwWCICW6qPQ8_yB--0IrUaLLh9C3K24aDaCxHNAY,1739
23
- outerloop/measure.py,sha256=NFAljbUV29py4JoqfFSI3JYGho8by_L1eDgPw2ubNjU,22493
24
- outerloop/orchestrator.py,sha256=bH2pOhpfJdwhvZvjqlQnfrwTMMWwQOLB-Xh6Ew2468Q,84458
25
- outerloop/panel.py,sha256=bSoQtuP2xqWbl7DBdeqihrGNyDz6-u5wWrV3nI-b0Js,7665
26
- outerloop/paths.py,sha256=SpOt3zU2KS9_KZ6FiNEH9vs9fbQt8y1rWMYbZskwxHA,1501
27
- outerloop/posting.py,sha256=dUi7P46HzsQmVgs5BXy5GXGOkcTNjOZDO-x_y0yAaaU,6632
28
- outerloop/progress.py,sha256=9vCKsO8_NGrw3-hhSif5QA6rLoqNOrQ8ICIpVBqD2VI,6133
29
- outerloop/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
30
- outerloop/review.py,sha256=_vJcTCVdtrK7ymfd0pgjz-IE7oM3CVzMceNZt1AMhGk,27184
31
- outerloop/review_agent.py,sha256=XxGrGVZb61pqd6F3I15lrFBQptduER7QNmxMmCaUNec,9961
32
- outerloop/review_agent_cli.py,sha256=R9rZHvQ9LSttocSUuO0087fywLf3iHt-vldxo6Ygd7k,8355
33
- outerloop/review_post_cli.py,sha256=XNw8BjxJMtEMzWRK4jKV-f_alOdIWeqJfwdndFCqTm8,6266
34
- outerloop/review_summarize_cli.py,sha256=p_gmTGj_uEKik47EgVuHNDDtO18cw2dVznnvnm_9k3c,6791
35
- outerloop/role_runner.py,sha256=4l4zw0TtiFrGDmzHW8T6vZ7OLq7vVD0cN4U4BoNbgUs,9681
36
- outerloop/roles.py,sha256=hx8RrtJCBb0nsJGvxamHJ8Dk3j8lIFsTCapbOlQyKN0,10952
37
- outerloop/rolespec.py,sha256=6VmrwTlLSsN17aQSDtLB2MvcEHYpbSOPIyPcY1uGny8,3915
38
- outerloop/runstate.py,sha256=5bEczCz1KTbVI1cTwNvd4Oc2RXIX-9piWD5-g_S6tMo,17976
39
- outerloop/steward.py,sha256=3MiYQsSDVR6KGU8dDji3v_vr8OUT5nl_6JWhUB4U9AE,34396
40
- outerloop/style.py,sha256=icZQNq0hOLjhoXStpH7rxZm24fsAh4uL8NMH72nz9S0,497
41
- outerloop/syscall.py,sha256=367I0rhhkEgm7-KNkXaX1h9960K_y2mTRmLjwcFqFCM,44601
42
- outerloop/syscall_cli.py,sha256=e2Y_TQ9rAxAKjwLgSHHKgT93yMfu55SFO04k4s83OF4,22079
43
- outerloop/tick.py,sha256=tLMDs-zoPy_y659s1FQuglevDURR84bXGZsVCROJX-Q,157004
44
- outerloop/verifier.py,sha256=BeizeiAqXGiuL-pmGptedtFo_GWAxfguwZhoTdAn1qg,17793
45
- outerloop/verify_agent.py,sha256=fzv5N_cm5g6ZcwMmIpd5S5UbqW3wAEXTQmPFWK_Zdi4,6482
46
- outerloop/verify_agent_cli.py,sha256=3QCQTB7MSdtN-k9x9HovLPo8q988H9o9Uus8rmaR444,3908
47
- outerloop/verify_post_cli.py,sha256=vw2WXzP10HlZW7inDDpt6k6Zq9sPQwFnmXrwvnpHjZg,4601
48
- outerloop_science-0.1.0.dev2.dist-info/METADATA,sha256=vHimtrNg4OlCZSXbJMWvIrvvrJmvC7ZjO3qogLgRkYw,6659
49
- outerloop_science-0.1.0.dev2.dist-info/WHEEL,sha256=zOwg4jB6zX2kU910N-cMawjivD6tO8NEWvE12je1bVk,87
50
- outerloop_science-0.1.0.dev2.dist-info/entry_points.txt,sha256=mLYW8Dxv8FnrYdCkKV8ekwcV3TnfzPYkdbJOkYzF5xE,49
51
- outerloop_science-0.1.0.dev2.dist-info/licenses/LICENSE,sha256=Kqt7czAsZdfZp9B6a3WoTt9CZuy7_0sjPNMW7KHckk8,11375
52
- outerloop_science-0.1.0.dev2.dist-info/licenses/NOTICE,sha256=VTBNz2gnaoM68CoUkQe1G7VbrV-R0WfqKHrZhYYbuqI,193
53
- outerloop_science-0.1.0.dev2.dist-info/RECORD,,