solvewright 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 (31) hide show
  1. solvewright/__init__.py +3 -0
  2. solvewright/__main__.py +3 -0
  3. solvewright/agents.py +450 -0
  4. solvewright/cli.py +263 -0
  5. solvewright/connection.py +137 -0
  6. solvewright/interviewer/cases/portals/brief.txt +13 -0
  7. solvewright/interviewer/cases/portals/interviewer.txt +49 -0
  8. solvewright/interviewer/cases/portals/reported-round.txt +34 -0
  9. solvewright/interviewer/data/portal-samples/sample-01.json +967 -0
  10. solvewright/interviewer/data/portal-samples/sample-02.json +967 -0
  11. solvewright/interviewer/data/portal-samples/sample-03.json +967 -0
  12. solvewright/interviewer/data/portal-samples/sample-04.json +967 -0
  13. solvewright/interviewer/data/portal-samples/sample-05.json +967 -0
  14. solvewright/interviewer/data/portal-samples/sample-06.json +967 -0
  15. solvewright/interviewer/data/portal-samples/sample-07.json +967 -0
  16. solvewright/interviewer/data/portal-samples/sample-08.json +967 -0
  17. solvewright/interviewer/interviewer.txt +113 -0
  18. solvewright/interviewer/references/case-generation.txt +52 -0
  19. solvewright/interviewer/references/curriculum.txt +21 -0
  20. solvewright/interviewer/references/scoring.txt +66 -0
  21. solvewright/interviewer/references/session-format.txt +60 -0
  22. solvewright/interviewer/tools/portal_lab.py +217 -0
  23. solvewright/runtime.py +673 -0
  24. solvewright/service.py +52 -0
  25. solvewright/settings.py +52 -0
  26. solvewright/worker.py +159 -0
  27. solvewright-0.1.0.dist-info/METADATA +132 -0
  28. solvewright-0.1.0.dist-info/RECORD +31 -0
  29. solvewright-0.1.0.dist-info/WHEEL +4 -0
  30. solvewright-0.1.0.dist-info/entry_points.txt +2 -0
  31. solvewright-0.1.0.dist-info/licenses/LICENSE +21 -0
