@homericintelligence/athena-opencode 0.5.1 → 0.5.2

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 (40) hide show
  1. package/package.json +1 -1
  2. package/skills/_cli.py +7 -4
  3. package/skills/_plugin.json +1 -0
  4. package/skills/_support/docs/dependency-resolution.md +49 -38
  5. package/skills/_support/docs/policies/development.md +16 -2
  6. package/skills/_support/docs/principles/README.md +191 -168
  7. package/skills/_support/docs/principles/details/p065-verify-before-claiming-completion.md +7 -5
  8. package/skills/_support/docs/review/README.md +5 -1
  9. package/skills/_support/docs/review/behavior-first-testing.md +5 -0
  10. package/skills/_support/docs/review/common.md +44 -9
  11. package/skills/_support/docs/review/issue-planning.md +36 -9
  12. package/skills/advise/SKILL.md +82 -74
  13. package/skills/advise/scripts/list_retrievable_skills.py +17 -5
  14. package/skills/advise/scripts/resolve_knowledge_checkout.py +533 -0
  15. package/skills/brainstorm/SKILL.md +3 -0
  16. package/skills/change-review/scripts/resolve_scope.py +25 -11
  17. package/skills/finalize-plan/SKILL.md +10 -3
  18. package/skills/git-worktrees/SKILL.md +1 -1
  19. package/skills/git-worktrees/scripts/prepare_worktree.py +18 -5
  20. package/skills/learn/SKILL.md +136 -59
  21. package/skills/pr-review/SKILL.md +33 -15
  22. package/skills/pr-review/references/criteria.md +3 -0
  23. package/skills/pr-review/references/delivery.md +136 -18
  24. package/skills/pr-review/references/evidence.md +92 -12
  25. package/skills/pr-review/scripts/collect_evidence.py +101 -22
  26. package/skills/pr-review/scripts/deliver_go.py +701 -0
  27. package/skills/pr-review/scripts/diff_context.py +28 -11
  28. package/skills/pr-review/scripts/materialize_snapshot.py +29 -10
  29. package/skills/pr-review/scripts/resolve_pr.py +24 -10
  30. package/skills/realign/SKILL.md +516 -0
  31. package/skills/realign/references/aislop-integration.md +215 -0
  32. package/skills/realign/references/architecture-and-structure.md +271 -0
  33. package/skills/realign/references/control-flow-and-errors.md +344 -0
  34. package/skills/realign/references/tests-dependencies-and-security.md +261 -0
  35. package/skills/realign/scripts/resolve_assessment.py +1525 -0
  36. package/skills/simplify/SKILL.md +174 -0
  37. package/skills/systematic-debugging/SKILL.md +2 -0
  38. package/skills/systematic-debugging/scripts/repository_evidence.py +17 -4
  39. package/skills/tidy/SKILL.md +13 -1
  40. package/skills/tidy/scripts/run_tidy.py +51 -3
