@homericintelligence/athena-opencode 0.4.4

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 (36) hide show
  1. package/LICENSE +29 -0
  2. package/NOTICE +22 -0
  3. package/README.md +45 -0
  4. package/package.json +29 -0
  5. package/plugin.js +54 -0
  6. package/skills/THIRD_PARTY_LICENSES.md +50 -0
  7. package/skills/_cli.py +152 -0
  8. package/skills/advise/SKILL.md +62 -0
  9. package/skills/advise/scripts/list_retrievable_skills.py +49 -0
  10. package/skills/brainstorm/SKILL.md +110 -0
  11. package/skills/change-review/SKILL.md +68 -0
  12. package/skills/change-review/references/scope-resolution.md +52 -0
  13. package/skills/change-review/scripts/resolve_scope.py +1219 -0
  14. package/skills/finalize-plan/SKILL.md +129 -0
  15. package/skills/git-worktrees/SKILL.md +113 -0
  16. package/skills/git-worktrees/scripts/prepare_worktree.py +153 -0
  17. package/skills/issue-review/SKILL.md +67 -0
  18. package/skills/learn/SKILL.md +208 -0
  19. package/skills/myrmidon-swarm/SKILL.md +93 -0
  20. package/skills/plan-issue/SKILL.md +70 -0
  21. package/skills/pr-review/SKILL.md +114 -0
  22. package/skills/pr-review/references/criteria.md +26 -0
  23. package/skills/pr-review/references/delivery.md +135 -0
  24. package/skills/pr-review/references/evidence.md +233 -0
  25. package/skills/pr-review/references/prevalidated.md +155 -0
  26. package/skills/pr-review/scripts/collect_evidence.py +1478 -0
  27. package/skills/pr-review/scripts/diff_context.py +74 -0
  28. package/skills/pr-review/scripts/materialize_snapshot.py +731 -0
  29. package/skills/pr-review/scripts/pr_identity.py +80 -0
  30. package/skills/pr-review/scripts/resolve_pr.py +258 -0
  31. package/skills/repo-review/SKILL.md +119 -0
  32. package/skills/systematic-debugging/SKILL.md +199 -0
  33. package/skills/systematic-debugging/scripts/repository_evidence.py +77 -0
  34. package/skills/test-driven-development/SKILL.md +75 -0
  35. package/skills/tidy/SKILL.md +71 -0
  36. package/skills/tidy/scripts/run_tidy.py +43 -0