solvewright/cli.py ADDED
@@ -0,0 +1,263 @@
1
+ """`solvewright`: connect this computer to Interview Lab and serve its rounds.
2
+
3
+ solvewright connect if needed, then wait for interview rounds
4
+ solvewright connect approve this computer again (for example after a reset)
5
+ solvewright status show the connection and the agents found here
6
+ solvewright disconnect remove this computer from your account
7
+ solvewright doctor check Python, Claude Code, Codex and the sandbox
8
+
9
+ Nothing runs in the background: closing the window stops the connector.
10
+ """
11
+ from __future__ import annotations
12
+
13
+ import argparse
14
+ import os
15
+ import signal
16
+ import sys
17
+ import time
18
+ import urllib.request
19
+ import webbrowser
20
+ from datetime import datetime
21
+ from pathlib import Path
22
+
23
+ from . import __version__, agents, connection, settings as settings_module, worker
24
+ from .runtime import Runtime
25
+ from .service import Service, ServiceError, Unavailable
26
+
27
+ INSTRUCTIONS = Path(__file__).resolve().parent / "interviewer"
28
+ ISOLATION_TEXT = {"sandbox": "sandboxed", "writes": "writes sandboxed, reads not limited",
29
+ "no-commands": "no commands (sandbox unavailable here)"}
30
+
31
+
32
+ class Out:
33
+ def __init__(self, stream=sys.stdout):
34
+ self.stream = stream
35
+ self.color = stream.isatty() and not os.environ.get("NO_COLOR") and os.environ.get("TERM") != "dumb"
36
+
37
+ def _wrap(self, code: str, text: str) -> str:
38
+ return f"\033[{code}m{text}\033[0m" if self.color else text
39
+
40
+ def bold(self, text: str) -> str:
41
+ return self._wrap("1", text)
42
+
43
+ def dim(self, text: str) -> str:
44
+ return self._wrap("2", text)
45
+
46
+ def good(self, text: str) -> str:
47
+ return self._wrap("32", text)
48
+
49
+ def warn(self, text: str) -> str:
50
+ return self._wrap("33", text)
51
+
52
+ def line(self, text: str = "") -> None:
53
+ print(text, file=self.stream, flush=True)
54
+
55
+ def row(self, label: str, value: str) -> None:
56
+ self.line(" " + self.dim(label.ljust(14)) + value)
57
+
58
+ def event(self, text: str) -> None:
59
+ self.line(self.dim(datetime.now().strftime("%H:%M")) + " " + text)
60
+
61
+
62
+ def pretty_path(path: Path) -> str:
63
+ try:
64
+ return "~/" + path.relative_to(Path.home()).as_posix()
65
+ except ValueError:
66
+ return str(path)
67
+
68
+
69
+ def agent_rows(out: Out, described: dict) -> None:
70
+ for name, label in (("claude", "Claude Code"), ("codex", "Codex")):
71
+ info = described[name]
72
+ if info["available"]:
73
+ out.row(label, f"{info['version']} · {ISOLATION_TEXT.get(info['isolation'], info['isolation'])}")
74
+ else:
75
+ out.row(label, out.dim("not found"))
76
+
77
+
78
+ def header(out: Out, config, label: str, described: dict, account: str | None = None) -> None:
79
+ out.line(out.bold("Solvewright connector") + " " + out.dim(__version__))
80
+ out.line()
81
+ out.row("This computer", f"{label} · {connection.operating_system()}")
82
+ if account:
83
+ out.row("Account", "@" + account)
84
+ agent_rows(out, described)
85
+ out.row("Interviews", pretty_path(config.interviews))
86
+ if not any(info["available"] for info in described.values()):
87
+ out.line()
88
+ out.line(out.warn("No Claude Code or Codex was found on this computer."))
89
+ out.line("Install one and sign in once in a terminal, then start the connector again:")
90
+ out.line(" Claude Code https://code.claude.com/docs/en/setup")
91
+ out.line(" Codex https://developers.openai.com/codex/cli")
92
+
93
+
94
+ def approve(out: Out, config, service: Service, label: str, open_browser: bool) -> dict:
95
+ described = agents.describe()
96
+ details = {"connector": __version__, "os": connection.operating_system(),
97
+ "python": ".".join(str(part) for part in sys.version_info[:3]),
98
+ "agents": {name: info["version"] for name, info in described.items() if info["available"]}}
99
+ pending = connection.request(service, label, details)
100
+ code = pending["userCode"]
101
+ url = f"{config.site_url}/connect?code={code}"
102
+ out.line()
103
+ out.line("Approve this computer in your browser:")
104
+ out.line()
105
+ out.line(" " + out.bold(url))
106
+ out.line()
107
+ out.line(" Check that the page shows " + out.bold(code))
108
+ out.line()
109
+ if open_browser:
110
+ try:
111
+ webbrowser.open(url)
112
+ except webbrowser.Error:
113
+ pass
114
+ shown = {"last": None}
115
+
116
+ def waiting(seconds_left: int) -> None:
117
+ minutes = seconds_left // 60
118
+ if shown["last"] != minutes:
119
+ shown["last"] = minutes
120
+ out.event(out.dim(f"Waiting for approval ({minutes + 1} min left). Ctrl+C cancels."))
121
+
122
+ reply = connection.wait(service, pending, on_wait=waiting)
123
+ record = connection.record(reply, config.service_url, label)
124
+ connection.save(config.connection_file, record)
125
+ out.line()
126
+ out.line(out.good("✓") + " Connected" + (f" to @{record['account']}" if record["account"] else "") + f" as “{label}”.")
127
+ return record
128
+
129
+
130
+ def serve(out: Out, config, service: Service, record: dict) -> int:
131
+ runtime = Runtime(config.interviews, INSTRUCTIONS, config.home / "logs")
132
+ runtime.on_event = out.event
133
+ out.line()
134
+ out.line("Interview Lab is ready: " + out.bold(f"{config.site_url}/coding/interviews"))
135
+ out.line("Keep this window open while you practise. Closing it stops the connector;")
136
+ out.line("nothing keeps running in the background.")
137
+ out.line()
138
+ out.event("Waiting for the Interview Lab")
139
+ if sys.platform == "win32":
140
+ def stop_on_break(_signal, _frame):
141
+ raise KeyboardInterrupt
142
+ signal.signal(signal.SIGBREAK, stop_on_break)
143
+ try:
144
+ worker.run(runtime, service, record, say=out.event)
145
+ except KeyboardInterrupt:
146
+ out.line()
147
+ out.line("Stopped. Nothing from Solvewright is running on this computer now.")
148
+ return 0
149
+ except worker.Revoked:
150
+ connection.forget(config.connection_file)
151
+ out.line()
152
+ out.line(out.warn("This computer was disconnected from your Solvewright account."))
153
+ out.line("Run `solvewright` again to connect it.")
154
+ return 2
155
+ except ServiceError as error:
156
+ out.line(out.warn(str(error)))
157
+ return 1
158
+ finally:
159
+ runtime.close()
160
+ return 0
161
+
162
+
163
+ def start(out: Out, config, args) -> int:
164
+ service = Service(config.service_url, config.service_key)
165
+ label = (args.name or connection.computer_name()).strip()[:64]
166
+ record = connection.load(config.connection_file)
167
+ if record and record.get("service") != config.service_url:
168
+ record = None
169
+ described = agents.describe()
170
+ header(out, config, record["label"] if record and not args.name else label, described, record.get("account") if record else None)
171
+ if args.command == "connect" or not record:
172
+ if record:
173
+ try:
174
+ service.rpc("remove_interview_worker", {"p_worker_id": record["workerId"], "p_key": record["key"]})
175
+ except (OSError, ServiceError):
176
+ pass
177
+ connection.forget(config.connection_file)
178
+ try:
179
+ record = approve(out, config, service, label, not args.no_browser)
180
+ except KeyboardInterrupt:
181
+ out.line()
182
+ out.line("Cancelled. This computer was not connected.")
183
+ return 130
184
+ except connection.ApprovalDenied:
185
+ out.line(out.warn("The connection was declined in the browser. Nothing was saved."))
186
+ return 1
187
+ except connection.ApprovalExpired:
188
+ out.line(out.warn("The code expired. Run `solvewright` again for a new one."))
189
+ return 1
190
+ except (Unavailable, ServiceError) as error:
191
+ out.line(out.warn(str(error)))
192
+ return 1
193
+ return serve(out, config, service, record)
194
+
195
+
196
+ def status(out: Out, config) -> int:
197
+ record = connection.load(config.connection_file)
198
+ header(out, config, record["label"] if record else connection.computer_name(), agents.describe(), record.get("account") if record else None)
199
+ out.line()
200
+ if record:
201
+ out.line(f"Connected since {record.get('connectedAt', 'an earlier session')}. Run `solvewright` to serve rounds.")
202
+ else:
203
+ out.line("Not connected. Run `solvewright` to approve this computer.")
204
+ return 0
205
+
206
+
207
+ def disconnect(out: Out, config) -> int:
208
+ record = connection.load(config.connection_file)
209
+ if not record:
210
+ out.line("This computer is not connected.")
211
+ return 0
212
+ try:
213
+ Service(config.service_url, config.service_key).rpc("remove_interview_worker", {"p_worker_id": record["workerId"], "p_key": record["key"]})
214
+ except (OSError, ServiceError) as error:
215
+ out.line(out.warn(f"Could not reach Solvewright ({error}). Remove the computer from the Interview Lab page instead."))
216
+ connection.forget(config.connection_file)
217
+ out.line("Disconnected. Your interview files stay in " + pretty_path(config.interviews) + ".")
218
+ return 0
219
+
220
+
221
+ def doctor(out: Out, config) -> int:
222
+ out.line(out.bold("Solvewright connector") + " " + out.dim(__version__))
223
+ out.line()
224
+ out.row("Python", sys.version.split()[0] + ("" if sys.version_info >= (3, 10) else out.warn(" needs 3.10 or newer")))
225
+ described = agents.describe(max_age=0)
226
+ agent_rows(out, described)
227
+ claude = agents.executable("claude")
228
+ if claude:
229
+ out.row("Claude flags", "ok" if agents.claude_supports_isolation(claude) else out.warn("too old: run `claude update`"))
230
+ out.row("Claude sandbox", "available" if agents.claude_can_sandbox() else out.warn(
231
+ "unavailable" + (" (install bubblewrap and socat)" if sys.platform.startswith("linux") else "")))
232
+ try:
233
+ with urllib.request.urlopen(config.site_url, timeout=10) as response:
234
+ out.row("Solvewright", "reachable" if response.status == 200 else f"HTTP {response.status}")
235
+ except OSError as error:
236
+ out.row("Solvewright", out.warn(f"unreachable ({error})"))
237
+ record = connection.load(config.connection_file)
238
+ out.row("Connection", f"@{record.get('account') or '?'} · {record['label']}" if record else "not connected")
239
+ return 0
240
+
241
+
242
+ def main(argv: list[str] | None = None) -> int:
243
+ parser = argparse.ArgumentParser(prog="solvewright", description="Connect your own Claude Code or Codex to Solvewright Interview Lab.",
244
+ epilog="Source: https://github.com/fus3r/solvewright-connector")
245
+ parser.add_argument("command", nargs="?", default="start", choices=("start", "connect", "status", "disconnect", "doctor"))
246
+ parser.add_argument("--name", help="how this computer is named in your account")
247
+ parser.add_argument("--no-browser", action="store_true", help="print the approval link without opening a browser")
248
+ parser.add_argument("--home", help=argparse.SUPPRESS)
249
+ parser.add_argument("--version", action="version", version=f"solvewright {__version__}")
250
+ args = parser.parse_args(argv)
251
+ out = Out()
252
+ config = settings_module.load(args.home)
253
+ if args.command == "status":
254
+ return status(out, config)
255
+ if args.command == "disconnect":
256
+ return disconnect(out, config)
257
+ if args.command == "doctor":
258
+ return doctor(out, config)
259
+ return start(out, config, args)
260
+
261
+
262
+ if __name__ == "__main__":
263
+ raise SystemExit(main())
@@ -0,0 +1,137 @@
1
+ """Approving this computer from the browser, and the key that approval yields.
2
+
3
+ The flow is the one `gh auth login` uses: the terminal shows a short code, the
4
+ person signed in to Solvewright confirms it on solvewright.app/connect, and
5
+ only then does this computer receive its own key. The command line never
6
+ carries a secret, and the key never leaves this machine; the service keeps a
7
+ hash of it.
8
+ """
9
+ from __future__ import annotations
10
+
11
+ import json
12
+ import os
13
+ import platform
14
+ import re
15
+ import subprocess
16
+ import sys
17
+ import tempfile
18
+ import time
19
+ from datetime import datetime, timezone
20
+ from pathlib import Path
21
+
22
+ from .service import Service, ServiceError, Unavailable
23
+
24
+ FORMAT = "solvewright-connection-v1"
25
+
26
+
27
+ class ApprovalDenied(Exception):
28
+ pass
29
+
30
+
31
+ class ApprovalExpired(Exception):
32
+ pass
33
+
34
+
35
+ def computer_name() -> str:
36
+ name = ""
37
+ if sys.platform == "darwin":
38
+ try:
39
+ name = subprocess.run(["scutil", "--get", "ComputerName"], capture_output=True, text=True, timeout=3).stdout
40
+ except (OSError, subprocess.SubprocessError):
41
+ name = ""
42
+ elif sys.platform == "win32":
43
+ name = os.environ.get("COMPUTERNAME", "")
44
+ name = name or platform.node()
45
+ name = re.sub(r"\.(local|lan|home)$", "", name.strip())
46
+ name = re.sub(r"[\x00-\x1f\x7f]", "", name)
47
+ return name[:64] or "This computer"
48
+
49
+
50
+ def operating_system() -> str:
51
+ if sys.platform == "darwin":
52
+ return "macOS " + (platform.mac_ver()[0] or "").strip()
53
+ if sys.platform == "win32":
54
+ return "Windows " + platform.release()
55
+ try:
56
+ release = platform.freedesktop_os_release()
57
+ return release.get("PRETTY_NAME") or "Linux"
58
+ except (OSError, AttributeError):
59
+ return "Linux"
60
+
61
+
62
+ def load(path: Path) -> dict | None:
63
+ try:
64
+ data = json.loads(path.read_text(encoding="utf-8"))
65
+ except (OSError, ValueError):
66
+ return None
67
+ if not isinstance(data, dict) or data.get("format") != FORMAT:
68
+ return None
69
+ if not all(isinstance(data.get(field), str) and data[field] for field in ("workerId", "owner", "key", "service")):
70
+ return None
71
+ return data
72
+
73
+
74
+ def save(path: Path, data: dict) -> None:
75
+ path.parent.mkdir(parents=True, exist_ok=True)
76
+ handle, temporary = tempfile.mkstemp(prefix=".connection-", dir=path.parent)
77
+ try:
78
+ with os.fdopen(handle, "w", encoding="utf-8") as file:
79
+ json.dump({"format": FORMAT, **data}, file, indent=2)
80
+ os.chmod(temporary, 0o600)
81
+ os.replace(temporary, path)
82
+ except BaseException:
83
+ try:
84
+ os.unlink(temporary)
85
+ except OSError:
86
+ pass
87
+ raise
88
+
89
+
90
+ def forget(path: Path) -> None:
91
+ try:
92
+ path.unlink()
93
+ except FileNotFoundError:
94
+ pass
95
+
96
+
97
+ def request(service: Service, label: str, details: dict) -> dict:
98
+ reply = service.rpc("start_interview_connection", {"p_label": label, "p_details": details})
99
+ if not isinstance(reply, dict) or not all(isinstance(reply.get(key), str) for key in ("deviceCode", "userCode")):
100
+ raise ServiceError("Solvewright returned an incomplete connection request.")
101
+ return reply
102
+
103
+
104
+ def wait(service: Service, pending: dict, on_wait=None, sleep=time.sleep, now=time.monotonic) -> dict:
105
+ """Poll until the browser approves or denies, or the code expires."""
106
+ interval = max(2, int(pending.get("interval") or 3))
107
+ deadline = now() + max(60, int(pending.get("expiresIn") or 600))
108
+ while now() < deadline:
109
+ try:
110
+ reply = service.rpc("poll_interview_connection", {"p_device_code": pending["deviceCode"]})
111
+ except Unavailable:
112
+ reply = {"status": "pending"}
113
+ status = reply.get("status") if isinstance(reply, dict) else None
114
+ if status == "approved":
115
+ if not all(isinstance(reply.get(key), str) and reply[key] for key in ("workerId", "owner", "key")):
116
+ raise ServiceError("Solvewright approved this computer but sent an incomplete key.")
117
+ return reply
118
+ if status == "denied":
119
+ raise ApprovalDenied()
120
+ if status == "expired":
121
+ raise ApprovalExpired()
122
+ if on_wait:
123
+ on_wait(max(0, int(deadline - now())))
124
+ sleep(interval)
125
+ raise ApprovalExpired()
126
+
127
+
128
+ def record(reply: dict, service_url: str, label: str) -> dict:
129
+ return {
130
+ "service": service_url,
131
+ "workerId": reply["workerId"],
132
+ "owner": reply["owner"],
133
+ "key": reply["key"],
134
+ "account": reply.get("handle") or "",
135
+ "label": label,
136
+ "connectedAt": datetime.now(timezone.utc).isoformat(timespec="seconds"),
137
+ }
@@ -0,0 +1,13 @@
1
+ # Reusable portals — candidate brief
2
+
3
+ This is a synthetic practice case inspired by a reported round at a quant firm (HRT or Jane Street). Its exact rules and datasets are invented for training.
4
+
5
+ Nomads arrive with an origin and destination on a 2D plane. Walking a unit of Euclidean distance costs $1. You may construct reusable bidirectional portals. Constructing a portal from `a` to `b` costs `5 * distance(a, b)` once; traversing it is free.
6
+
7
+ At each step you see the current origin–destination request and all earlier requests. Before that nomad travels, you may add zero or more portals whose endpoints are finite 2D coordinates within ±1,000,000 on each axis (a practical runner limit). Existing portals persist for the rest of the episode. You cannot move or remove them. A nomad takes the shortest route through any combination of walking and the portals built so far. Each trip is charged once at its arrival; later construction does not change an earlier trip's bill.
8
+
9
+ Minimize the sum of construction and travel costs over the episode. The horizon is known. You do not see future evaluation requests. Eight complete **synthetic training episodes** are available in `data/portal-samples/`; you may inspect and plot them. Fresh evaluation episodes follow this mock's generator, not a claimed reconstruction of HRT's generator.
10
+
11
+ For practical evaluation, the runner limits an episode to 24 portals and gives a submitted policy 10 seconds to answer each request on the local machine. The scorer and stream runner are provided; you are not required to implement the routing evaluator unless that is the selected task. Python's standard library is enough for a policy; exploration tools are optional.
12
+
13
+ Begin by explaining what you would establish and implement before optimizing placement. There is no requirement to prove a globally optimal online policy.
@@ -0,0 +1,49 @@
1
+ # Portal case — examiner only
2
+
3
+ Read [reported-round.txt](reported-round.txt) for provenance and uncertainty. Show [brief.txt](brief.txt) at the start of the playable case. Do not present synthetic defaults as recovered historical details.
4
+
5
+ ## Contract and runner
6
+
7
+ The brief fixes Euclidean walking, free teleportation, bidirectional reusable portals, endpoints within ±1,000,000 per axis, no deletion, current-request-before-travel construction, no retrospective savings, and no future observations. The training generator uses 96 arrivals per episode. The coordinate bound, 24-portal limit and 10-second per-response timeout are practical mock limits, not source facts. Candidate policies receive the horizon in every request.
8
+
9
+ Keep `tools/portal_lab.py` examiner-only until the candidate has completed sample investigation: its source contains the synthetic distribution. The source generates origins in arrival order along a slowly drifting random walk and destinations around a translated, noisy copy. Both the law and this choice of interpretation are invented. It is an empirical optimization exercise, not an exact recreation. Eight shipped samples use fixed training seeds. For final evaluation, generate new seeds after the candidate freezes the policy. Report every tested episode, not just the best.
10
+
11
+ Commands, run from the context/interviewer folder:
12
+
13
+ ```sh
14
+ python3 tools/portal_lab.py self-test
15
+ python3 tools/portal_lab.py samples --out data/portal-samples
16
+ python3 tools/portal_lab.py generate --seed 9001 --out ../../.examiner/portal-eval-9001.json
17
+ python3 tools/portal_lab.py score --episode data/portal-samples/sample-01.json
18
+ python3 tools/portal_lab.py run --episode ../../.examiner/portal-eval-9001.json --policy /absolute/path/policy.py
19
+ ```
20
+
21
+ Write generated evaluation episodes under `.examiner/`: the sandbox only allows writes inside the round folder. The policy protocol is newline-delimited JSON over stdin/stdout, one request and one response at a time. Input is `{"step":0,"horizon":96,"origin":[x,y],"destination":[x,y],"portals":[...]}`. Response is `{"build":[{"a":[x,y],"b":[x,y]}]}`; an empty list means no construction. Send debug logs to stderr. The runner launches the candidate Python file as a child, enforces protocol and sequential delivery, and calculates the routes; the child must not open the evaluation episode or generator files. This is a trust-based local protocol, not an adversarial sandbox.
22
+
23
+ For offline scoring, a plan file is `[{"step":0,"a":[0,0],"b":[10,0]}, ...]`. `score --plan plan.json` replays the build schedule. This checks the objective, not whether the plan was generated without future access. Distinguish it from online policy evaluation.
24
+
25
+ ## Stages (release individually)
26
+
27
+ **Clarify and baseline.** Ask the candidate to state the objective and what information the decision sees. Offer the no-portal scorer as infrastructure. If the round is specifically about routing, ask for a small correct fixed-network evaluator instead; reduce the later workload accordingly. Do not require both a full routing engine and a strong policy in a short mock.
28
+
29
+ **Inspect samples.** Ask for the most useful first plot or summary, then let the candidate run it. Reward hypotheses supported by all samples and explicit uncertainty. Do not disclose the walk/copy structure in your questions. The original user already knows the reported walk premise, so record this case as `seen` unless a genuine transfer variant is used.
30
+
31
+ **One usable policy.** Ask for a baseline placement policy and a measurable comparison against no portals. It need not be sophisticated. Freeze calibration/validation choices before tuning. Ask what evidence would justify a build's one-time cost. Implementation can use the supplied evaluator. Do not optimize on final-test seeds.
32
+
33
+ **Online defense.** Ask the candidate to explain what happens at a late arrival or after demand changes. The online contract was visible from the start; do not “reveal” it now. Evaluate the cost of decisions at the time they were made.
34
+
35
+ **One extension if time remains.** Choose one: buffer of four future requests; endpoint restrictions; construction coefficient changes from five to eight; abrupt change of destination regime; traversal charge; build only after the current trip. Disclose it as a new variant and agree on a baseline. Changing timing must also change the evaluator, so do not score an after-travel policy with the bundled before-travel runner.
36
+
37
+ ## Private checking notes / scoped reveal material
38
+
39
+ For a fixed network of P portals, a complete walking graph over `s`, `t`, and all `2P` portal endpoints, plus zero-cost bidirectional edges for portal pairs, gives exact shortest travel distance. Dijkstra is enough. Comparing only “direct” and “one portal” is wrong when chains are allowed. Geometry can cross; there are no walls. The supplied evaluator recomputes this small graph per trip.
40
+
41
+ With a single proposed portal of length L, a necessary aggregate benefit condition over the chosen trips is travel savings greater than `5L`; measure marginal savings against the existing network, not against direct walking, to avoid counting the same benefit twice. This is an offline benefit calculation, not by itself an online optimality proof. A policy may estimate future demand from training episodes or a recent window, but its assumptions and risk must be explicit. At the final request, no benefit can exceed L for a new portal of length L under these rules, so the factor-five construction charge cannot pay for itself on that request alone.
42
+
43
+ Accept reasonable heuristics: representative endpoint pairs from recent requests, clustered demand, periodically evaluated builds, or another defensible scheme. The point is executable decisions and honest evaluation. Do not require the candidate to reproduce a preferred algorithm or infer the exact random generator.
44
+
45
+ Hints, if requested, in order: (1) “What would the bill be with no construction?” (2) “Which part of the bill is paid once, and which is paid for each trip?” (3) a tiny repeated-request counterexample to per-nomad construction; (4) a marginal-savings equation or partial policy. Reveal only the obstacle requested.
46
+
47
+ Score model quality and measured cost separately. Include no-portal cost, construction cost, travel cost, number of builds, and percent savings, which can be negative. Average across all held-out episodes. An offline comparator is a reference, not a known global optimum, unless proven.
48
+
49
+ For an unseen transfer, change the mechanism materially: a one-way shortcut with a per-use toll, an online facility with maintenance, or a request buffer with a build delay. Update all relevant contract fields and evaluation; use a different case ID and mark `transfer`. Merely rotating the same points is `seen` practice.
@@ -0,0 +1,34 @@
1
+ # Reported quant-firm round: reusable bidirectional portals
2
+
3
+ Cleaned reconstruction of a candidate's account of a practical round at a quant firm (HRT or Jane Street). It describes the reported exercise only. No original statement, dataset, evaluator or submitted solution was available.
4
+
5
+ ## Reported setup
6
+
7
+ A collection of nomads has starting and destination coordinates on a two-dimensional map. The candidate can place bidirectional portals to shorten journeys. Nomads take a shortest route using the portals made available to them.
8
+
9
+ The stated economic tradeoff was approximately:
10
+
11
+ - one dollar per unit of distance travelled by nomads;
12
+ - five dollars per unit of distance between a portal's two ends.
13
+
14
+ The candidate therefore needs to weigh construction cost against travel saved across multiple nomads. The recollection emphasizes reusable infrastructure rather than a separate portal for every traveller.
15
+
16
+ Eight input samples could be inspected. They contained starting and destination positions, with no solution outputs. The reporting candidate was told that generated tests would follow the same distribution. AI tools were allowed.
17
+
18
+ ## Data exploration and the information-flow issue
19
+
20
+ Plotting the samples suggested that points lay along a random walk. The relationship between nomad starts and ends on that walk was unusual and is not specified precisely in the account.
21
+
22
+ The reporting candidate initially approached the problem as a general optimization problem, then tried to recover the walk's order geometrically: identify possible ends and approximate successive points by nearby positions. The account does not establish that this reconstruction was correct or necessary.
23
+
24
+ Only after an implementation had been prepared did the interviewer reportedly clarify that inputs arrived sequentially, through a buffer, and that portal-placement decisions had to be made along the way. They further reported that points in generated tests arrived in the random walk's order. This made the arrival sequence directly useful, reducing the need to infer that order from the geometry alone.
25
+
26
+ ## What the account does not establish
27
+
28
+ The exact distance metric; portal traversal cost; whether portals could be chained; legal endpoint locations; construction timing; whether old portals persisted; whether travellers were charged immediately or rerouted retrospectively; buffer size and lookahead; number of travellers; runtime and memory limits; the exact walk generator; scoring normalization; and total round duration were not supplied. It is also ambiguous whether “points” arriving online meant entire origin–destination requests or another representation of the walk.
29
+
30
+ These gaps materially affect an implementation. They must be clarified rather than filled in as supposed historical facts. The playable mock in this folder fixes them as **synthetic exercise rules**.
31
+
32
+ ## What this supports practising
33
+
34
+ The useful training target is a workflow: establish when information becomes available, implement and verify the objective, inspect sample structure, build a cheap baseline, compare empirical changes, and adapt a policy to sequential decisions. The source does not establish an official intended algorithm, a globally optimal solution, or how the firm scored the interview.