@@ -0,0 +1,533 @@
1
+ #!/usr/bin/env python3
2
+ """Resolve a trusted Mnemosyne checkout for read-only or write workflows."""
3
+
4
+ from __future__ import annotations
5
+
6
+ import argparse
7
+ import importlib.util
8
+ import json
9
+ import os
10
+ import re
11
+ import shutil
12
+ import subprocess
13
+ import sys
14
+ from collections.abc import Callable
15
+ from dataclasses import dataclass
16
+ from pathlib import Path
17
+ from typing import Any, Protocol, cast
18
+ from urllib.parse import urlparse
19
+
20
+
21
+ class _CliModule(Protocol):
22
+ """Describe the shared helpers loaded from the installed skill corpus."""
23
+
24
+ argument_parser: Callable[..., argparse.ArgumentParser]
25
+ git_read_arguments: Callable[[], tuple[str, ...]]
26
+ git_read_environment: Callable[[], dict[str, str]]
27
+ run_command: Callable[..., subprocess.CompletedProcess[str]]
28
+
29
+
30
+ def _load_installed_cli_module() -> _CliModule:
31
+ """Load the sibling shared helper from an installed skill corpus."""
32
+ cli_path = Path(__file__).resolve().parents[2] / "_cli.py"
33
+ spec = importlib.util.spec_from_file_location("athena_installed_cli", cli_path)
34
+ if spec is None or spec.loader is None:
35
+ raise RuntimeError(
36
+ f"The installed Athena CLI helper is unavailable: '{cli_path}'."
37
+ )
38
+ module = importlib.util.module_from_spec(spec)
39
+ spec.loader.exec_module(module)
40
+ return cast(_CliModule, module)
41
+
42
+
43
+ if __package__ in {None, ""}:
44
+ _cli = _load_installed_cli_module()
45
+ argument_parser = _cli.argument_parser
46
+ git_read_arguments = _cli.git_read_arguments
47
+ git_read_environment = _cli.git_read_environment
48
+ run_command = _cli.run_command
49
+ else:
50
+ from skills._cli import (
51
+ argument_parser,
52
+ git_read_arguments,
53
+ git_read_environment,
54
+ run_command,
55
+ )
56
+
57
+ DEFAULT_KNOWLEDGE_ROOT = Path.home() / ".agent_brain" / "knowledge"
58
+ DEFAULT_ORGANIZATION_OWNER = "HomericIntelligence"
59
+ REPOSITORY_NAME = "Mnemosyne"
60
+ BEST_EFFORT_REMOTE_TIMEOUT_SECONDS = 2.0
61
+ OWNER_PATTERN = re.compile(r"^[A-Za-z0-9](?:[A-Za-z0-9-]{0,37}[A-Za-z0-9])?$")
62
+ FULL_SHA_PATTERN = re.compile(r"^[0-9a-f]{40}$")
63
+ SAFE_LOCAL_CONFIG_KEYS = frozenset(
64
+ {
65
+ "core.bare",
66
+ "core.filemode",
67
+ "core.ignorecase",
68
+ "core.logallrefupdates",
69
+ "core.precomposeunicode",
70
+ "core.repositoryformatversion",
71
+ "remote.origin.fetch",
72
+ "remote.origin.url",
73
+ }
74
+ )
75
+ SAFE_BRANCH_CONFIG_PATTERN = re.compile(r"^branch\..+\.(?:merge|remote)$")
76
+ SAFE_LOCAL_GIT_OVERRIDES = (
77
+ "-c",
78
+ f"core.hooksPath={os.devnull}",
79
+ "-c",
80
+ "core.fsmonitor=false",
81
+ )
82
+
83
+
84
+ @dataclass(frozen=True)
85
+ class LocalCheckout:
86
+ """Validated local checkout metadata."""
87
+
88
+ root: Path
89
+ repository: str
90
+ origin: str
91
+ branch: str | None
92
+ revision: str
93
+
94
+
95
+ @dataclass(frozen=True)
96
+ class RefreshOutcome:
97
+ """Best-effort refresh result."""
98
+
99
+ revision: str
100
+ refresh_state: str
101
+ freshness_limit: str
102
+ limitations: list[str]
103
+
104
+
105
+ def validate_owner(owner: str) -> str:
106
+ """Validate a GitHub owner name."""
107
+ if not OWNER_PATTERN.fullmatch(owner):
108
+ raise RuntimeError(f"The knowledge owner is not valid: '{owner}'.")
109
+ return owner
110
+
111
+
112
+ def expected_repository() -> str:
113
+ """Return the expected Mnemosyne repository for the current environment."""
114
+ owner = os.environ.get("HOMERIC_INTELLIGENCE_MNEMOSYNE_OWNER")
115
+ if owner is None or not owner:
116
+ owner = DEFAULT_ORGANIZATION_OWNER
117
+ return f"{validate_owner(owner)}/{REPOSITORY_NAME}"
118
+
119
+
120
+ def repository_from_origin(origin: str) -> str:
121
+ """Return the repository identity encoded in a Git origin URL or path."""
122
+ owner: str | None = None
123
+ repository: str | None = None
124
+ if origin.startswith("git@") and ":" in origin and "://" not in origin:
125
+ host, candidate = origin.split(":", maxsplit=1)
126
+ if host != "git@github.com":
127
+ raise RuntimeError(
128
+ f"The origin is not a trusted GitHub repository URL: '{origin}'."
129
+ )
130
+ segments = [
131
+ segment for segment in candidate.replace("\\", "/").split("/") if segment
132
+ ]
133
+ if len(segments) != 2:
134
+ raise RuntimeError(
135
+ f"The origin does not identify an owner and repository: '{origin}'."
136
+ )
137
+ owner, repository = segments
138
+ else:
139
+ parsed = urlparse(origin)
140
+ if parsed.scheme not in {"https", "ssh"}:
141
+ raise RuntimeError(
142
+ f"The origin is not a trusted GitHub repository URL: '{origin}'."
143
+ )
144
+ if parsed.hostname != "github.com":
145
+ raise RuntimeError(
146
+ f"The origin is not a trusted GitHub repository URL: '{origin}'."
147
+ )
148
+ segments = [
149
+ segment for segment in parsed.path.replace("\\", "/").split("/") if segment
150
+ ]
151
+ if len(segments) != 2:
152
+ raise RuntimeError(
153
+ f"The origin does not identify an owner and repository: '{origin}'."
154
+ )
155
+ owner, repository = segments
156
+ assert owner is not None
157
+ assert repository is not None
158
+ repository = repository.removesuffix(".git")
159
+ if not OWNER_PATTERN.fullmatch(owner):
160
+ raise RuntimeError(
161
+ f"The origin does not identify a valid repository owner: '{origin}'."
162
+ )
163
+ if repository.casefold() != REPOSITORY_NAME.casefold():
164
+ raise RuntimeError(
165
+ f"The origin does not identify the Mnemosyne repository: '{origin}'."
166
+ )
167
+ return f"{owner}/{repository}"
168
+
169
+
170
+ def run_git(
171
+ cwd: Path, *arguments: str, timeout: float | None = None
172
+ ) -> subprocess.CompletedProcess[str]:
173
+ """Run Git with the immutable read boundary."""
174
+ try:
175
+ return run_command(
176
+ [
177
+ "git",
178
+ *git_read_arguments(),
179
+ *SAFE_LOCAL_GIT_OVERRIDES,
180
+ *arguments,
181
+ ],
182
+ capture_output=True,
183
+ cwd=cwd,
184
+ env=git_read_environment(),
185
+ text=True,
186
+ check=False,
187
+ timeout=timeout,
188
+ )
189
+ except subprocess.TimeoutExpired as error:
190
+ raise RuntimeError(
191
+ f"The git {' '.join(arguments)} command timed out after "
192
+ f"{timeout:.1f} seconds."
193
+ ) from error
194
+
195
+
196
+ def require_safe_local_git_configuration(cwd: Path) -> None:
197
+ """Reject local Git settings that can redirect or execute refresh work."""
198
+ result = run_git(
199
+ cwd,
200
+ "config",
201
+ "--local",
202
+ "--name-only",
203
+ "--null",
204
+ "--list",
205
+ "--includes",
206
+ )
207
+ if result.returncode != 0:
208
+ message = result.stderr.strip() or "The local Git configuration cannot be read."
209
+ raise RuntimeError(message)
210
+ keys = [value.casefold() for value in result.stdout.split("\0") if value]
211
+ unsafe = sorted(
212
+ key
213
+ for key in keys
214
+ if key not in SAFE_LOCAL_CONFIG_KEYS
215
+ and SAFE_BRANCH_CONFIG_PATTERN.fullmatch(key) is None
216
+ )
217
+ if unsafe:
218
+ raise RuntimeError(
219
+ "The knowledge checkout has unsafe local Git configuration: "
220
+ + ", ".join(unsafe)
221
+ )
222
+
223
+
224
+ def git_text(cwd: Path, *arguments: str) -> str:
225
+ """Return the trimmed stdout for a successful immutable Git command."""
226
+ result = run_git(cwd, *arguments)
227
+ if result.returncode != 0:
228
+ message = result.stderr.strip() or (
229
+ f"The git {' '.join(arguments)} command failed."
230
+ )
231
+ raise RuntimeError(message)
232
+ return result.stdout.strip()
233
+
234
+
235
+ def validate_local_checkout(knowledge_root: Path) -> LocalCheckout:
236
+ """Validate the local checkout identity and cleanliness."""
237
+ if not knowledge_root.is_dir():
238
+ raise RuntimeError(
239
+ f"The knowledge checkout is not available: '{knowledge_root}'."
240
+ )
241
+ top_level = Path(git_text(knowledge_root, "rev-parse", "--show-toplevel"))
242
+ if top_level.resolve() != knowledge_root.resolve():
243
+ raise RuntimeError(
244
+ f"The knowledge checkout root does not match the requested path: "
245
+ f"'{knowledge_root}'."
246
+ )
247
+ origin = git_text(knowledge_root, "config", "--get", "remote.origin.url")
248
+ repository = repository_from_origin(origin)
249
+ revision = git_text(knowledge_root, "rev-parse", "HEAD")
250
+ if not FULL_SHA_PATTERN.fullmatch(revision):
251
+ raise RuntimeError(
252
+ f"The knowledge checkout revision is not a full commit SHA: '{revision}'."
253
+ )
254
+ status = git_text(
255
+ knowledge_root,
256
+ "status",
257
+ "--porcelain=v1",
258
+ "--untracked-files=all",
259
+ )
260
+ if status:
261
+ raise RuntimeError("The knowledge checkout is dirty:\n" + status)
262
+ branch_result = run_git(knowledge_root, "branch", "--show-current")
263
+ if branch_result.returncode != 0:
264
+ message = (
265
+ branch_result.stderr.strip() or "The current branch could not be read."
266
+ )
267
+ raise RuntimeError(message)
268
+ branch = branch_result.stdout.strip() or None
269
+ return LocalCheckout(
270
+ root=knowledge_root,
271
+ repository=repository,
272
+ origin=origin,
273
+ branch=branch,
274
+ revision=revision,
275
+ )
276
+
277
+
278
+ def gh_command(*arguments: str) -> subprocess.CompletedProcess[str]:
279
+ """Run `gh` and return the completed process."""
280
+ try:
281
+ return run_command(
282
+ ["gh", *arguments],
283
+ capture_output=True,
284
+ text=True,
285
+ check=False,
286
+ timeout=BEST_EFFORT_REMOTE_TIMEOUT_SECONDS,
287
+ )
288
+ except subprocess.TimeoutExpired as error:
289
+ raise RuntimeError(
290
+ f"The gh {' '.join(arguments)} command timed out after "
291
+ f"{BEST_EFFORT_REMOTE_TIMEOUT_SECONDS:.1f} seconds."
292
+ ) from error
293
+
294
+
295
+ def parse_repo_view(output: str, expected: str) -> str:
296
+ """Return the default branch reported by GitHub."""
297
+ value = json.loads(output)
298
+ if not isinstance(value, dict):
299
+ raise TypeError("GitHub returned repository metadata that is not valid.")
300
+ name_with_owner = value.get("nameWithOwner")
301
+ if (
302
+ isinstance(name_with_owner, str)
303
+ and name_with_owner.casefold() != expected.casefold()
304
+ ):
305
+ raise RuntimeError(
306
+ f"GitHub returned a different repository than expected: '{name_with_owner}'."
307
+ )
308
+ default_branch_ref = value.get("defaultBranchRef")
309
+ if not isinstance(default_branch_ref, dict):
310
+ raise TypeError(
311
+ "GitHub did not return a default branch for the knowledge repository."
312
+ )
313
+ default_branch = default_branch_ref.get("name")
314
+ if not isinstance(default_branch, str) or not default_branch:
315
+ raise TypeError(
316
+ "GitHub did not return a valid default branch for the knowledge repository."
317
+ )
318
+ return default_branch
319
+
320
+
321
+ def unavailable_refresh(
322
+ checkout: LocalCheckout,
323
+ mode: str,
324
+ limitations: list[str],
325
+ reason: str,
326
+ *,
327
+ cause: BaseException | None = None,
328
+ ) -> RefreshOutcome:
329
+ """Apply the read-only fallback or the write-mode failure policy."""
330
+ if mode == "write":
331
+ raise RuntimeError(reason) from cause
332
+ limitations.append(reason)
333
+ return RefreshOutcome(
334
+ revision=checkout.revision,
335
+ refresh_state="unavailable",
336
+ freshness_limit="freshness could not be verified or updated",
337
+ limitations=limitations,
338
+ )
339
+
340
+
341
+ def refresh_local_checkout(
342
+ checkout: LocalCheckout, expected: str, mode: str
343
+ ) -> RefreshOutcome:
344
+ """Attempt a best-effort refresh and report the result."""
345
+ limitations: list[str] = []
346
+ if shutil.which("gh") is None:
347
+ return unavailable_refresh(
348
+ checkout,
349
+ mode,
350
+ limitations,
351
+ "The required command is not available: 'gh'.",
352
+ )
353
+ try:
354
+ auth_result = gh_command("auth", "status", "--hostname", "github.com")
355
+ except RuntimeError as error:
356
+ return unavailable_refresh(checkout, mode, limitations, str(error), cause=error)
357
+ if auth_result.returncode != 0:
358
+ reason = (
359
+ auth_result.stderr.strip()
360
+ or auth_result.stdout.strip()
361
+ or "GitHub authentication is unavailable."
362
+ )
363
+ return unavailable_refresh(checkout, mode, limitations, reason)
364
+ try:
365
+ repo_result = gh_command(
366
+ "repo",
367
+ "view",
368
+ expected,
369
+ "--json",
370
+ "nameWithOwner,defaultBranchRef",
371
+ )
372
+ except RuntimeError as error:
373
+ return unavailable_refresh(checkout, mode, limitations, str(error), cause=error)
374
+ if repo_result.returncode != 0:
375
+ reason = (
376
+ repo_result.stderr.strip()
377
+ or repo_result.stdout.strip()
378
+ or "GitHub repository discovery failed."
379
+ )
380
+ return unavailable_refresh(checkout, mode, limitations, reason)
381
+ try:
382
+ default_branch = parse_repo_view(repo_result.stdout, expected)
383
+ except (json.JSONDecodeError, TypeError) as error:
384
+ return unavailable_refresh(checkout, mode, limitations, str(error), cause=error)
385
+ if checkout.branch is None:
386
+ return unavailable_refresh(
387
+ checkout,
388
+ mode,
389
+ limitations,
390
+ "The knowledge checkout is detached and cannot be refreshed.",
391
+ )
392
+ if checkout.branch != default_branch:
393
+ reason = (
394
+ "The knowledge checkout branch does not match the remote default "
395
+ f"branch: '{checkout.branch}' != '{default_branch}'."
396
+ )
397
+ return unavailable_refresh(checkout, mode, limitations, reason)
398
+ try:
399
+ require_safe_local_git_configuration(checkout.root)
400
+ current_revision = git_text(checkout.root, "rev-parse", "HEAD")
401
+ except RuntimeError as error:
402
+ return unavailable_refresh(checkout, mode, limitations, str(error), cause=error)
403
+ if current_revision != checkout.revision:
404
+ return unavailable_refresh(
405
+ checkout,
406
+ mode,
407
+ limitations,
408
+ "The knowledge checkout revision changed before refresh.",
409
+ )
410
+ trusted_url = f"https://github.com/{expected}.git"
411
+ try:
412
+ fetch_result = run_git(
413
+ checkout.root,
414
+ "fetch",
415
+ "--no-tags",
416
+ trusted_url,
417
+ default_branch,
418
+ timeout=BEST_EFFORT_REMOTE_TIMEOUT_SECONDS,
419
+ )
420
+ except RuntimeError as error:
421
+ return unavailable_refresh(checkout, mode, limitations, str(error), cause=error)
422
+ if fetch_result.returncode != 0:
423
+ reason = (
424
+ fetch_result.stderr.strip()
425
+ or fetch_result.stdout.strip()
426
+ or f"The knowledge checkout could not fetch origin/{default_branch}."
427
+ )
428
+ return unavailable_refresh(checkout, mode, limitations, reason)
429
+ try:
430
+ require_safe_local_git_configuration(checkout.root)
431
+ fetched_revision = git_text(checkout.root, "rev-parse", "FETCH_HEAD")
432
+ except RuntimeError as error:
433
+ return unavailable_refresh(checkout, mode, limitations, str(error), cause=error)
434
+ if not FULL_SHA_PATTERN.fullmatch(fetched_revision):
435
+ reason = (
436
+ f"The upstream revision is not a full commit SHA: '{fetched_revision}'."
437
+ )
438
+ return unavailable_refresh(checkout, mode, limitations, reason)
439
+ ancestor_result = run_git(
440
+ checkout.root,
441
+ "merge-base",
442
+ "--is-ancestor",
443
+ checkout.revision,
444
+ fetched_revision,
445
+ )
446
+ if ancestor_result.returncode != 0:
447
+ reason = (
448
+ "The local knowledge revision is not an ancestor of the upstream revision."
449
+ if ancestor_result.returncode == 1
450
+ else ancestor_result.stderr.strip()
451
+ or "The knowledge checkout ancestry could not be verified."
452
+ )
453
+ return unavailable_refresh(checkout, mode, limitations, reason)
454
+ merge_result = run_git(checkout.root, "merge", "--ff-only", "FETCH_HEAD")
455
+ if merge_result.returncode != 0:
456
+ reason = (
457
+ merge_result.stderr.strip()
458
+ or merge_result.stdout.strip()
459
+ or "The knowledge checkout could not fast-forward."
460
+ )
461
+ return unavailable_refresh(checkout, mode, limitations, reason)
462
+ revision = git_text(checkout.root, "rev-parse", "HEAD")
463
+ if revision != fetched_revision:
464
+ raise RuntimeError(
465
+ "The knowledge checkout does not match the verified upstream revision."
466
+ )
467
+ return RefreshOutcome(
468
+ revision=revision,
469
+ refresh_state="updated",
470
+ freshness_limit="freshness verified by upstream refresh",
471
+ limitations=limitations,
472
+ )
473
+
474
+
475
+ def resolve_knowledge_checkout(knowledge_root: Path, mode: str) -> dict[str, Any]:
476
+ """Resolve the local checkout and report its revision and limits."""
477
+ expected = expected_repository()
478
+ checkout = validate_local_checkout(knowledge_root)
479
+ if checkout.repository.casefold() != expected.casefold():
480
+ raise RuntimeError(
481
+ f"The knowledge checkout origin does not match '{expected}': "
482
+ f"'{checkout.origin}'."
483
+ )
484
+ refresh = refresh_local_checkout(checkout, expected, mode)
485
+ return {
486
+ "branch": checkout.branch,
487
+ "checkout": str(checkout.root),
488
+ "checkout_state": "clean",
489
+ "freshness_limit": refresh.freshness_limit,
490
+ "limitations": refresh.limitations,
491
+ "local_revision": checkout.revision,
492
+ "mode": mode,
493
+ "refresh_state": refresh.refresh_state,
494
+ "repository": checkout.repository,
495
+ "revision": refresh.revision,
496
+ "trust_basis": "validated local checkout",
497
+ }
498
+
499
+
500
+ def main(argv: list[str] | None = None) -> int:
501
+ """Resolve the checkout and print a machine-readable result."""
502
+ parser = argument_parser(description=__doc__)
503
+ parser.add_argument(
504
+ "--mode",
505
+ choices=("read-only", "write"),
506
+ required=True,
507
+ help="Select the read-only or mutation boundary.",
508
+ )
509
+ parser.add_argument(
510
+ "--knowledge-root",
511
+ type=Path,
512
+ default=DEFAULT_KNOWLEDGE_ROOT,
513
+ help="Use the resolved Mnemosyne checkout path.",
514
+ )
515
+ parser.add_argument(
516
+ "--json",
517
+ action="store_true",
518
+ help="Emit JSON for downstream machine parsing.",
519
+ )
520
+ arguments = parser.parse_args(argv)
521
+ if not arguments.json:
522
+ parser.error("Specify --json.")
523
+ try:
524
+ result = resolve_knowledge_checkout(arguments.knowledge_root, arguments.mode)
525
+ except (json.JSONDecodeError, RuntimeError, TypeError, ValueError) as error:
526
+ print(error, file=sys.stderr)
527
+ return 1
528
+ print(json.dumps(result, sort_keys=True))
529
+ return 0
530
+
531
+
532
+ if __name__ == "__main__":
533
+ raise SystemExit(main())
@@ -99,6 +99,9 @@ Complete in order:
99
99
  - Propose two or three different approaches with trade-offs.
