nexus-coding-agent 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 (131) hide show
  1. nexus/__init__.py +4 -0
  2. nexus/application/__init__.py +2 -0
  3. nexus/application/approval_service.py +130 -0
  4. nexus/application/diff_service.py +177 -0
  5. nexus/application/event_publisher.py +180 -0
  6. nexus/application/execution_context.py +29 -0
  7. nexus/application/execution_ledger.py +126 -0
  8. nexus/application/model_call_context.py +29 -0
  9. nexus/application/observed_model_gateway.py +194 -0
  10. nexus/application/plan_approval_service.py +250 -0
  11. nexus/application/planning.py +668 -0
  12. nexus/application/runtime.py +624 -0
  13. nexus/application/session_service.py +411 -0
  14. nexus/application/structured_output.py +96 -0
  15. nexus/application/telemetry.py +396 -0
  16. nexus/application/telemetry_subscriber.py +216 -0
  17. nexus/application/tool_runtime.py +496 -0
  18. nexus/application/tracing_dispatcher.py +283 -0
  19. nexus/application/validation.py +293 -0
  20. nexus/config/__init__.py +17 -0
  21. nexus/config/loader.py +253 -0
  22. nexus/config/models.py +310 -0
  23. nexus/context/__init__.py +6 -0
  24. nexus/context/budget.py +24 -0
  25. nexus/context/builder.py +92 -0
  26. nexus/context/chunking.py +65 -0
  27. nexus/context/explorer.py +246 -0
  28. nexus/context/integration.py +63 -0
  29. nexus/context/manager.py +328 -0
  30. nexus/context/retrieval.py +261 -0
  31. nexus/domain/__init__.py +2 -0
  32. nexus/domain/agent_decision.py +113 -0
  33. nexus/domain/agent_state.py +49 -0
  34. nexus/domain/approvals.py +64 -0
  35. nexus/domain/context.py +108 -0
  36. nexus/domain/exploration.py +86 -0
  37. nexus/domain/mcp.py +51 -0
  38. nexus/domain/model.py +173 -0
  39. nexus/domain/observability.py +326 -0
  40. nexus/domain/persistence.py +122 -0
  41. nexus/domain/planning.py +329 -0
  42. nexus/domain/ports/__init__.py +61 -0
  43. nexus/domain/ports/agent_decision.py +9 -0
  44. nexus/domain/ports/approval_unit_of_work.py +27 -0
  45. nexus/domain/ports/checkpoint_provider.py +11 -0
  46. nexus/domain/ports/context.py +71 -0
  47. nexus/domain/ports/events.py +21 -0
  48. nexus/domain/ports/graph_runtime.py +30 -0
  49. nexus/domain/ports/mcp.py +23 -0
  50. nexus/domain/ports/model_gateway.py +21 -0
  51. nexus/domain/ports/model_input_budget.py +15 -0
  52. nexus/domain/ports/planning.py +62 -0
  53. nexus/domain/ports/repositories.py +54 -0
  54. nexus/domain/ports/repository_context.py +18 -0
  55. nexus/domain/ports/session_unit_of_work.py +33 -0
  56. nexus/domain/ports/skills.py +41 -0
  57. nexus/domain/ports/tooling.py +39 -0
  58. nexus/domain/ports/tracer.py +13 -0
  59. nexus/domain/ports/validation.py +32 -0
  60. nexus/domain/runtime_events.py +445 -0
  61. nexus/domain/skills.py +129 -0
  62. nexus/domain/tooling.py +163 -0
  63. nexus/domain/validation.py +161 -0
  64. nexus/errors/__init__.py +82 -0
  65. nexus/evaluation/__init__.py +7 -0
  66. nexus/evaluation/assertions.py +4 -0
  67. nexus/evaluation/evaluator.py +263 -0
  68. nexus/evaluation/fixtures.py +36 -0
  69. nexus/evaluation/harness.py +606 -0
  70. nexus/evaluation/loader.py +415 -0
  71. nexus/evaluation/metrics.py +27 -0
  72. nexus/evaluation/models.py +362 -0
  73. nexus/evaluation/report.py +43 -0
  74. nexus/evaluation/runner.py +480 -0
  75. nexus/infrastructure/__init__.py +2 -0
  76. nexus/infrastructure/asyncio_compat.py +13 -0
  77. nexus/infrastructure/bootstrap/__init__.py +17 -0
  78. nexus/infrastructure/bootstrap/composition.py +663 -0
  79. nexus/infrastructure/checkpoint/__init__.py +5 -0
  80. nexus/infrastructure/checkpoint/postgres.py +62 -0
  81. nexus/infrastructure/database.py +39 -0
  82. nexus/infrastructure/embedding.py +61 -0
  83. nexus/infrastructure/graph/__init__.py +6 -0
  84. nexus/infrastructure/graph/day4_runtime.py +966 -0
  85. nexus/infrastructure/graph/langgraph_runtime.py +172 -0
  86. nexus/infrastructure/mcp/__init__.py +5 -0
  87. nexus/infrastructure/mcp/manager.py +380 -0
  88. nexus/infrastructure/model_gateway/__init__.py +6 -0
  89. nexus/infrastructure/model_gateway/openai_compatible.py +345 -0
  90. nexus/infrastructure/observability/__init__.py +7 -0
  91. nexus/infrastructure/observability/console.py +31 -0
  92. nexus/infrastructure/observability/langsmith.py +236 -0
  93. nexus/infrastructure/observability/structured_logger.py +70 -0
  94. nexus/infrastructure/persistence/__init__.py +13 -0
  95. nexus/infrastructure/persistence/approval_unit_of_work.py +74 -0
  96. nexus/infrastructure/persistence/models.py +146 -0
  97. nexus/infrastructure/persistence/repositories.py +326 -0
  98. nexus/infrastructure/persistence/unit_of_work.py +82 -0
  99. nexus/infrastructure/repository_files.py +117 -0
  100. nexus/infrastructure/sandbox/__init__.py +5 -0
  101. nexus/infrastructure/sandbox/local_process.py +353 -0
  102. nexus/infrastructure/semantic.py +276 -0
  103. nexus/infrastructure/semantic_models.py +56 -0
  104. nexus/interfaces/__init__.py +2 -0
  105. nexus/interfaces/cli/__init__.py +2 -0
  106. nexus/interfaces/cli/__main__.py +6 -0
  107. nexus/interfaces/cli/app.py +266 -0
  108. nexus/interfaces/cli/renderer.py +118 -0
  109. nexus/security/__init__.py +14 -0
  110. nexus/security/approval_policies.py +73 -0
  111. nexus/security/command_policy.py +117 -0
  112. nexus/security/executables.py +107 -0
  113. nexus/security/git_commands.py +65 -0
  114. nexus/security/workspace.py +93 -0
  115. nexus/skills/__init__.py +7 -0
  116. nexus/skills/builtin/__init__.py +1 -0
  117. nexus/skills/builtin/debug-python/SKILL.md +26 -0
  118. nexus/skills/builtin/review-repository/SKILL.md +26 -0
  119. nexus/skills/builtin/write-tests/SKILL.md +26 -0
  120. nexus/skills/loader.py +439 -0
  121. nexus/skills/registry.py +146 -0
  122. nexus/skills/selector.py +152 -0
  123. nexus/tools/__init__.py +6 -0
  124. nexus/tools/editing.py +312 -0
  125. nexus/tools/mcp.py +160 -0
  126. nexus/tools/native.py +654 -0
  127. nexus/tools/registry.py +27 -0
  128. nexus_coding_agent-0.1.0.dist-info/METADATA +142 -0
  129. nexus_coding_agent-0.1.0.dist-info/RECORD +131 -0
  130. nexus_coding_agent-0.1.0.dist-info/WHEEL +4 -0
  131. nexus_coding_agent-0.1.0.dist-info/entry_points.txt +2 -0
