@homericintelligence/athena-opencode 0.5.0 → 0.5.1

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 (136) hide show
  1. package/README.md +30 -17
  2. package/package.json +2 -2
  3. package/plugin.js +20 -5
  4. package/skills/TECHNICAL_ENGLISH.md +78 -0
  5. package/skills/_cli.py +26 -18
  6. package/skills/_support/docs/dependency-resolution.md +192 -0
  7. package/skills/_support/docs/policies/development.md +117 -0
  8. package/skills/_support/docs/policies/evidence-integrity.md +40 -0
  9. package/skills/_support/docs/principles/README.md +542 -0
  10. package/skills/_support/docs/principles/details/p001-kiss.md +114 -0
  11. package/skills/_support/docs/principles/details/p002-yagni.md +109 -0
  12. package/skills/_support/docs/principles/details/p003-dry.md +126 -0
  13. package/skills/_support/docs/principles/details/p004-solid.md +165 -0
  14. package/skills/_support/docs/principles/details/p005-modularity.md +123 -0
  15. package/skills/_support/docs/principles/details/p006-principle-of-least-astonishment.md +115 -0
  16. package/skills/_support/docs/principles/details/p007-subtraction-over-addition.md +113 -0
  17. package/skills/_support/docs/principles/details/p008-understand-before-subtracting.md +115 -0
  18. package/skills/_support/docs/principles/details/p009-general-mechanisms-over-special-cases.md +108 -0
  19. package/skills/_support/docs/principles/details/p010-scope-fidelity.md +117 -0
  20. package/skills/_support/docs/principles/details/p011-minimal-coherent-change.md +128 -0
  21. package/skills/_support/docs/principles/details/p012-evidence-before-modification.md +115 -0
  22. package/skills/_support/docs/principles/details/p013-avoid-hasty-abstractions.md +112 -0
  23. package/skills/_support/docs/principles/details/p014-preserve-unrequested-behavior.md +110 -0
  24. package/skills/_support/docs/principles/details/p015-architecture-conformance.md +124 -0
  25. package/skills/_support/docs/principles/details/p016-separation-of-concerns.md +125 -0
  26. package/skills/_support/docs/principles/details/p017-high-cohesion-low-coupling.md +130 -0
  27. package/skills/_support/docs/principles/details/p018-information-hiding.md +128 -0
  28. package/skills/_support/docs/principles/details/p019-explicit-contracts.md +134 -0
  29. package/skills/_support/docs/principles/details/p020-executable-architecture.md +130 -0
  30. package/skills/_support/docs/principles/details/p021-evolutionary-and-reversible-design.md +126 -0
  31. package/skills/_support/docs/principles/details/p022-test-behavior-not-implementation.md +129 -0
  32. package/skills/_support/docs/principles/details/p023-parameterized-table-driven-testing.md +129 -0
  33. package/skills/_support/docs/principles/details/p024-boundary-value-testing.md +128 -0
  34. package/skills/_support/docs/principles/details/p025-property-based-testing-for-invariants.md +133 -0
  35. package/skills/_support/docs/principles/details/p026-regression-before-repair.md +134 -0
  36. package/skills/_support/docs/principles/details/p027-deterministic-and-hermetic-tests.md +128 -0
  37. package/skills/_support/docs/principles/details/p028-test-failure-paths.md +133 -0
  38. package/skills/_support/docs/principles/details/p029-generalize-error-policy-preserve-specific-cause.md +131 -0
  39. package/skills/_support/docs/principles/details/p030-nearest-responsible-error-boundary.md +133 -0
  40. package/skills/_support/docs/principles/details/p031-propagate-rather-than-swallow.md +131 -0
  41. package/skills/_support/docs/principles/details/p032-handle-once-preserve-causality.md +137 -0
  42. package/skills/_support/docs/principles/details/p033-state-safe-failure-semantics.md +140 -0
  43. package/skills/_support/docs/principles/details/p034-fail-fast.md +132 -0
  44. package/skills/_support/docs/principles/details/p035-fail-secure-fail-closed.md +137 -0
  45. package/skills/_support/docs/principles/details/p036-graceful-degradation.md +137 -0
  46. package/skills/_support/docs/principles/details/p037-idempotency-before-retry.md +139 -0
  47. package/skills/_support/docs/principles/details/p038-bounded-retry.md +145 -0
  48. package/skills/_support/docs/principles/details/p039-bounded-waiting.md +151 -0
  49. package/skills/_support/docs/principles/details/p040-bounded-resources.md +138 -0
  50. package/skills/_support/docs/principles/details/p041-backpressure-and-load-shedding.md +141 -0
  51. package/skills/_support/docs/principles/details/p042-fault-isolation-bulkheads.md +140 -0
  52. package/skills/_support/docs/principles/details/p043-circuit-breakers.md +148 -0
  53. package/skills/_support/docs/principles/details/p044-atomicity-where-possible.md +141 -0
  54. package/skills/_support/docs/principles/details/p045-compensation-where-atomicity-is-impossible.md +163 -0
  55. package/skills/_support/docs/principles/details/p046-resumability.md +138 -0
  56. package/skills/_support/docs/principles/details/p047-observability-is-part-of-correctness.md +134 -0
  57. package/skills/_support/docs/principles/details/p048-secure-by-design.md +132 -0
  58. package/skills/_support/docs/principles/details/p049-secure-by-default.md +126 -0
  59. package/skills/_support/docs/principles/details/p050-least-privilege.md +123 -0
  60. package/skills/_support/docs/principles/details/p051-complete-mediation.md +124 -0
  61. package/skills/_support/docs/principles/details/p052-separation-of-duties.md +129 -0
  62. package/skills/_support/docs/principles/details/p053-validate-at-trust-boundaries.md +126 -0
  63. package/skills/_support/docs/principles/details/p054-defense-in-depth.md +135 -0
  64. package/skills/_support/docs/principles/details/p055-minimize-attack-surface.md +133 -0
  65. package/skills/_support/docs/principles/details/p056-secrets-stay-out-of-code-and-context.md +137 -0
  66. package/skills/_support/docs/principles/details/p057-supply-chain-integrity.md +135 -0
  67. package/skills/_support/docs/principles/details/p058-bounded-agent-authority.md +137 -0
  68. package/skills/_support/docs/principles/details/p059-data-is-not-instruction.md +130 -0
  69. package/skills/_support/docs/principles/details/p060-constrain-sub-agents.md +136 -0
  70. package/skills/_support/docs/principles/details/p061-separate-decision-from-high-impact-execution.md +131 -0
  71. package/skills/_support/docs/principles/details/p062-human-approval-for-irreversible-or-high-risk-actions.md +129 -0
  72. package/skills/_support/docs/principles/details/p063-requirement-to-code-traceability.md +116 -0
  73. package/skills/_support/docs/principles/details/p064-requirement-to-test-traceability.md +116 -0
  74. package/skills/_support/docs/principles/details/p065-verify-before-claiming-completion.md +129 -0
  75. package/skills/_support/docs/principles/details/p066-preserve-existing-work.md +114 -0
  76. package/skills/_support/docs/principles/details/p067-no-test-cheating.md +126 -0
  77. package/skills/_support/docs/principles/details/p068-no-validation-bypass.md +119 -0
  78. package/skills/_support/docs/principles/details/p069-independent-review-for-high-risk-changes.md +129 -0
  79. package/skills/_support/docs/principles/details/p070-code-health-must-not-regress.md +116 -0
  80. package/skills/_support/docs/principles/details/p071-consistency-over-personal-preference.md +117 -0
  81. package/skills/_support/docs/principles/details/p072-technical-evidence-over-preference.md +122 -0
  82. package/skills/_support/docs/principles/details/p073-optimize-only-with-evidence.md +123 -0
  83. package/skills/_support/docs/principles/details/p074-prefer-existing-mechanisms.md +121 -0
  84. package/skills/_support/docs/principles/details/p075-make-invalid-states-hard-to-represent.md +128 -0
  85. package/skills/_support/docs/principles/details/p076-parse-then-validate-then-operate.md +133 -0
  86. package/skills/_support/docs/principles/details/p077-separate-policy-from-mechanism.md +112 -0
  87. package/skills/_support/docs/principles/details/p078-single-source-of-truth.md +131 -0
  88. package/skills/_support/docs/principles/details/p079-explicit-ownership-and-lifetimes.md +124 -0
  89. package/skills/_support/docs/principles/details/p080-make-concurrency-deliberate.md +122 -0
  90. package/skills/_support/docs/principles/details/p081-forward-progress-with-safety.md +125 -0
  91. package/skills/_support/docs/principles/details/p082-design-for-cancellation.md +133 -0
  92. package/skills/_support/docs/principles/details/p083-irreversible-actions-last.md +124 -0
  93. package/skills/_support/docs/principles/details/p084-prefer-local-reasoning.md +131 -0
  94. package/skills/_support/docs/principles/details/p085-explicit-is-better-than-implicit.md +123 -0
  95. package/skills/_support/docs/principles/details/p086-readability-counts.md +113 -0
  96. package/skills/_support/docs/principles/details/p087-comments-explain-why-code-explains-what.md +120 -0
  97. package/skills/_support/docs/principles/details/p088-delete-dead-code.md +125 -0
  98. package/skills/_support/docs/principles/details/p089-delete-obsolete-configuration-and-dependencies.md +125 -0
  99. package/skills/_support/docs/principles/details/p090-prefer-negative-code.md +134 -0
  100. package/skills/_support/docs/principles/details/p091-test-driven-development.md +149 -0
  101. package/skills/_support/docs/review/README.md +59 -0
  102. package/skills/_support/docs/review/behavior-first-testing.md +73 -0
  103. package/skills/_support/docs/review/common.md +365 -0
  104. package/skills/_support/docs/review/design-docs.md +74 -0
  105. package/skills/_support/docs/review/issue-planning.md +220 -0
  106. package/skills/_support/docs/review/language-routing.md +141 -0
  107. package/skills/_support/docs/review/repository-scorecard.md +170 -0
  108. package/skills/advise/SKILL.md +93 -67
  109. package/skills/advise/scripts/list_retrievable_skills.py +3 -3
  110. package/skills/brainstorm/SKILL.md +160 -127
  111. package/skills/change-review/SKILL.md +91 -71
  112. package/skills/change-review/references/scope-resolution.md +69 -46
  113. package/skills/change-review/scripts/resolve_scope.py +119 -73
  114. package/skills/finalize-plan/SKILL.md +198 -139
  115. package/skills/git-worktrees/SKILL.md +100 -80
  116. package/skills/git-worktrees/scripts/prepare_worktree.py +16 -10
  117. package/skills/issue-review/SKILL.md +136 -79
  118. package/skills/learn/SKILL.md +276 -186
  119. package/skills/myrmidon-swarm/SKILL.md +126 -86
  120. package/skills/plan-issue/SKILL.md +122 -81
  121. package/skills/pr-review/SKILL.md +164 -114
  122. package/skills/pr-review/references/criteria.md +58 -37
  123. package/skills/pr-review/references/delivery.md +156 -98
  124. package/skills/pr-review/references/evidence.md +265 -148
  125. package/skills/pr-review/references/prevalidated.md +93 -61
  126. package/skills/pr-review/scripts/collect_evidence.py +211 -140
  127. package/skills/pr-review/scripts/diff_context.py +6 -4
  128. package/skills/pr-review/scripts/materialize_snapshot.py +112 -120
  129. package/skills/pr-review/scripts/pr_identity.py +27 -15
  130. package/skills/pr-review/scripts/resolve_pr.py +53 -31
  131. package/skills/repo-review/SKILL.md +202 -131
  132. package/skills/systematic-debugging/SKILL.md +210 -192
  133. package/skills/systematic-debugging/scripts/repository_evidence.py +9 -4
  134. package/skills/test-driven-development/SKILL.md +126 -96
  135. package/skills/tidy/SKILL.md +92 -59
  136. package/skills/tidy/scripts/run_tidy.py +3 -3
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env python3
2
- """Resolve a change-review scope without changing repository or Git state."""
2
+ """Resolve the scope for a change review. Do not change the repository or Git state."""
3
3
 