100
100
  - Put your recommended option first. Explain the reason for the recommendation.
101
101
  - Refer to existing patterns in the target codebase.
102
+ - For each approach that adds code or structure, name one credible subtractive or reuse
103
+ alternative first.
104
+ - If no subtractive or reuse alternative can meet the requirement, say why with evidence.
102
105
 
103
106
  ### Present the design
104
107
 
@@ -4,6 +4,7 @@
4
4
  from __future__ import annotations
5
5
 
6
6
  import hashlib
7
+ import importlib.util
7
8
  import json
8
9
  import os
9
10
  import stat
@@ -15,17 +16,30 @@ from dataclasses import dataclass
15
16
  from hashlib import sha256
16
17
  from operator import index
17
18
  from pathlib import Path
18
- from typing import Protocol, cast
19
-
20
- if __package__ in {None, ""}:
21
- sys.path.insert(0, str(Path(__file__).resolve().parents[3]))
22
-
23
- from skills._cli import (
24
- argument_parser,
25
- git_read_arguments,
26
- git_read_environment,
27
- run_command,
28
- )
19
+ from typing import TYPE_CHECKING, Protocol, cast
20
+
21
+ if TYPE_CHECKING or __package__ not in {None, ""}:
22
+ from skills._cli import (
23
+ argument_parser,
24
+ git_read_arguments,
25
+ git_read_environment,
26
+ run_command,
27
+ )
28
+ else:
29
+ _cli_path = Path(__file__).resolve().parents[2] / "_cli.py"
30
+ _cli_spec = importlib.util.spec_from_file_location(
31
+ "athena_installed_cli", _cli_path
32
+ )
33
+ if _cli_spec is None or _cli_spec.loader is None:
34
+ raise RuntimeError(
35
+ f"The installed Athena CLI helper is unavailable: '{_cli_path}'."
36
+ )
37
+ _cli = importlib.util.module_from_spec(_cli_spec)
38
+ _cli_spec.loader.exec_module(_cli)
39
+ argument_parser = _cli.argument_parser
40
+ git_read_arguments = _cli.git_read_arguments
41
+ git_read_environment = _cli.git_read_environment
42
+ run_command = _cli.run_command
29
43
 