@@ -0,0 +1,606 @@
1
+ """Sandbox-backed evaluation harness with a closed command grammar."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import hashlib
6
+ import os
7
+ import stat
8
+ import sys
9
+ import unicodedata
10
+ from pathlib import Path, PurePosixPath, PureWindowsPath
11
+
12
+ from nexus.domain.tooling import PolicyDecision, RiskLevel, SandboxRequest
13
+ from nexus.evaluation.fixtures import copy_fixture_source
14
+ from nexus.evaluation.models import (
15
+ EvalDiscoveryResult,
16
+ EvalDiscoveryStatus,
17
+ EvalForbiddenPathEvidence,
18
+ EvalHarnessCommand,
19
+ EvalHarnessCommandResult,
20
+ EvalHarnessOperation,
21
+ EvalPytestNodeOutcome,
22
+ EvalPytestNodeResult,
23
+ EvalRepositoryEvidence,
24
+ EvalValidationResult,
25
+ EvalValidationStatus,
26
+ FileFingerprint,
27
+ ForbiddenPathSnapshot,
28
+ RepositoryState,
29
+ )
30
+ from nexus.infrastructure.sandbox.local_process import LocalProcessSandbox
31
+ from nexus.security.executables import TrustedExecutables
32
+ from nexus.security.workspace import WorkspaceGuard
33
+
34
+ DEFAULT_EVAL_VALIDATION_TIMEOUT_SECONDS = 120
35
+ _SUMMARY_LIMIT = 16_384
36
+ _IGNORED_DIRS = {
37
+ "__pycache__",
38
+ ".pytest_cache",
39
+ ".mypy_cache",
40
+ ".ruff_cache",
41
+ ".nexus-eval-runtime",
42
+ }
43
+ _IGNORED_FILES = {".coverage"}
44
+ _GIT_FORMS = {
45
+ EvalHarnessOperation.GIT_INIT: ("git", "init"),
46
+ EvalHarnessOperation.GIT_ADD_BASELINE: ("git", "add", "--all"),
47
+ EvalHarnessOperation.GIT_COMMIT_BASELINE: (
48
+ "git",
49
+ "-c",
50
+ "user.name=Nexus Eval",
51
+ "-c",
52
+ "user.email=nexus-eval@local.invalid",
53
+ "commit",
54
+ "-m",
55
+ "nexus-eval-baseline",
56
+ ),
57
+ EvalHarnessOperation.GIT_STATUS: (
58
+ "git",
59
+ "status",
60
+ "--porcelain=v1",
61
+ "--untracked-files=all",
62
+ ),
63
+ EvalHarnessOperation.GIT_DIFF: ("git", "diff", "--no-ext-diff", "--binary"),
64
+ EvalHarnessOperation.GIT_LS_FILES: ("git", "ls-files"),
65
+ }
66
+
67
+
68
+ class EvalHarnessCommandPolicy:
69
+ """Allow only the exact v0.8 Harness grammar."""
70
+
71
+ def __init__(self, executables: TrustedExecutables) -> None:
72
+ self._executables = executables
73
+
74
+ def classify(self, *, operation: str, arguments: dict[str, object]) -> RiskLevel:
75
+ raw = arguments.get("argv")
76
+ if (
77
+ not isinstance(raw, (list, tuple))
78
+ or not raw
79
+ or not all(isinstance(item, str) for item in raw)
80
+ ):
81
+ return RiskLevel.DANGEROUS
82
+ argv = tuple(raw)
83
+ try:
84
+ harness_operation = EvalHarnessOperation(operation)
85
+ except ValueError:
86
+ return RiskLevel.DANGEROUS
87
+ family = self._family(argv[0])
88
+ if family is None:
89
+ return RiskLevel.DANGEROUS
90
+ if harness_operation in _GIT_FORMS:
91
+ expected = _GIT_FORMS[harness_operation]
92
+ allowed = family == "git" and argv[1:] == expected[1:]
93
+ elif harness_operation is EvalHarnessOperation.TEST_DISCOVERY:
94
+ allowed = family == "pytest" and self._valid_discovery(argv[1:])
95
+ elif harness_operation is EvalHarnessOperation.VALIDATION:
96
+ allowed = self._valid_validation(family, argv[1:])
97
+ else:
98
+ allowed = False
99
+ return RiskLevel.SAFE if allowed else RiskLevel.DANGEROUS
100
+
101
+ def _family(self, executable: str) -> str | None:
102
+ configured = {
103
+ "git": self._executables.git,
104
+ "pytest": self._executables.pytest,
105
+ "ruff": self._executables.ruff,
106
+ "mypy": self._executables.mypy,
107
+ }
108
+ candidate = Path(executable)
109
+ if _is_absolute_executable(executable):
110
+ normalized = os.path.normcase(str(candidate.resolve(strict=False)))
111
+ for name, trusted in configured.items():
112
+ if trusted and normalized == os.path.normcase(trusted):
113
+ return name
114
+ return None
115
+ if "/" in executable or "\\" in executable:
116
+ return None
117
+ name = candidate.name.casefold().removesuffix(".exe")
118
+ return name if name in configured and configured[name] is not None else None
119
+
120
+ @staticmethod
121
+ def _valid_discovery(args: tuple[str, ...]) -> bool:
122
+ return (
123
+ len(args) >= 3
124
+ and args[:2] == ("--collect-only", "-q")
125
+ and all(_safe_path(item, node=False) for item in args[2:])
126
+ )
127
+
128
+ @staticmethod
129
+ def _valid_validation(family: str, args: tuple[str, ...]) -> bool:
130
+ if family == "ruff":
131
+ return (
132
+ len(args) >= 2
133
+ and args[0] == "check"
134
+ and all(_safe_path(item, node=False) for item in args[1:])
135
+ )
136
+ if family == "mypy":
137
+ return bool(args) and all(_safe_path(item, node=False) for item in args)
138
+ if family != "pytest":
139
+ return False
140
+ targets: list[str] = []
141
+ index = 0
142
+ while index < len(args):
143
+ argument = args[index]
144
+ if argument in {"-q", "-v", "-vv", "--disable-warnings"}:
145
+ index += 1
146
+ elif argument == "--maxfail":
147
+ if index + 1 >= len(args) or not _bounded_int(args[index + 1]):
148
+ return False
149
+ index += 2
150
+ elif argument.startswith("--maxfail="):
151
+ if not _bounded_int(argument.partition("=")[2]):
152
+ return False
153
+ index += 1
154
+ elif argument.startswith("-"):
155
+ return False
156
+ else:
157
+ targets.append(argument)
158
+ index += 1
159
+ return bool(targets) and all(_safe_path(item, node=True) for item in targets)
160
+
161
+
162
+ class EvalHarnessExecutor:
163
+ """Adapter from typed Harness commands to the existing sandbox."""
164
+
165
+ def __init__(self, workspace: Path) -> None:
166
+ self._workspace = workspace.resolve(strict=True)
167
+ self._executables = _resolve_eval_executables(self._workspace)
168
+ policy = EvalHarnessCommandPolicy(self._executables)
169
+ self._workspace_guard = WorkspaceGuard(self._workspace)
170
+ self._sandbox = LocalProcessSandbox(self._workspace_guard, policy, self._executables)
171
+
172
+ async def execute(self, command: EvalHarnessCommand) -> EvalHarnessCommandResult:
173
+ if command.cwd.resolve(strict=True) != self._workspace:
174
+ return _denied(command, "WORKSPACE_PATH_DENIED")
175
+ if not _command_paths_are_contained(command, self._workspace):
176
+ return _denied(command, "WORKSPACE_PATH_DENIED")
177
+ result = await self._sandbox.execute(
178
+ SandboxRequest(
179
+ operation=command.operation.value,
180
+ argv=list(command.argv),
181
+ cwd=".",
182
+ timeout_seconds=command.timeout_seconds,
183
+ )
184
+ )
185
+ allowed = result.policy_decision is PolicyDecision.ALLOWED
186
+ denial_code = None
187
+ infrastructure_error = None
188
+ if not allowed:
189
+ denial_code = result.error.code if result.error else "COMMAND_DENIED"
190
+ elif result.error and result.error.code == "SANDBOX_EXECUTION_ERROR":
191
+ infrastructure_error = result.error.code
192
+ return EvalHarnessCommandResult(
193
+ operation=command.operation,
194
+ allowed=allowed,
195
+ denial_code=denial_code,
196
+ exit_code=result.exit_code,
197
+ stdout_summary=_summary(result.stdout),
198
+ stderr_summary=_summary(result.stderr),
199
+ duration_ms=result.duration_ms,
200
+ timed_out=result.timed_out,
201
+ infrastructure_error=infrastructure_error,
202
+ )
203
+
204
+
205
+ class EvaluationHarness:
206
+ """Own isolated fixture setup, checks, and authoritative snapshots."""
207
+
208
+ @staticmethod
209
+ def copy_fixture(source: Path, destination: Path) -> None:
210
+
211
+ copy_fixture_source(source, destination)
212
+
213
+ @staticmethod
214
+ async def initialize_git(workspace: Path) -> tuple[EvalHarnessCommandResult, ...]:
215
+ executor = EvalHarnessExecutor(workspace)
216
+ results: list[EvalHarnessCommandResult] = []
217
+ for operation in (
218
+ EvalHarnessOperation.GIT_INIT,
219
+ EvalHarnessOperation.GIT_ADD_BASELINE,
220
+ EvalHarnessOperation.GIT_COMMIT_BASELINE,
221
+ ):
222
+ result = await executor.execute(
223
+ EvalHarnessCommand(operation, _GIT_FORMS[operation], workspace, 30)
224
+ )
225
+ results.append(result)
226
+ if (
227
+ not result.allowed
228
+ or result.timed_out
229
+ or result.infrastructure_error
230
+ or result.exit_code != 0
231
+ ):
232
+ break
233
+ return tuple(results)
234
+
235
+ @staticmethod
236
+ async def validation(
237
+ workspace: Path,
238
+ command: tuple[str, ...],
239
+ timeout_seconds: int,
240
+ expected_node_ids: tuple[str, ...] = (),
241
+ ) -> EvalValidationResult:
242
+ if not command:
243
+ return _not_run_validation()
244
+ result = await EvalHarnessExecutor(workspace).execute(
245
+ EvalHarnessCommand(EvalHarnessOperation.VALIDATION, command, workspace, timeout_seconds)
246
+ )
247
+ status = _validation_status(result)
248
+ nodes = _parse_node_results(result.stdout_summary, expected_node_ids)
249
+ return EvalValidationResult(
250
+ status=status,
251
+ command=command,
252
+ exit_code=result.exit_code,
253
+ stdout_summary=result.stdout_summary,
254
+ stderr_summary=result.stderr_summary,
255
+ duration_ms=result.duration_ms,
256
+ timeout_seconds=timeout_seconds,
257
+ infrastructure_error=result.infrastructure_error,
258
+ pytest_node_results=nodes,
259
+ )
260
+
261
+ @staticmethod
262
+ async def discovery(
263
+ workspace: Path, command: tuple[str, ...], timeout_seconds: int
264
+ ) -> EvalDiscoveryResult:
265
+ if not command:
266
+ return _not_run_discovery()
267
+ result = await EvalHarnessExecutor(workspace).execute(
268
+ EvalHarnessCommand(
269
+ EvalHarnessOperation.TEST_DISCOVERY, command, workspace, timeout_seconds
270
+ )
271
+ )
272
+ status = _discovery_status(result)
273
+ collected = (
274
+ _parse_collected_nodes(result.stdout_summary)
275
+ if status is EvalDiscoveryStatus.PASSED
276
+ else ()
277
+ )
278
+ return EvalDiscoveryResult(
279
+ status=status,
280
+ command=command,
281
+ exit_code=result.exit_code,
282
+ collected_node_ids=collected,
283
+ stdout_summary=result.stdout_summary,
284
+ stderr_summary=result.stderr_summary,
285
+ duration_ms=result.duration_ms,
286
+ timeout_seconds=timeout_seconds,
287
+ infrastructure_error=result.infrastructure_error,
288
+ )
289
+
290
+
291
+ def snapshot_repository(root: Path, forbidden_files: tuple[str, ...]) -> RepositoryState:
292
+ root = root.resolve(strict=True)
293
+ files: dict[str, FileFingerprint] = {}
294
+ for current, directory_names, file_names in os.walk(root, followlinks=False):
295
+ current_path = Path(current)
296
+ relative_dir = current_path.relative_to(root)
297
+ directory_names[:] = sorted(
298
+ name for name in directory_names if name != ".git" and name not in _IGNORED_DIRS
299
+ )
300
+ for name in tuple(directory_names):
301
+ path = current_path / name
302
+ relative = (relative_dir / name).as_posix()
303
+ files[relative] = _fingerprint(path, relative)
304
+ if path.is_symlink():
305
+ directory_names.remove(name)
306
+ for name in sorted(file_names):
307
+ if name in _IGNORED_FILES:
308
+ continue
309
+ path = current_path / name
310
+ relative = (relative_dir / name).as_posix()
311
+ files[relative] = _fingerprint(path, relative)
312
+ forbidden = {path: _forbidden_snapshot(root, path) for path in sorted(forbidden_files)}
313
+ return RepositoryState(files=dict(sorted(files.items())), forbidden_snapshots=forbidden)
314
+
315
+
316
+ def compare_repository_states(
317
+ before: RepositoryState,
318
+ after: RepositoryState,
319
+ text_paths: tuple[str, ...],
320
+ root: Path,
321
+ ) -> EvalRepositoryEvidence:
322
+ before_paths = set(before.files)
323
+ after_paths = set(after.files)
324
+ created = tuple(sorted(after_paths - before_paths))
325
+ deleted = tuple(sorted(before_paths - after_paths))
326
+ changed = tuple(
327
+ sorted(
328
+ path for path in before_paths & after_paths if before.files[path] != after.files[path]
329
+ )
330
+ )
331
+ forbidden_evidence = {
332
+ path: EvalForbiddenPathEvidence(
333
+ path=path,
334
+ before=before.forbidden_snapshots[path],
335
+ after=after.forbidden_snapshots[path],
336
+ changed=before.forbidden_snapshots[path] != after.forbidden_snapshots[path],
337
+ )
338
+ for path in sorted(before.forbidden_snapshots)
339
+ }
340
+ for forbidden_path in _workspace_escaping_symlinks(after, root):
341
+ if forbidden_path in forbidden_evidence:
342
+ continue
343
+ before_snapshot = _snapshot_from_fingerprint(
344
+ forbidden_path, before.files.get(forbidden_path)
345
+ )
346
+ after_snapshot = _snapshot_from_fingerprint(forbidden_path, after.files[forbidden_path])
347
+ forbidden_evidence[forbidden_path] = EvalForbiddenPathEvidence(
348
+ path=forbidden_path,
349
+ before=before_snapshot,
350
+ after=after_snapshot,
351
+ changed=before_snapshot != after_snapshot,
352
+ )
353
+ forbidden = tuple(forbidden_evidence[path] for path in sorted(forbidden_evidence))
354
+ texts: dict[str, str] = {}
355
+ for relative in sorted(set(text_paths)):
356
+ path = root / PurePosixPath(relative)
357
+ try:
358
+ if path.is_file() and not path.is_symlink():
359
+ value = path.read_bytes().decode("utf-8", errors="strict")
360
+ texts[relative] = unicodedata.normalize(
361
+ "NFC", value.replace("\r\n", "\n").replace("\r", "\n")
362
+ )
363
+ except (OSError, UnicodeDecodeError):
364
+ continue
365
+ return EvalRepositoryEvidence(changed, created, deleted, forbidden, texts)
366
+
367
+
368
+ def _safe_path(value: str, *, node: bool) -> bool:
369
+ file_part = value.split("::", 1)[0] if node else value
370
+ path = PurePosixPath(file_part)
371
+ if (
372
+ not value
373
+ or not file_part
374
+ or path.is_absolute()
375
+ or PureWindowsPath(file_part).is_absolute()
376
+ or ".." in path.parts
377
+ or _contains_shell_operator(value)
378
+ ):
379
+ return False
380
+ if "\\" in file_part or file_part.startswith("-"):
381
+ return False
382
+ return node or "::" not in value
383
+
384
+
385
+ def _is_absolute_executable(value: str) -> bool:
386
+ return (
387
+ Path(value).is_absolute()
388
+ or PurePosixPath(value).is_absolute()
389
+ or PureWindowsPath(value).is_absolute()
390
+ )
391
+
392
+
393
+ def _contains_shell_operator(value: str) -> bool:
394
+ return (
395
+ any(token in value for token in ("&&", "||", "|", ";", "`", "$("))
396
+ or value.startswith((">", "<", "&"))
397
+ or value.startswith(("1>", "2>"))
398
+ )
399
+
400
+
401
+ def _bounded_int(value: str) -> bool:
402
+ try:
403
+ return 1 <= int(value) <= 10
404
+ except ValueError:
405
+ return False
406
+
407
+
408
+ def _command_paths_are_contained(command: EvalHarnessCommand, root: Path) -> bool:
409
+ for value in _command_path_arguments(command):
410
+ file_part = value.split("::", 1)[0]
411
+ candidate = root / PurePosixPath(file_part)
412
+ if not candidate.exists() and not candidate.is_symlink():
413
+ continue
414
+ try:
415
+ resolved = candidate.resolve(strict=True)
416
+ except (OSError, RuntimeError):
417
+ return False
418
+ if not _is_within_root(resolved, root):
419
+ return False
420
+ return True
421
+
422
+
423
+ def _command_path_arguments(command: EvalHarnessCommand) -> tuple[str, ...]:
424
+ if command.operation is EvalHarnessOperation.TEST_DISCOVERY:
425
+ return command.argv[3:]
426
+ if command.operation is not EvalHarnessOperation.VALIDATION:
427
+ return ()
428
+ family = Path(command.argv[0]).name.casefold().removesuffix(".exe")
429
+ arguments = command.argv[1:]
430
+ if family == "ruff":
431
+ return arguments[1:]
432
+ if family == "mypy":
433
+ return arguments
434
+ if family != "pytest":
435
+ return ()
436
+ paths: list[str] = []
437
+ index = 0
438
+ while index < len(arguments):
439
+ argument = arguments[index]
440
+ if argument == "--maxfail":
441
+ index += 2
442
+ elif argument.startswith("-"):
443
+ index += 1
444
+ else:
445
+ paths.append(argument)
446
+ index += 1
447
+ return tuple(paths)
448
+
449
+
450
+ def _workspace_escaping_symlinks(state: RepositoryState, root: Path) -> tuple[str, ...]:
451
+ escaping: list[str] = []
452
+ for relative, fingerprint in state.files.items():
453
+ if fingerprint.kind != "symlink" or fingerprint.symlink_target is None:
454
+ continue
455
+ link_parent = root / PurePosixPath(relative).parent
456
+ target = Path(fingerprint.symlink_target)
457
+ resolved = (target if target.is_absolute() else link_parent / target).resolve(strict=False)
458
+ if not _is_within_root(resolved, root):
459
+ escaping.append(relative)
460
+ return tuple(sorted(escaping))
461
+
462
+
463
+ def _snapshot_from_fingerprint(
464
+ path: str, fingerprint: FileFingerprint | None
465
+ ) -> ForbiddenPathSnapshot:
466
+ if fingerprint is None:
467
+ return ForbiddenPathSnapshot(path, False, None, None, None, None)
468
+ return ForbiddenPathSnapshot(
469
+ path,
470
+ True,
471
+ fingerprint.kind,
472
+ fingerprint.content_sha256,
473
+ fingerprint.executable,
474
+ fingerprint.symlink_target,
475
+ )
476
+
477
+
478
+ def _is_within_root(path: Path, root: Path) -> bool:
479
+ try:
480
+ return os.path.commonpath(
481
+ [os.path.normcase(str(path)), os.path.normcase(str(root))]
482
+ ) == os.path.normcase(str(root))
483
+ except ValueError:
484
+ return False
485
+
486
+
487
+ def _resolve_eval_executables(workspace: Path) -> TrustedExecutables:
488
+ resolved = TrustedExecutables.resolve(workspace)
489
+ script_directory = Path(sys.executable).resolve(strict=True).parent
490
+
491
+ def adjacent(name: str, current: str | None) -> str | None:
492
+ if current is not None:
493
+ return current
494
+ candidates = (
495
+ script_directory / f"{name}.exe",
496
+ script_directory / name,
497
+ )
498
+ for candidate in candidates:
499
+ if candidate.is_file():
500
+ return str(candidate.resolve(strict=True))
501
+ return None
502
+
503
+ return TrustedExecutables(
504
+ python=resolved.python,
505
+ uv=resolved.uv,
506
+ git=resolved.git,
507
+ pytest=adjacent("pytest", resolved.pytest),
508
+ ruff=adjacent("ruff", resolved.ruff),
509
+ mypy=adjacent("mypy", resolved.mypy),
510
+ )
511
+
512
+
513
+ def _fingerprint(path: Path, relative: str) -> FileFingerprint:
514
+ mode = path.lstat().st_mode
515
+ if path.is_symlink():
516
+ return FileFingerprint(relative, "symlink", None, False, os.readlink(path))
517
+ if stat.S_ISREG(mode):
518
+ digest = hashlib.sha256(path.read_bytes()).hexdigest()
519
+ return FileFingerprint(relative, "file", digest, bool(mode & stat.S_IXUSR), None)
520
+ if stat.S_ISDIR(mode):
521
+ return FileFingerprint(relative, "directory", None, bool(mode & stat.S_IXUSR), None)
522
+ return FileFingerprint(relative, "other", None, bool(mode & stat.S_IXUSR), None)
523
+
524
+
525
+ def _forbidden_snapshot(root: Path, relative: str) -> ForbiddenPathSnapshot:
526
+ path = root / PurePosixPath(relative)
527
+ if not path.exists() and not path.is_symlink():
528
+ return ForbiddenPathSnapshot(relative, False, None, None, None, None)
529
+ fingerprint = _fingerprint(path, relative)
530
+ return ForbiddenPathSnapshot(
531
+ relative,
532
+ True,
533
+ fingerprint.kind,
534
+ fingerprint.content_sha256,
535
+ fingerprint.executable,
536
+ fingerprint.symlink_target,
537
+ )
538
+
539
+
540
+ def _summary(value: str) -> str:
541
+ return value if len(value) <= _SUMMARY_LIMIT else value[:_SUMMARY_LIMIT]
542
+
543
+
544
+ def _denied(command: EvalHarnessCommand, code: str) -> EvalHarnessCommandResult:
545
+ return EvalHarnessCommandResult(command.operation, False, code, None, "", "", 0, False, None)
546
+
547
+
548
+ def _validation_status(result: EvalHarnessCommandResult) -> EvalValidationStatus:
549
+ if result.infrastructure_error:
550
+ return EvalValidationStatus.INFRASTRUCTURE_ERROR
551
+ if not result.allowed:
552
+ return EvalValidationStatus.FAILED
553
+ if result.timed_out:
554
+ return EvalValidationStatus.TIMED_OUT
555
+ return EvalValidationStatus.PASSED if result.exit_code == 0 else EvalValidationStatus.FAILED
556
+
557
+
558
+ def _discovery_status(result: EvalHarnessCommandResult) -> EvalDiscoveryStatus:
559
+ if result.infrastructure_error:
560
+ return EvalDiscoveryStatus.INFRASTRUCTURE_ERROR
561
+ if not result.allowed:
562
+ return EvalDiscoveryStatus.FAILED
563
+ if result.timed_out:
564
+ return EvalDiscoveryStatus.TIMED_OUT
565
+ return EvalDiscoveryStatus.PASSED if result.exit_code == 0 else EvalDiscoveryStatus.FAILED
566
+
567
+
568
+ def _not_run_validation() -> EvalValidationResult:
569
+ return EvalValidationResult(EvalValidationStatus.NOT_RUN, (), None, "", "", 0, None, None)
570
+
571
+
572
+ def _not_run_discovery() -> EvalDiscoveryResult:
573
+ return EvalDiscoveryResult(EvalDiscoveryStatus.NOT_RUN, (), None, (), "", "", 0, None, None)
574
+
575
+
576
+ def _parse_collected_nodes(output: str) -> tuple[str, ...]:
577
+ nodes = {
578
+ line.strip()
579
+ for line in output.splitlines()
580
+ if "::" in line and not line.lstrip().startswith(("<", "="))
581
+ }
582
+ return tuple(sorted(nodes))
583
+
584
+
585
+ def _parse_node_results(output: str, expected: tuple[str, ...]) -> tuple[EvalPytestNodeResult, ...]:
586
+ tokens = {
587
+ "PASSED": EvalPytestNodeOutcome.PASSED,
588
+ "FAILED": EvalPytestNodeOutcome.FAILED,
589
+ "SKIPPED": EvalPytestNodeOutcome.SKIPPED,
590
+ "XFAIL": EvalPytestNodeOutcome.XFAILED,
591
+ "XPASS": EvalPytestNodeOutcome.XPASSED,
592
+ "ERROR": EvalPytestNodeOutcome.ERROR,
593
+ }
594
+ found: dict[str, EvalPytestNodeOutcome] = {}
595
+ for line in output.splitlines():
596
+ normalized = line.strip()
597
+ for node in expected:
598
+ prefix = f"{node} "
599
+ if normalized.startswith(prefix):
600
+ token = normalized[len(prefix) :].split(maxsplit=1)[0]
601
+ if token in tokens:
602
+ found[node] = tokens[token]
603
+ return tuple(
604
+ EvalPytestNodeResult(node, found.get(node, EvalPytestNodeOutcome.NOT_RUN))
605
+ for node in expected
606
+ )