4
4
  from __future__ import annotations
5
5
 
@@ -34,7 +34,7 @@ MAX_WORKTREE_CANDIDATES = 50_000
34
34
 
35
35
 
36
36
  def git_bytes(*arguments: str, repository_root: Path | None = None) -> bytes:
37
- """Run Git and return its raw stdout or raise a concise error."""
37
+ """Run Git and return its raw standard output or raise a concise error."""
38
38
  command = git_command(arguments, repository_root)
39
39
  result = run_command(
40
40
  command,
@@ -47,7 +47,9 @@ def git_bytes(*arguments: str, repository_root: Path | None = None) -> bytes:
47
47
  stderr = cast(bytes, result.stderr)
48
48
  if result.returncode != 0:
49
49
  message = stderr.decode("utf-8", errors="replace").strip()
50
- raise RuntimeError(message or f"git {' '.join(arguments)} failed")
50
+ raise RuntimeError(
51
+ message or f"The Git command failed. Command: git {' '.join(arguments)}"
52
+ )
51
53
  return stdout
52
54
 
53
55
 
@@ -60,13 +62,13 @@ def git_text(*arguments: str, repository_root: Path | None = None) -> str:
60
62
 
61
63
 
62
64
  def path_list(document: bytes) -> list[str]:
63
- """Return sorted Git NUL-delimited paths without lossy shell parsing."""
65
+ """Return sorted Git paths from null-byte-delimited data without lossy parsing."""
64
66
  return sorted(os.fsdecode(path) for path in document.split(b"\0") if path)
65
67
 
66
68
 
67
69
  @dataclass(frozen=True)
68
70
  class PathEntry:
69
- """One no-follow worktree or immutable Git-object manifest entry."""
71
+ """This record describes one worktree or immutable Git-object manifest entry."""
70
72
 
71
73
  path: str
72
74
  kind: str
@@ -77,7 +79,7 @@ class PathEntry:
77
79
 
78
80
  @dataclass(frozen=True)
79
81
  class ContentFingerprint:
80
- """Bounded identity for an arbitrarily large byte stream."""
82
+ """This record contains a bounded identity for a byte stream of any size."""
81
83
 
82
84
  length: int
83
85
  digest: str
@@ -85,7 +87,7 @@ class ContentFingerprint:
85
87
 
86
88
  @dataclass(frozen=True)
87
89
  class ScopeCapture:
88
- """One complete observed scope capture used to detect worktree races."""
90
+ """This record contains one scope observation that detects worktree races."""
89
91
 
90
92
  paths: tuple[str, ...]
91
93
  tracked_paths: tuple[str, ...]
@@ -135,12 +137,15 @@ def git_stream_fingerprint(
135
137
  )
136
138
  except FileNotFoundError as error:
137
139
  raise RuntimeError(
138
- f"required command unavailable: {error.filename or command[0]}"
140
+ "The required command is not available: "
141
+ f"'{error.filename or command[0]}'."
139
142
  ) from error
140
143
  try:
141
144
  stdout = process.stdout
142
145
  if stdout is None:
143
- raise RuntimeError("Git did not provide stdout for scope capture")
146
+ raise RuntimeError(
147
+ "Git did not provide standard output for scope capture."
148
+ )
144
149
  try:
145
150
  fingerprint = content_fingerprint(
146
151
  iter(lambda: stdout.read(READ_CHUNK_SIZE), b"")
@@ -160,10 +165,16 @@ def git_stream_fingerprint(
160
165
  .decode("utf-8", errors="replace")
161
166
  .strip()
162
167
  )
163
- raise RuntimeError(message or f"git {' '.join(arguments)} failed")
168
+ raise RuntimeError(
169
+ message
170
+ or f"The Git command failed. Command: git {' '.join(arguments)}"
171
+ )
164
172
  return fingerprint
165
173
  except OSError as error:
166
- raise RuntimeError(f"cannot stream git output: {error}") from error
174
+ raise RuntimeError(
175
+ "The tool cannot stream Git output. "
176
+ f"The operation returned this diagnostic.\n{error}"
177
+ ) from error
167
178
 
168
179
 
169
180
  def consume_git_nul_records(
@@ -171,7 +182,7 @@ def consume_git_nul_records(
171
182
  repository_root: Path,
172
183
  consume: Callable[[bytes], None],
173
184
  ) -> None:
174
- """Pass Git NUL records to a consumer without buffering command output."""
185
+ """Pass null-byte-delimited Git records without buffering command output."""
175
186
  command = git_command(arguments, repository_root)
176
187
  try:
177
188
  with tempfile.TemporaryFile() as error_output:
@@ -184,13 +195,16 @@ def consume_git_nul_records(
184
195
  )
185
196
  except FileNotFoundError as error:
186
197
  raise RuntimeError(
187
- f"required command unavailable: {error.filename or command[0]}"
198
+ "The required command is not available: "
199
+ f"'{error.filename or command[0]}'."
188
200
  ) from error
189
201
  stdout = process.stdout
190
202
  if stdout is None:
191
203
  process.kill()
192
204
  process.wait()
193
- raise RuntimeError("Git did not provide stdout for scope capture")
205
+ raise RuntimeError(
206
+ "Git did not provide standard output for scope capture."
207
+ )
194
208
  pending = b""
195
209
  try:
196
210
  while chunk := stdout.read(READ_CHUNK_SIZE):
@@ -199,17 +213,18 @@ def consume_git_nul_records(
199
213
  pending = records.pop()
200
214
  if len(pending) > MAX_METADATA_RECORD_BYTES:
201
215
  raise RuntimeError(
202
- "Git metadata record exceeds the safe scope limit"
216
+ "The Git metadata record is larger than the safe scope limit."
203
217
  )
204
218
  for record in records:
205
219
  if record:
206
220
  if len(record) > MAX_METADATA_RECORD_BYTES:
207
221
  raise RuntimeError(
208
- "Git metadata record exceeds the safe scope limit"
222
+ "The Git metadata record is larger than the safe scope "
223
+ "limit."
209
224
  )
210
225
  consume(record)
211
226
  if pending:
212
- raise RuntimeError("unterminated Git metadata record")
227
+ raise RuntimeError("The Git metadata record is not terminated.")
213
228
  return_code = process.wait()
214
229
  except BaseException:
215
230
  stdout.close()
@@ -225,9 +240,15 @@ def consume_git_nul_records(
225
240
  .decode("utf-8", errors="replace")
226
241
  .strip()
227
242
  )
228
- raise RuntimeError(message or f"git {' '.join(arguments)} failed")
243
+ raise RuntimeError(
244
+ message
245
+ or f"The Git command failed. Command: git {' '.join(arguments)}"
246
+ )
229
247
  except OSError as error:
230
- raise RuntimeError(f"cannot stream git metadata: {error}") from error
248
+ raise RuntimeError(
249
+ "The tool cannot stream Git metadata. "
250
+ f"The operation returned this diagnostic.\n{error}"
251
+ ) from error
231
252
 
232
253
 
233
254
  def pathspec_arguments(arguments: list[str], paths: Sequence[str]) -> list[str]:
@@ -237,7 +258,7 @@ def pathspec_arguments(arguments: list[str], paths: Sequence[str]) -> list[str]:
237
258
 
238
259
 
239
260
  def normalized_paths(repository_root: Path, paths: Sequence[str]) -> list[str]:
240
- """Keep lexical filters inside the repository without following symlinks."""
261
+ """Keep lexical filters inside the repository without following symbolic links."""
241
262
  root = Path(os.path.abspath(os.fspath(repository_root)))
242
263
  normalized: list[str] = []
243
264
  for raw_path in paths:
@@ -249,15 +270,17 @@ def normalized_paths(repository_root: Path, paths: Sequence[str]) -> list[str]:
249
270
  try:
250
271
  relative = resolved.relative_to(root)
251
272
  except ValueError as error:
252
- raise RuntimeError(f"path outside repository: {raw_path!r}") from error
273
+ raise RuntimeError(
274
+ f"The path is outside the repository: {raw_path!r}."
275
+ ) from error
253
276
  normalized.append(relative.as_posix())
254
277
  return sorted(set(normalized))
255
278
 
256
279
 
257
280
  def verified_commit(reference: str, repository_root: Path | None = None) -> str:
258
- """Resolve one non-option Git reference to an immutable commit OID."""
281
+ """Resolve one non-option Git reference to an immutable commit object identifier."""
259
282
  if not reference or reference.startswith("-"):
260
- raise RuntimeError(f"invalid Git reference: {reference!r}")
283
+ raise RuntimeError(f"The Git reference is not valid: {reference!r}.")
261
284
  return git_text(
262
285
  "rev-parse",
263
286
  "--verify",
@@ -267,9 +290,9 @@ def verified_commit(reference: str, repository_root: Path | None = None) -> str:
267
290
 
268
291
 
269
292
  def range_revisions(value: str, repository_root: Path) -> tuple[str, str]:
270
- """Resolve the required BASE..HEAD notation to immutable commit OIDs."""
293
+ """Resolve the required BASE..HEAD notation to immutable commit object identifiers."""
271
294
  if value.count("..") != 1:
272
- raise RuntimeError("range must use exactly one BASE..HEAD separator")
295
+ raise RuntimeError("The range must contain exactly one BASE..HEAD separator.")
273
296
  base_reference, head_reference = value.split("..", maxsplit=1)
274
297
  return (
275
298
  verified_commit(base_reference, repository_root),
@@ -368,8 +391,8 @@ def untracked_paths(paths: Sequence[str], repository_root: Path) -> list[str]:
368
391
  def consume(record: bytes) -> None:
369
392
  if len(selected) >= MAX_WORKTREE_CANDIDATES:
370
393
  raise RuntimeError(
371
- "untracked path limit "
372
- f"({MAX_WORKTREE_CANDIDATES}) reached; rerun with narrower PATH arguments"
394
+ f"The untracked path limit ({MAX_WORKTREE_CANDIDATES}) was reached. "
395
+ "Run the command again with narrower PATH arguments."
373
396
  )
374
397
  selected.append(os.fsdecode(record))
375
398
 
@@ -382,7 +405,7 @@ def untracked_paths(paths: Sequence[str], repository_root: Path) -> list[str]:
382
405
 
383
406
 
384
407
  class Digest(Protocol):
385
- """Minimal hashlib protocol used by the canonical scope digest."""
408
+ """This protocol defines the minimum hashlib operations for the scope digest."""
386
409
 
387
410
  def update(self, data: bytes) -> None:
388
411
  """Add bytes to the digest state."""
@@ -405,7 +428,7 @@ def path_components(relative_path: str) -> tuple[str, ...]:
405
428
  """Return a verified repository-relative path split into lexical components."""
406
429
  components = Path(relative_path).parts
407
430
  if not components or any(component in {".", ".."} for component in components):
408
- raise RuntimeError(f"invalid repository path: {relative_path!r}")
431
+ raise RuntimeError(f"The repository path is not valid: {relative_path!r}.")
409
432
  return components
410
433
 
411
434
 
@@ -421,10 +444,10 @@ def close_descriptor_quietly(descriptor: int) -> None:
421
444
  def nofollow_parent_descriptor(
422
445
  repository_root: Path, relative_path: str
423
446
  ) -> tuple[int, str]:
424
- """Open a path's parent without following any repository symlink."""
447
+ """Open a path's parent without following any repository symbolic link."""
425
448
  if not hasattr(os, "O_NOFOLLOW") or not hasattr(os, "O_DIRECTORY"):
426
449
  raise RuntimeError(
427
- "host cannot inspect repository paths without following links"
450
+ "The host cannot inspect repository paths without following symbolic links."
428
451
  )
429
452
  components = path_components(relative_path)
430
453
  descriptor: int | None = os.open(
@@ -442,13 +465,14 @@ def nofollow_parent_descriptor(
442
465
  )
443
466
  except (NotImplementedError, TypeError) as error:
444
467
  raise RuntimeError(
445
- "host cannot inspect repository paths without following links"
468
+ "The host cannot inspect repository paths without following symbolic "
469
+ "links."
446
470
  ) from error
447
471
  try:
448
472
  os.close(descriptor)
449
473
  except OSError:
450
- # `close()` leaves descriptor state unspecified on error; do not
451
- # retry the parent descriptor, but never leak the opened child.
474
+ # After an error, `close()` leaves the descriptor state unspecified.
475
+ # Do not try the parent descriptor again. Always close the opened child.
452
476
  descriptor = None
453
477
  close_descriptor_quietly(child_descriptor)
454
478
  raise
@@ -462,7 +486,7 @@ def nofollow_parent_descriptor(
462
486
 
463
487
 
464
488
  def worktree_path_entry(repository_root: Path, relative_path: str) -> PathEntry:
465
- """Describe a path without following repository or target symlinks."""
489
+ """Describe a path without following repository or target symbolic links."""
466
490
  try:
467
491
  parent_descriptor, filename = nofollow_parent_descriptor(
468
492
  repository_root, relative_path
@@ -474,7 +498,7 @@ def worktree_path_entry(repository_root: Path, relative_path: str) -> PathEntry:
474
498
  mode = os.lstat(filename, dir_fd=parent_descriptor).st_mode
475
499
  except (NotImplementedError, TypeError) as error:
476
500
  raise RuntimeError(
477
- "host cannot inspect repository paths without following links"
501
+ "The host cannot inspect repository paths without following symbolic links."
478
502
  ) from error
479
503
  except FileNotFoundError:
480
504
  return PathEntry(relative_path, "absent")
@@ -483,7 +507,8 @@ def worktree_path_entry(repository_root: Path, relative_path: str) -> PathEntry:
483
507
  target = os.readlink(filename, dir_fd=parent_descriptor)
484
508
  except (NotImplementedError, TypeError) as error:
485
509
  raise RuntimeError(
486
- "host cannot inspect repository links without following them"
510
+ "The host cannot inspect repository symbolic links without following "
511
+ "them."
487
512
  ) from error
488
513
  return PathEntry(
489
514
  relative_path,
@@ -504,7 +529,7 @@ def worktree_path_entry(repository_root: Path, relative_path: str) -> PathEntry:
504
529
  def worktree_path_entries(
505
530
  repository_root: Path, paths: Sequence[str]
506
531
  ) -> tuple[PathEntry, ...]:
507
- """Describe every selected repository object without dereferencing links."""
532
+ """Describe each selected repository object without following symbolic links."""
508
533
  return tuple(worktree_path_entry(repository_root, path) for path in paths)
509
534
 
510
535
 
@@ -520,7 +545,7 @@ def git_object_kind(mode: str, object_type: str) -> str:
520
545
 
521
546
 
522
547
  def nul_records(document: bytes) -> list[bytes]:
523
- """Split a Git NUL-delimited record stream while preserving path bytes."""
548
+ """Split null-byte-delimited Git records and preserve the path bytes."""
524
549
  return [record for record in document.split(b"\0") if record]
525
550
 
526
551
 
@@ -540,12 +565,14 @@ def index_entry_map(
540
565
  raw_mode, raw_object_id, raw_stage = header.split()
541
566
  except ValueError as error:
542
567
  raise RuntimeError(
543
- "invalid Git index entry while resolving scope"
568
+ "The Git index entry is not valid for scope resolution."
544
569
  ) from error
545
570
  path = os.fsdecode(raw_path)
546
571
  stage = raw_stage.decode("ascii")
547
572
  if stage != "0":
548
- raise RuntimeError(f"unmerged index entry in selected scope: {path}")
573
+ raise RuntimeError(
574
+ f"The selected scope contains an unmerged index entry: '{path}'."
575
+ )
549
576
  mode = raw_mode.decode("ascii")
550
577
  object_id = raw_object_id.decode("ascii")
551
578
  entries[path] = PathEntry(
@@ -583,7 +610,7 @@ def head_tree_entry_map(
583
610
  raw_mode, raw_type, raw_object_id = header.split()
584
611
  except ValueError as error:
585
612
  raise RuntimeError(
586
- "invalid Git tree entry while resolving scope"
613
+ "The Git tree entry is not valid for scope resolution."
587
614
  ) from error
588
615
  path = os.fsdecode(raw_path)
589
616
  mode = raw_mode.decode("ascii")
@@ -609,7 +636,7 @@ def head_tree_path_entries(
609
636
 
610
637
  @dataclass(frozen=True)
611
638
  class WorktreeMetadata:
612
- """Bounded immutable metadata needed to compare raw worktree candidates."""
639
+ """This record contains bounded metadata for comparison of worktree candidates."""
613
640
 
614
641
  head_entries: dict[str, PathEntry]
615
642
  index_entries: dict[str, PathEntry]
@@ -623,8 +650,8 @@ def add_worktree_candidate(candidates: set[str], path: str) -> None:
623
650
  return
624
651
  if len(candidates) >= MAX_WORKTREE_CANDIDATES:
625
652
  raise RuntimeError(
626
- "worktree candidate limit "
627
- f"({MAX_WORKTREE_CANDIDATES}) reached; rerun with narrower PATH arguments"
653
+ f"The worktree candidate limit ({MAX_WORKTREE_CANDIDATES}) was reached. "
654
+ "Run the command again with narrower PATH arguments."
628
655
  )
629
656
  candidates.add(path)
630
657
 
@@ -635,7 +662,9 @@ def parse_head_tree_record(record: bytes) -> PathEntry:
635
662
  header, raw_path = record.split(b"\t", maxsplit=1)
636
663
  raw_mode, raw_type, raw_object_id = header.split()
637
664
  except ValueError as error:
638
- raise RuntimeError("invalid Git tree entry while resolving scope") from error
665
+ raise RuntimeError(
666
+ "The Git tree entry is not valid for scope resolution."
667
+ ) from error
639
668
  path = os.fsdecode(raw_path)
640
669
  mode = raw_mode.decode("ascii")
641
670
  object_type = raw_type.decode("ascii")
@@ -654,11 +683,15 @@ def parse_tagged_index_record(record: bytes) -> tuple[PathEntry, bool]:
654
683
  header, raw_path = raw_entry.split(b"\t", maxsplit=1)
655
684
  raw_mode, raw_object_id, raw_stage = header.split()
656
685
  except ValueError as error:
657
- raise RuntimeError("invalid Git index entry while resolving scope") from error
686
+ raise RuntimeError(
687
+ "The Git index entry is not valid for scope resolution."
688
+ ) from error
658
689
  path = os.fsdecode(raw_path)
659
690
  stage = raw_stage.decode("ascii")
660
691
  if stage != "0":
661
- raise RuntimeError(f"unmerged index entry in selected scope: {path}")
692
+ raise RuntimeError(
693
+ f"The selected scope contains an unmerged index entry: '{path}'."
694
+ )
662
695
  mode = raw_mode.decode("ascii")
663
696
  return (
664
697
  PathEntry(
@@ -674,7 +707,7 @@ def parse_tagged_index_record(record: bytes) -> tuple[PathEntry, bool]:
674
707
  def worktree_metadata(
675
708
  head: str, paths: Sequence[str], repository_root: Path
676
709
  ) -> WorktreeMetadata:
677
- """Stream bounded HEAD/index metadata without reading worktree bytes."""
710
+ """Stream bounded HEAD and index metadata without reading worktree bytes."""
678
711
  candidates: set[str] = set()
679
712
  head_entries: dict[str, PathEntry] = {}
680
713
  index_entries: dict[str, PathEntry] = {}
@@ -697,7 +730,8 @@ def worktree_metadata(
697
730
  path = os.fsdecode(record)
698
731
  if path not in candidates:
699
732
  raise RuntimeError(
700
- f"staged change path was missing from worktree scope metadata: {path}"
733
+ "The worktree scope metadata does not contain this staged change "
734
+ f"path: '{path}'."
701
735
  )
702
736
  staged_change_paths.add(path)
703
737
 
@@ -747,7 +781,7 @@ def worktree_metadata(
747
781
 
748
782
  @dataclass(frozen=True)
749
783
  class FileSnapshot:
750
- """A regular file's streamed content identity and optional Git blob OID."""
784
+ """This record contains a regular-file identity and optional Git blob object identifier."""
751
785
 
752
786
  fingerprint: ContentFingerprint
753
787
  object_id: str | None
@@ -756,7 +790,7 @@ class FileSnapshot:
756
790
 
757
791
  @dataclass(frozen=True)
758
792
  class WorktreePathSnapshot:
759
- """No-follow worktree metadata plus raw content identity where applicable."""
793
+ """This record contains worktree metadata and the applicable raw content identity."""
760
794
 
761
795
  entry: PathEntry
762
796
  content: ContentFingerprint | None = None
@@ -765,7 +799,7 @@ class WorktreePathSnapshot:
765
799
 
766
800
  @dataclass(frozen=True)
767
801
  class WorktreeTrackedCapture:
768
- """One bounded representation of all worktree changes relative to HEAD."""
802
+ """This record contains all worktree changes relative to HEAD within a size limit."""
769
803
 
770
804
  paths: tuple[str, ...]
771
805
  fingerprint: ContentFingerprint
@@ -780,7 +814,8 @@ def git_object_format(repository_root: Path) -> str:
780
814
  hashlib.new(object_format)
781
815
  except ValueError as error:
782
816
  raise RuntimeError(
783
- f"unsupported Git object format for worktree review: {object_format}"
817
+ "The worktree review does not support this Git object format: "
818
+ f"'{object_format}'."
784
819
  ) from error
785
820
  return object_format
786
821
 
@@ -817,7 +852,7 @@ def read_regular_file_snapshot_without_following(
817
852
  relative_path: str,
818
853
  object_format: str | None = None,
819
854
  ) -> FileSnapshot:
820
- """Fingerprint a regular file without following links or blocking on a FIFO."""
855
+ """Fingerprint a regular file without following symbolic links or blocking on a named pipe."""
821
856
  nonblocking_value = getattr(os, "O_NONBLOCK", None)
822
857
  try:
823
858
  if nonblocking_value is None:
@@ -825,7 +860,8 @@ def read_regular_file_snapshot_without_following(
825
860
  nonblocking_flag = index(nonblocking_value)
826
861
  except TypeError as error:
827
862
  raise RuntimeError(
828
- "host cannot inspect repository files without nonblocking open support"
863
+ "The host cannot inspect repository files without support for a "
864
+ "nonblocking open operation."
829
865
  ) from error
830
866
  parent_descriptor, filename = nofollow_parent_descriptor(
831
867
  repository_root, relative_path
@@ -840,7 +876,8 @@ def read_regular_file_snapshot_without_following(
840
876
  )
841
877
  except (NotImplementedError, TypeError) as error:
842
878
  raise RuntimeError(
843
- "host cannot inspect repository paths without following links"
879
+ "The host cannot inspect repository paths without following symbolic "
880
+ "links."
844
881
  ) from error
845
882
  finally:
846
883
  try:
@@ -853,7 +890,9 @@ def read_regular_file_snapshot_without_following(
853
890
  try:
854
891
  initial_stat = os.fstat(descriptor)
855
892
  if not stat.S_ISREG(initial_stat.st_mode):
856
- raise RuntimeError(f"untracked path is not a regular file: {relative_path}")
893
+ raise RuntimeError(
894
+ f"The untracked path is not a regular file: '{relative_path}'."
895
+ )
857
896
  content_digest = sha256()
858
897
  object_digest = (
859
898
  hashlib.new(object_format) if object_format is not None else None
@@ -871,7 +910,7 @@ def read_regular_file_snapshot_without_following(
871
910
  initial_stat, final_stat
872
911
  ):
873
912
  raise RuntimeError(
874
- f"repository file changed while resolving scope: {relative_path}"
913
+ f"The repository file changed during scope resolution: '{relative_path}'."
875
914
  )
876
915
  return FileSnapshot(
877
916
  fingerprint=ContentFingerprint(
@@ -910,7 +949,8 @@ def worktree_path_snapshot(
910
949
  if entry.kind == "symlink":
911
950
  if entry.target is None:
912
951
  raise RuntimeError(
913
- f"repository link changed while resolving scope: {relative_path}"
952
+ "The repository symbolic link changed during scope resolution: "
953
+ f"'{relative_path}'."
914
954
  )
915
955
  contents = os.fsencode(entry.target)
916
956
  return WorktreePathSnapshot(
@@ -924,7 +964,7 @@ def worktree_path_snapshot(
924
964
  def git_mode_for_worktree_file(entry: PathEntry) -> str:
925
965
  """Map a regular filesystem mode to Git's executable-bit-only mode."""
926
966
  if entry.kind != "file" or entry.mode is None:
927
- raise RuntimeError(f"invalid worktree file entry: {entry.path}")
967
+ raise RuntimeError(f"The worktree file entry is not valid: '{entry.path}'.")
928
968
  return "100755" if int(entry.mode, 8) & 0o111 else "100644"
929
969
 
930
970
 
@@ -947,7 +987,8 @@ def worktree_matches_entry(
947
987
  and tree_entry.mode == "120000"
948
988
  )
949
989
  raise RuntimeError(
950
- f"worktree scope cannot safely compare Git object kind for {tree_entry.path}"
990
+ "The worktree scope cannot safely compare the Git object type for "
991
+ f"'{tree_entry.path}'."
951
992
  )
952
993
 
953
994
 
@@ -997,15 +1038,15 @@ def worktree_tracked_capture(
997
1038
  and index_entry.kind == "git-submodule"
998
1039
  ):
999
1040
  raise RuntimeError(
1000
- "worktree scope cannot safely determine submodule state for "
1001
- f"{path}; use --staged or --range"
1041
+ "The worktree scope cannot safely determine the submodule state for "
1042
+ f"'{path}'. Use '--staged' or '--range'."
1002
1043
  )
1003
1044
  index_differs_from_head = index_entry != head_entry
1004
1045
  if path in metadata.skip_worktree_paths and snapshot.entry.kind == "absent":
1005
1046
  if index_differs_from_head:
1006
1047
  raise RuntimeError(
1007
- "worktree scope cannot safely inspect staged change in "
1008
- f"skip-worktree path {path}; use --staged"
1048
+ "The worktree scope cannot safely inspect the staged change in "
1049
+ f"skip-worktree path '{path}'. Use '--staged'."
1009
1050
  )
1010
1051
  continue
1011
1052
  if (
@@ -1014,8 +1055,8 @@ def worktree_tracked_capture(
1014
1055
  and not worktree_matches_entry(snapshot, index_entry)
1015
1056
  ):
1016
1057
  raise RuntimeError(
1017
- "worktree scope cannot safely inspect staged change whose live "
1018
- f"bytes differ from the index for {path}; use --staged"
1058
+ "The worktree scope cannot safely inspect a staged change whose live "
1059
+ f"bytes differ from the index for '{path}'. Use '--staged'."
1019
1060
  )
1020
1061
  if worktree_matches_entry(snapshot, head_entry):
1021
1062
  continue
@@ -1039,14 +1080,17 @@ def untracked_content(
1039
1080
  if entry.kind == "symlink":
1040
1081
  if entry.target is None:
1041
1082
  raise RuntimeError(
1042
- f"untracked link changed while resolving scope: {relative_path}"
1083
+ "The untracked symbolic link changed during scope resolution: "
1084
+ f"'{relative_path}'."
1043
1085
  )
1044
1086
  return b"symlink", content_fingerprint((os.fsencode(entry.target),))
1045
1087
  if entry.kind == "file":
1046
1088
  return b"file", read_regular_file_without_following(
1047
1089
  repository_root, relative_path
1048
1090
  )
1049
- raise RuntimeError(f"untracked path changed while resolving scope: {relative_path}")
1091
+ raise RuntimeError(
1092
+ f"The untracked path changed during scope resolution: '{relative_path}'."
1093
+ )
1050
1094
 
1051
1095
 
1052
1096
  def scope_digest(
@@ -1154,7 +1198,7 @@ def resolve_scope(
1154
1198
  paths = normalized_paths(repository_root, selected_paths)
1155
1199
  if scope == "range":
1156
1200
  if range_value is None:
1157
- raise RuntimeError("range scope requires BASE..HEAD")
1201
+ raise RuntimeError("The range scope requires 'BASE..HEAD'.")
1158
1202
  base, head = range_revisions(range_value, repository_root)
1159
1203
  else:
1160
1204
  head = verified_commit("HEAD", repository_root)
@@ -1163,9 +1207,11 @@ def resolve_scope(
1163
1207
  first_capture = capture_scope(scope, base, head, paths, repository_root)
1164
1208
  second_capture = capture_scope(scope, base, head, paths, repository_root)
1165
1209
  if first_capture != second_capture:
1166
- raise RuntimeError("change scope changed while resolving; retry the review")
1210
+ raise RuntimeError(
1211
+ "The change scope changed during resolution. Run the review again."
1212
+ )
1167
1213
  if scope != "range" and verified_commit("HEAD", repository_root) != head:
1168
- raise RuntimeError("HEAD changed while resolving; retry the review")
1214
+ raise RuntimeError("HEAD changed during resolution. Run the review again.")
1169
1215
  return {
1170
1216
  "base": base,
1171
1217
  "content_source": (