30
44
  READ_CHUNK_SIZE = 1024 * 1024
31
45
  ERROR_OUTPUT_LIMIT = 16 * 1024
@@ -84,23 +84,30 @@ A planning epoch is one set of these sealed source identities:
84
84
 
85
85
  - `R` is the canonical digest of the original issue requirements. It contains the exact issue ID,
86
86
  title, body, and acceptance criteria before finalization.
87
- - `P` identifies one actor-owned `<!-- athena:plan-issue -->` comment ID and its canonical plan-content
87
+ - `P` identifies one actor-owned `<!-- HomericIntelligence:plan-issue -->` comment ID and its canonical plan-content
88
88
  digest.
89
- - `V` identifies one actor-owned `<!-- athena:issue-review -->` comment ID and its review-content
89
+ - `V` identifies one actor-owned `<!-- HomericIntelligence:issue-review -->` comment ID and its review-content
90
90
  digest.
91
91
 
92
+ `P` and `V` must identify different comment IDs. Do not use one comment as both plan and review.
93
+
92
94
  The review must contain the same issue, `R`, plan-comment ID, and `P`. These values must match
93
95
  exactly. The review must have the exact `GO` disposition. It must not have an unresolved `critical`,
94
96
  `major`, or other `required` finding. Do not write if an artifact is conditional, partial,