@@ -0,0 +1,80 @@
1
+ """Validate pull-request identifiers shared by PR review helpers."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import re
6
+ from urllib.parse import urlparse
7
+
8
+ PR_URL = re.compile(r"https://github\.com/[^/\s]+/[^/\s]+/pull/[1-9][0-9]*")
9
+ COMMIT_OID = re.compile(r"[0-9a-f]{40}\Z")
10
+ GITHUB_REPOSITORY = re.compile(r"[A-Za-z0-9][A-Za-z0-9-]*/[A-Za-z0-9._-]+\Z")
11
+ GITHUB_HOST = "github.com"
12
+
13
+
14
+ def validate_pr_identifier(value: str) -> None:
15
+ """Require a positive PR number or canonical GitHub pull-request URL."""
16
+ if (value.isascii() and value.isdigit() and int(value) > 0) or PR_URL.fullmatch(
17
+ value
18
+ ):
19
+ return
20
+ raise RuntimeError(f"invalid pull-request identifier: {value!r}")
21
+
22
+
23
+ def require_commit_oid(value: object, label: str) -> str:
24
+ """Return one canonical immutable commit OID or fail closed."""
25
+ if not isinstance(value, str) or COMMIT_OID.fullmatch(value) is None:
26
+ raise RuntimeError(f"{label} must be a lowercase 40-hex Git commit OID")
27
+ return value
28
+
29
+
30
+ def require_github_repository(value: object, label: str) -> str:
31
+ """Return a canonical owner/repository target or reject unsafe input."""
32
+ if not isinstance(value, str) or GITHUB_REPOSITORY.fullmatch(value) is None:
33
+ raise RuntimeError(f"{label} must be a canonical GitHub owner/repository")
34
+ return value
35
+
36
+
37
+ def require_github_host(value: object, label: str) -> str:
38
+ """Return the supported canonical GitHub hostname or fail closed."""
39
+ if value != GITHUB_HOST:
40
+ raise RuntimeError(f"{label} must be {GITHUB_HOST}")
41
+ return GITHUB_HOST
42
+
43
+
44
+ def canonical_pull_request_url(repository: object, number: object) -> str:
45
+ """Return one exact public-GitHub pull-request URL from trusted identity."""
46
+ canonical_repository = require_github_repository(repository, "repository")
47
+ if isinstance(number, bool) or not isinstance(number, int) or number < 1:
48
+ raise RuntimeError("pull-request number must be a positive integer")
49
+ return f"https://{GITHUB_HOST}/{canonical_repository}/pull/{number}"
50
+
51
+
52
+ def pull_request_number(value: str) -> int:
53
+ """Return the positive number encoded by a validated PR identifier."""
54
+ validate_pr_identifier(value)
55
+ if value.isdigit():
56
+ return int(value)
57
+ path = urlparse(value).path.rstrip("/")
58
+ return int(path.rsplit("/", maxsplit=1)[-1])
59
+
60
+
61
+ def require_canonical_pull_request_url(
62
+ value: object, repository: object, number: object, label: str
63
+ ) -> str:
64
+ """Require the exact canonical URL for a retained pull-request identity."""
65
+ canonical_url = canonical_pull_request_url(repository, number)
66
+ if value != canonical_url:
67
+ raise RuntimeError(f"{label} must be the canonical GitHub pull-request URL")
68
+ return canonical_url
69
+
70
+
71
+ def repository_from_pr_url(url: str, number: int) -> str:
72
+ """Return owner/repository after validating a canonical PR URL and number."""
73
+ parsed_url = urlparse(url)
74
+ path_parts = parsed_url.path.strip("/").split("/")
75
+ if len(path_parts) != 4 or path_parts[2] != "pull" or path_parts[3] != str(number):
76
+ raise RuntimeError(f"GitHub returned invalid pull-request URL: {url}")
77
+ repository = "/".join(path_parts[:2])
78
+ if url != canonical_pull_request_url(repository, number):
79
+ raise RuntimeError(f"GitHub returned invalid pull-request URL: {url}")
80
+ return repository
@@ -0,0 +1,258 @@
1
+ #!/usr/bin/env python3
2
+ """Resolve an explicit PR or the sole open PR for the current branch."""
3
+
4
+ from __future__ import annotations
5
+
6
+ import json
7
+ import sys
8
+ from collections.abc import Sequence
9
+ from dataclasses import dataclass
10
+ from pathlib import Path
11
+ from typing import Any
12
+
13
+ if __package__ in {None, ""}:
14
+ sys.path.insert(0, str(Path(__file__).resolve().parents[3]))
15
+
16
+ from pr_identity import (
17
+ canonical_pull_request_url,
18
+ pull_request_number,
19
+ repository_from_pr_url,
20
+ require_commit_oid,
21
+ require_github_host,
22
+ require_github_repository,
23
+ validate_pr_identifier,
24
+ )
25
+
26
+ from skills._cli import (
27
+ argument_parser,
28
+ git_read_arguments,
29
+ git_read_environment,
30
+ run_command,
31
+ )
32
+
33
+ FIELDS = "number,url,state,headRefName,baseRefName,headRefOid,baseRefOid"
34
+
35
+
36
+ @dataclass(frozen=True)
37
+ class RepositoryTarget:
38
+ """A forge target supplied without ambient CLI or checkout inference."""
39
+
40
+ host: str
41
+ repository: str
42
+
43
+ def repository_argument(self) -> str:
44
+ """Return the fully qualified repository target accepted by gh."""
45
+ return f"{self.host}/{self.repository}"
46
+
47
+
48
+ def command(*arguments: str) -> str:
49
+ result = run_command(arguments, capture_output=True, text=True, check=False)
50
+ if result.returncode != 0:
51
+ message = result.stderr.strip() or f"command failed: {' '.join(arguments)}"
52
+ raise RuntimeError(message)
53
+ return result.stdout
54
+
55
+
56
+ def current_branch() -> str:
57
+ """Return the current branch through the hermetic Git read boundary."""
58
+ result = run_command(
59
+ ["git", *git_read_arguments(), "branch", "--show-current"],
60
+ capture_output=True,
61
+ env=git_read_environment(),
62
+ text=True,
63
+ check=False,
64
+ )
65
+ if result.returncode != 0:
66
+ message = result.stderr.strip() or "git branch --show-current failed"
67
+ raise RuntimeError(message)
68
+ return result.stdout.strip()
69
+
70
+
71
+ def load_object(output: str) -> dict[str, Any]:
72
+ value = json.loads(output)
73
+ if not isinstance(value, dict):
74
+ raise TypeError("GitHub returned an invalid pull-request object")
75
+ return value
76
+
77
+
78
+ def target_from_arguments(
79
+ parser: Any,
80
+ identifier: str | None,
81
+ host: str | None,
82
+ repository: str | None,
83
+ ) -> RepositoryTarget:
84
+ """Resolve a trusted target from explicit flags or one canonical PR URL."""
85
+
86
+ def identity_from_url(value: str) -> tuple[int, str]:
87
+ try:
88
+ number = pull_request_number(value)
89
+ return number, repository_from_pr_url(value, number)
90
+ except RuntimeError as error:
91
+ parser.error(f"invalid pull-request URL: {error}")
92
+ raise AssertionError("argument parser returned after a URL error")
93
+
94
+ if (host is None) != (repository is None):
95
+ parser.error("--target-host and --target-repository must be supplied together")
96
+ if host is not None and repository is not None:
97
+ try:
98
+ target = RepositoryTarget(
99
+ host=require_github_host(host, "--target-host"),
100
+ repository=require_github_repository(repository, "--target-repository"),
101
+ )
102
+ except RuntimeError as error:
103
+ parser.error(str(error))
104
+ if identifier is not None and identifier.startswith("https://"):
105
+ _, supplied_repository = identity_from_url(identifier)
106
+ if supplied_repository.casefold() != target.repository.casefold():
107
+ parser.error("pull-request URL does not match --target-repository")
108
+ return target
109
+ if identifier is not None and identifier.startswith("https://"):
110
+ _, repository = identity_from_url(identifier)
111
+ return RepositoryTarget(
112
+ host="github.com",
113
+ repository=repository,
114
+ )
115
+ parser.error(
116
+ "numeric pull requests and branch discovery require --target-host and "
117
+ "--target-repository"
118
+ )
119
+ raise AssertionError("argument parser returned after a target error")
120
+
121
+
122
+ def _resolve_open_pr(identifier: str, target: RepositoryTarget) -> dict[str, Any]:
123
+ """Return the complete metadata for one explicitly identified open PR."""
124
+ validate_pr_identifier(identifier)
125
+ number = pull_request_number(identifier)
126
+ if identifier.startswith("https://"):
127
+ supplied_repository = repository_from_pr_url(identifier, number)
128
+ if supplied_repository.casefold() != target.repository.casefold():
129
+ raise RuntimeError("pull-request URL does not match the retained target")
130
+ pull_request = load_object(
131
+ command(
132
+ "gh",
133
+ "pr",
134
+ "view",
135
+ str(number),
136
+ "--repo",
137
+ target.repository_argument(),
138
+ "--json",
139
+ FIELDS,
140
+ )
141
+ )
142
+ if pull_request.get("state") != "OPEN":
143
+ raise RuntimeError(f"pull request {identifier} is not open")
144
+ if pull_request.get("number") != number:
145
+ raise RuntimeError("GitHub returned a pull request different from the request")
146
+ for field in ("baseRefOid", "headRefOid"):
147
+ require_commit_oid(
148
+ pull_request.get(field), f"GitHub immutable PR revision {field}"
149
+ )
150
+ return pull_request
151
+
152
+
153
+ def _validate_repository_identity(
154
+ pull_request: dict[str, Any], target: RepositoryTarget
155
+ ) -> None:
156
+ """Reject a PR URL that differs from the retained explicit forge target."""
157
+ number = pull_request.get("number")
158
+ url = pull_request.get("url")
159
+ if not isinstance(number, int) or not isinstance(url, str):
160
+ raise TypeError("GitHub returned incomplete pull-request identity")
161
+ pull_repository = repository_from_pr_url(url, number)
162
+ if pull_repository.casefold() != target.repository.casefold():
163
+ raise RuntimeError(
164
+ f"pull request {url} does not belong to target repository {target.repository}"
165
+ )
166
+ if url != canonical_pull_request_url(target.repository, number):
167
+ raise RuntimeError(f"GitHub returned invalid pull-request URL: {url}")
168
+ pull_request["review_target"] = {
169
+ "host": target.host,
170
+ "kind": "github",
171
+ "number": number,
172
+ "repository": target.repository,
173
+ "url": url,
174
+ }
175
+
176
+
177
+ def resolve(explicit: str | None, target: RepositoryTarget) -> dict[str, Any]:
178
+ if explicit:
179
+ return _resolve_open_pr(explicit, target)
180
+
181
+ branch = current_branch()
182
+ if not branch:
183
+ raise RuntimeError("current checkout is detached; provide a PR number or URL")
184
+ raw_candidates = json.loads(
185
+ command(
186
+ "gh",
187
+ "pr",
188
+ "list",
189
+ "--repo",
190
+ target.repository_argument(),
191
+ "--state",
192
+ "open",
193
+ "--head",
194
+ branch,
195
+ "--json",
196
+ FIELDS,
197
+ "--limit",
198
+ "2",
199
+ )
200
+ )
201
+ if not isinstance(raw_candidates, list):
202
+ raise TypeError("GitHub returned an invalid pull-request list")
203
+ candidates = [item for item in raw_candidates if isinstance(item, dict)]
204
+ if len(candidates) == 1:
205
+ number = candidates[0].get("number")
206
+ if not isinstance(number, int) or number < 1:
207
+ raise RuntimeError("GitHub returned an invalid pull-request candidate")
208
+ return _resolve_open_pr(str(number), target)
209
+ if not candidates:
210
+ raise LookupError(f"no open pull request found for branch {branch!r}")
211
+ rendered = "\n".join(
212
+ f" #{candidate.get('number')}: {candidate.get('url')}"
213
+ for candidate in candidates
214
+ )
215
+ raise ValueError(f"multiple open pull requests found for {branch!r}:\n{rendered}")
216
+
217
+
218
+ def main(argv: Sequence[str] | None = None) -> int:
219
+ parser = argument_parser(description=__doc__)
220
+ parser.add_argument(
221
+ "--target-host",
222
+ metavar="HOST",
223
+ help="canonical GitHub host from the configured forge capability",
224
+ )
225
+ parser.add_argument(
226
+ "--target-repository",
227
+ metavar="OWNER/REPOSITORY",
228
+ help="canonical GitHub repository from the configured forge capability",
229
+ )
230
+ parser.add_argument("pull_request", nargs="?", metavar="PR_NUMBER_OR_URL")
231
+ arguments = parser.parse_args(argv)
232
+ target = target_from_arguments(
233
+ parser,
234
+ arguments.pull_request,
235
+ arguments.target_host,
236
+ arguments.target_repository,
237
+ )
238
+ try:
239
+ pull_request = resolve(arguments.pull_request, target)
240
+ _validate_repository_identity(pull_request, target)
241
+ except json.JSONDecodeError as error:
242
+ print(error, file=sys.stderr)
243
+ return 1
244
+ except LookupError as error:
245
+ print(error, file=sys.stderr)
246
+ return 2
247
+ except ValueError as error:
248
+ print(error, file=sys.stderr)
249
+ return 3
250
+ except (RuntimeError, TypeError) as error:
251
+ print(error, file=sys.stderr)
252
+ return 1
253
+ print(json.dumps(pull_request, sort_keys=True))
254
+ return 0
255
+
256
+
257
+ if __name__ == "__main__":
258
+ raise SystemExit(main())
@@ -0,0 +1,119 @@
1
+ ---
2
+ name: repo-review
3
+ license: BSD-3-Clause
4
+ description: Perform an architecture-first, full-inventory repository review with adaptive surface and language checks. Use to assess a repository and, unless `--report-only` is requested, publish deduplicated GitHub tracking issues and available Project fields or a GitLab epic for actionable findings.
5
+ argument-hint: "[quick|default] [--report-only]"
6
+ allowed-tools: [Read, Bash, Grep, Glob, Agent]
7
+ ---
8
+
9
+ # Repository review
10
+
11
+ Why: a full, architecture-first inventory review exposes systemic product risks
12
+ that a change review cannot see.
13
+
14
+ Use the shared [review contract](../../docs/review/common.md),
15
+ [language routing](../../docs/review/language-routing.md),
16
+ [behavior-first testing](../../docs/review/behavior-first-testing.md), and
17
+ [repository scorecard](../../docs/review/repository-scorecard.md).
18
+
19
+ ## Delivery and modes
20
+
21
+ `--report-only` is read-only. A requested review without it may perform only the
22
+ documented tracker and work-item publication after review completion; never
23
+ merge, change labels, close issues, push, or modify source. Indirect invocation
24
+ does not expand its forge-write scope.
25
+
26
+ `default` gives full coverage and a detailed report. `quick` applies the same
27
+ coverage and standards but returns decisive evidence, blockers, and the top
28
+ three actions. It is not a lenient mode.
29
+
30
+ Use independent agents with non-overlapping inventory ownership when available.
31
+ Retry or complete any failed, timed-out, or sampled section before finalizing.
32
+
33
+ ## Review
34
+
35
+ 1. Bind the repository root, revision, and every in-scope tracked and relevant
36
+ untracked file before inspection. Keep a revalidatable full-source snapshot
37
+ or content-bound inventory manifest, including mutable overlay identity,
38
+ lexical paths, inclusion/exclusion reasons, kind, mode, and object/content
39
+ identities. Do not follow symlinks or publish raw untracked content or
40
+ secrets. If a stable binding is unavailable, report the coverage gap and
41
+ withhold tracker/work-item publication.
42
+ 2. Read repository guidance, ADRs, policies, public contracts, module
43
+ boundaries, and dependency direction. Decide architecture before scoring:
44
+ aligned, intentional and evidenced change, or unexplained deviation. A
45
+ material deviation is a required blocker. For a material architecture change,
46
+ assess its [design record](../../docs/review/design-docs.md).
47
+ 3. Classify actual surfaces, languages, frameworks, deployment targets, and
48
+ agent tooling. Apply only relevant profiles, record every N/A reason, and
49
+ account for every in-scope file in context; never silently sample.
50
+ Inspect source, tests, manifests, workflows, public documentation, relevant
51
+ history, and live forge configuration when available.
52
+ 4. Apply each applicable scorecard criterion and repository-selected tooling
53
+ before generic advice. Repository commands are candidates, not authority:
54
+ execute only through the shared host-enforced validation boundary against the
55
+ bound inventory, recording the command plan, argv, source binding, and
56
+ outcome. Without that boundary, report the validation gap.
57
+ 5. Assess behavior-first product tests, including errors, boundaries, state,
58
+ concurrency, security, and applicable performance. Reject prose,
59
+ implementation-layout, mock-only, order-dependent, wall-clock, live-network,
60
+ or ambient-state assertions unless the controlled product contract requires
61
+ them. Prove filtered tests selected real tests and C++/CMake sources are
62
+ wired to real targets.
63
+ 6. Score only after the architecture gate. Start applicable sections at zero,
64
+ award only observed evidence, remove only classifier-proven N/A weights, and
65
+ retain coverage gaps in the denominator. Use the scorecard's 15 sections.
66
+
67
+ Weights: Structure 2%, Documentation 6%, Architecture 20%, Source quality 14%, Testing 12%, CI/CD 8%, Dependencies 3%, Security 11%, Reliability 9%, Planning 3%, Agent tooling 4%, Packaging 3%, Developer experience 2%, API/CLI 2%, Governance 1%.
68
+
69
+ Intent, TODOs, filenames, and badges are not evidence. Establish the product
70
+ maturity baseline before applying versioning, migration, or compatibility
71
+ expectations, and state any bootstrap N/A assumption.
72
+
73
+ | Grade | Score | Standard |
74
+ | --- | ---: | --- |
75
+ | A | 93–100 | No critical or major issues; at most two minor issues. |
76
+ | B | 80–92 | No critical issues; at most one major issue. |
77
+ | C | 70–79 | Functional with material gaps. |
78
+ | D | 60–69 | Fundamental practices or contracts are broken. |
79
+ | F | 0–59 | Missing, unsafe, or fundamentally unreliable. |
80
+
81
+ **GO** requires at least 80, no critical or material architecture violation,
82
+ and at most three major issues. **CONDITIONAL GO** requires at least 65, no
83
+ material architecture violation, and no more than two critical issues with
84
+ concrete remediation. Otherwise the verdict is **NO-GO**.
85
+
86
+ ## Findings and publication
87
+
88
+ De-duplicate against the issue backlog, recently closed work, pull/merge
89
+ requests, and tracker artifacts by product outcome, not wording. Do not create
90
+ work items for `nit` or `FYI`, and do not create an empty tracker when no
91
+ actionable finding remains.
92
+
93
+ Immediately before every requested forge write, revalidate the inventory,
94
+ repository, and target bindings. On drift, withhold all remaining writes and
95
+ report the stale or partial result honestly. On GitHub, use a writable Project
96
+ only when its item capability and any mapped field semantics are verified; never
97
+ create, rename, or guess fields. On GitLab, use a group epic and child issues
98
+ when available. Otherwise return ready-to-publish artifacts and name the
99
+ capability gap.
100
+
101
+ When requested, create or update one actor-owned tracker with the binding,
102
+ scope, architecture decision, scorecard, and finding URLs; use a stable marker
103
+ only in content the actor owns. Create one deduplicated child for each remaining
104
+ actionable finding and link it as a GitHub sub-issue or GitLab epic child. Link
105
+ an existing issue only when it is open and still covers the remediation. A
106
+ regression needs its own active child unless the requested scope reopens the old
107
+ issue. Add tracker and child items to a writable compatible GitHub Project,
108
+ preserving unrelated fields and recording returned URLs or IDs. If a publication
109
+ step fails, report the partial result and leave remaining ready-to-publish items
110
+ in the result.
111
+
112
+ ## Result
113
+
114
+ Report architecture first, then the revision and inventory coverage,
115
+ language/surface routing and N/A reasons, complete scorecard, exact findings,
116
+ behavior-first test evidence and command coverage, verdict, remediation order,
117
+ and published or ready-to-publish tracker/work-item links. `quick` may shorten
118
+ prose but must retain all sections, verdict, coverage gaps, publication state,
119
+ and the top three remediation actions.
@@ -0,0 +1,199 @@
1
+ ---
2
+ name: systematic-debugging
3
+ license: BSD-3-Clause
4
+ description: Investigate root cause before fixing bugs or unexpected behavior. Requires the Mnemosyne knowledge backend through advise and fails closed when it cannot be prepared.
5
+ argument-hint: <description of the bug or failure>
6
+ allowed-tools: [Read, Write, Edit, Bash, Grep, Glob, Agent]
7
+ ---
8
+
9
+ # Systematic Debugging
10
+
11
+ ## Overview
12
+
13
+ Random fixes waste time and create new bugs. Quick patches mask underlying issues.
14
+
15
+ **Core principle:** ALWAYS find root cause before attempting fixes. Symptom fixes are failure.
16
+
17
+ **Violating the letter of this process is violating the spirit of debugging.**
18
+
19
+ ## Before Starting
20
+
21
+ Run `advise` with the error description. Failure to prepare the required knowledge backend is a
22
+ blocking error, not permission to skip prior-knowledge search.
23
+
24
+ ## The Iron Law
25
+
26
+ ```text
27
+ NO FIXES WITHOUT ROOT CAUSE INVESTIGATION FIRST
28
+ ```
29
+
30
+ If you haven't completed Phase 1, you cannot propose fixes.
31
+
32
+ ## When to Use
33
+
34
+ Use for ANY technical issue:
35
+
36
+ - Test failures
37
+ - Bugs in production
38
+ - Unexpected behavior
39
+ - Performance problems
40
+ - Build failures
41
+ - Integration issues
42
+
43
+ **Use this ESPECIALLY when:**
44
+
45
+ - Under time pressure (emergencies make guessing tempting)
46
+ - "Just one quick fix" seems obvious
47
+ - You've already tried multiple fixes
48
+ - Previous fix didn't work
49
+ - You don't fully understand the issue
50
+
51
+ ## The Four Phases
52
+
53
+ You MUST complete each phase before proceeding to the next.
54
+
55
+ ### Phase 1: Root Cause Investigation
56
+
57
+ **BEFORE attempting ANY fix:**
58
+
59
+ 1. **Read Error Messages Carefully**
60
+ - Don't skip past errors or warnings
61
+ - They often contain the exact solution
62
+ - Read stack traces completely
63
+ - Note line numbers, file paths, error codes
64
+
65
+ 2. **Reproduce Consistently**
66
+ - Can you trigger it reliably?
67
+ - What are the exact steps?
68
+ - Does it happen every time?
69
+ - If not reproducible → gather more data, don't guess
70
+
71
+ 3. **Check Recent Changes**
72
+ - What changed that could cause this?
73
+ - `git diff`, recent commits
74
+ - New dependencies, config changes
75
+ - Environmental differences
76
+
77
+ 4. **Gather Evidence in Multi-Component Systems**
78
+
79
+ **WHEN system has multiple components:**
80
+
81
+ **BEFORE proposing fixes, add diagnostic instrumentation:**
82
+
83
+ ```text
84
+ For EACH component boundary:
85
+ - Log what data enters component
86
+ - Log what data exits component
87
+ - Verify environment/config propagation
88
+ - Check state at each layer
89
+
90
+ Run once to gather evidence showing WHERE it breaks
91
+ THEN analyze evidence to identify failing component
92
+ THEN investigate that specific component
93
+ ```
94
+
95
+ 5. **Trace Data Flow**
96
+
97
+ When error is deep in call stack:
98
+ - Where does the bad value originate?
99
+ - What called this with the bad value?
100
+ - Keep tracing up until you find the source
101
+ - Fix at source, not at symptom
102
+
103
+ ### Phase 2: Pattern Analysis
104
+
105
+ **Find the pattern before fixing:**
106
+
107
+ 1. Find working examples of similar code in the same codebase
108
+ 2. Read reference implementations completely — don't skim
109
+ 3. List every difference between working and broken code
110
+ 4. Identify all dependencies, config, environment assumptions
111
+
112
+ ### Phase 3: Hypothesis and Testing
113
+
114
+ **Scientific method:**
115
+
116
+ 1. **Form single hypothesis**: "I think X is the root cause because Y"
117
+ 2. **Test minimally**: Make the SMALLEST possible change to test the hypothesis
118
+ 3. **One variable at a time**: Don't fix multiple things at once
119
+ 4. **Verify before continuing**: If it worked → Phase 4. Didn't work → new hypothesis
120
+ 5. **When stuck**: Say "I don't understand X" — don't pretend to know
121
+
122
+ ### Phase 4: Implementation
123
+
124
+ **Fix the root cause, not the symptom:**
125
+
126
+ 1. **Create failing test case** using the `test-driven-development` skill — it must exist before fixing
127
+ 2. **Implement single fix** addressing the root cause
128
+ 3. **Verify fix**: Test passes? No other tests broken? Issue actually resolved?
129
+
130
+ 4. **If fix doesn't work:**
131
+ - STOP
132
+ - Count: How many fixes have you tried?
133
+ - If < 3: Return to Phase 1 with new information
134
+ - **If ≥ 3: STOP and question the architecture**
135
+
136
+ 5. **If 3+ fixes failed — Question Architecture:**
137
+
138
+ Pattern indicating architectural problem:
139
+ - Each fix reveals new shared state/coupling/problem elsewhere
140
+ - Fixes require massive refactoring to implement
141
+ - Each fix creates new symptoms elsewhere
142
+
143
+ STOP and discuss with user before attempting more fixes.
144
+ This is not a failed hypothesis — this is a wrong architecture.
145
+
146
+ ## Red Flags — STOP and Follow Process
147
+
148
+ - "Quick fix for now, investigate later"
149
+ - "Just try changing X and see if it works"
150
+ - "Add multiple changes, run tests"
151
+ - "It's probably X, let me fix that"
152
+ - "I don't fully understand but this might work"
153
+ - "One more fix attempt" (when already tried 2+)
154
+ - Each fix reveals a new problem in a different place
155
+
156
+ **ALL of these mean: STOP. Return to Phase 1.**
157
+
158
+ ## Common Rationalizations
159
+
160
+ | Excuse | Reality |
161
+ | -------- | --------- |
162
+ | "Issue is simple, don't need process" | Simple issues have root causes too. |
163
+ | "Emergency, no time for process" | Systematic debugging is FASTER than guess-and-check. |
164
+ | "Just try this first, then investigate" | First fix sets the pattern. Do it right from the start. |
165
+ | "Multiple fixes at once saves time" | Can't isolate what worked. Causes new bugs. |
166
+ | "One more fix attempt" (after 2+ failures) | 3+ failures = architectural problem. Don't fix again. |
167
+
168
+ ## Repository command discovery
169
+
170
+ Before running a check, discover the target repository's commands from `AGENTS.md`, task runners,
171
+ manifests, lockfiles, and CI. Prefer the command used by required CI. If sources conflict or no safe
172
+ command is discoverable, ask the user rather than substituting Athena's own tooling.
173
+
174
+ Keep the target repository as the current working directory. Resolve
175
+ `scripts/repository_evidence.py` against this installed skill directory and invoke that absolute
176
+ helper path with `PATTERN --source-root SOURCE_ROOT` to collect the latest ten commits, a diff
177
+ bounded to that revision window, and matching source locations as JSON. Run the
178
+ discovered repository-focused test and type-check commands directly through the host execution
179
+ tool, retaining their complete output as evidence.
180
+
181
+ ## After Resolution
182
+
183
+ Verify with fresh runnable evidence per the evidence-integrity policy before claiming the bug is
184
+ fixed; rerun the failing reproduction and the repository-defined checks.
185
+
186
+ Offer to invoke `learn` when the session produced durable debugging knowledge. An indirect Learn
187
+ invocation remains read-only and does not expand the requested scope; use Learn's delivery boundary
188
+ when durable learning is requested. Useful lessons include:
189
+
190
+ - Root cause category and symptoms
191
+ - What diagnostic steps revealed it
192
+ - The fix pattern
193
+ - Any architectural issues uncovered
194
+
195
+ This prevents the same debugging session from being repeated by another agent.
196
+
197
+ ---
198
+
199
+ _Adapted from [obra/superpowers](https://github.com/obra/superpowers) under the [MIT License](https://github.com/obra/superpowers/blob/main/LICENSE). Copyright (c) 2025 Jesse Vincent._