95
97
  malformed, stale, foreign, duplicated, absent, or not verifiable.
96
98
 
97
99
  Record exactly one marker in the rendered body:
98
- `<!-- athena:finalize-plan R=<R> P=<P> V=<V> F=<F> -->`. Before you calculate `F`, use the literal
100
+ `<!-- HomericIntelligence:finalize-plan R=<R> P=<P> V=<V> F=<F> -->`. Before you calculate `F`, use the literal
99
101
  `<F>` placeholder as the marker's `F` value. Calculate `F` from the final body. Do not calculate a
100
102
  digest from a marker that contains its own digest. The marker identifies the sealed source
101
103
  identities separately from the generated body. It also permits later readback verification without
102
104
  recursion.
103
105
 
106
+ For migration only, resolve the exact actor-owned legacy aliases in the shared issue-planning contract.
107
+ Resolve an existing exact `<!-- athena:finalize-plan R=<R> P=<P> V=<V> F=<F> -->` body marker only as
108
+ sealed historical evidence. New finalizations must write the `HomericIntelligence` markers. Do not emit
109
+ both marker versions.
110
+
104
111
  ## Finalize
105
112
 
106
113
  1. Resolve one exact issue with its node or URL, title, body, state, and authenticated actor.
@@ -155,7 +155,7 @@ workflow and the user's answers to its prompts control the removal decision.
155
155
 
156
156
  - Invoke `tidy` for dependency-locked delegation to Hephaestus branch and worktree cleanup.
157
157
  - Before you report completion or start cleanup, get fresh runnable evidence. Follow the
158
- evidence-integrity policy.
158
+ [evidence-integrity policy](../_support/docs/policies/evidence-integrity.md).
159
159
 
160
160
  ---
161
161
 
@@ -3,16 +3,29 @@
3
3
 
4
4
  from __future__ import annotations
5
5
 
6
+ import importlib.util
6
7
  import json
7
8
  import subprocess
8
9
  import sys
9
10
  import tempfile
10
11
  from pathlib import Path
11
-
12
- if __package__ in {None, ""}:
13
- sys.path.insert(0, str(Path(__file__).resolve().parents[3]))
14
-
15
- from skills._cli import argument_parser, run_command
12
+ from typing import TYPE_CHECKING
13
+
14
+ if TYPE_CHECKING or __package__ not in {None, ""}:
15
+ from skills._cli import argument_parser, run_command
16
+ else:
17
+ _cli_path = Path(__file__).resolve().parents[2] / "_cli.py"
18
+ _cli_spec = importlib.util.spec_from_file_location(
19
+ "athena_installed_cli", _cli_path
20
+ )
21
+ if _cli_spec is None or _cli_spec.loader is None:
22
+ raise RuntimeError(
23
+ f"The installed Athena CLI helper is unavailable: '{_cli_path}'."
24
+ )
25
+ _cli = importlib.util.module_from_spec(_cli_spec)
26
+ _cli_spec.loader.exec_module(_cli)
27
+ argument_parser = _cli.argument_parser
28
+ run_command = _cli.run_command
16
29
 
17
30
 
18
31
  def git(