=== home/.hermes/config.yaml ===
mcp_servers:
  gortex:
    command: gortex
    args: [mcp]
    connect_timeout: 60
    timeout: 120
platform_toolsets:
  cli: true
=== home/.hermes/skills/analysis/gortex-architecture-review/SKILL.md ===
---
name: gortex-architecture-review
description: "Graph-grounded architectural read of a repo."
version: 1.0.0
metadata:
  hermes:
    tags: [code-intelligence, mcp, architecture-review]
    category: analysis
    platforms: [linux, macos, windows]
    related_skills: [gortex]
---
# Architecture Review with Gortex

Use this when the user wants a graph-grounded architectural read of a repo / system — "what does the architecture actually look like, where is the design under stress, what should we refactor before it breaks." Distinct from `/gortex-quality-audit` (which produces a punch list) — this produces a *narrative* with diagrams.

## Workflow (do not skip steps)

```
1. graph_stats                                                         -> Orient
2. get_repo_outline                                                    -> Narrative single-call overview
3. get_communities                                                     -> Functional clusters via Louvain — the de facto module boundaries
4. get_processes                                                       -> Discovered execution flows — the de facto use cases
5. analyze({kind: "components"})                                       -> UI component tree (when applicable) — render hierarchy fan-in/out
6. analyze({kind: "routes"})                                           -> The API surface (HTTP / gRPC / GraphQL / WS / topic)
7. analyze({kind: "models"})                                           -> ORM models → tables — the data surface
8. analyze({kind: "k8s_resources"}) + analyze({kind: "images"})        -> Deployment topology (when applicable)
9. analyze({kind: "hotspots", top: 20})                                -> Symbols under coupling stress
10. analyze({kind: "cycles"})                                          -> Architectural cycles (Tarjan SCC with severity)
11. analyze({kind: "would_create_cycle", from: "<A>", to: "<B>"})      -> Pre-flight before proposing a new dep in the review
12. analyze({kind: "cross_repo", base_kind: "calls"})                  -> Inter-repo coupling (when multi-repo)
13. contracts({action: "list"})                                        -> Provider-side wire surface
14. contracts({action: "check"})                                       -> Provider ↔ consumer match across repos
15. get_class_hierarchy({id: "<key interface>"})                       -> Inheritance / implementation depth on load-bearing interfaces
16. get_dependencies / get_dependents at the community / file level    -> Layering walk — does the dependency direction match the intent?
17. analyze({kind: "pubsub"}) + analyze({kind: "channel_ops"})         -> Async + concurrency topology
18. analyze({kind: "field_writers"}) + analyze({kind: "race_writes"})  -> Shared-state hotspots — the architecture's hidden mutable surface
19. analyze({kind: "stale_code", older_than: 365})                     -> Strata that haven't moved in a year — likely the "stable core"
20. analyze({kind: "ownership"})                                       -> Per-author footprint per community — Conway's law check
21. export_context({format: "markdown",                                -> The review deliverable
                    sections: ["overview", "modules", "processes",
                               "wire_surface", "data_surface", "concurrency",
                               "cross_repo", "stress_points", "recommendations"]})
```

## What the architecture review answers

| Question                                                       | Tool                                                                          |
| -------------------------------------------------------------- | ----------------------------------------------------------------------------- |
| **What are the de facto modules?** (not what's claimed)        | `get_communities` — Louvain finds the actual cluster structure              |
| **What are the de facto use cases?**                            | `get_processes` — discovered execution flows                                 |
| **Where is the design under coupling stress?**                  | `analyze kind=hotspots top=20` — fan-in + fan-out + community crossings     |
| **Are there cycles?**                                            | `analyze kind=cycles` — Tarjan SCC with severity classification             |
| **Does the dependency direction match the intent?**             | Walk `get_dependencies` / `get_dependents` at the community / file level |
| **What is the wire surface?**                                    | `analyze kind=routes` + `contracts({action: list})`                            |
| **What is the data surface?**                                    | `analyze kind=models` + `analyze kind=orphan_tables` / `unreferenced_tables` |
| **What is the deployment topology?**                            | `analyze kind=k8s_resources` + `analyze kind=images`                            |
| **Where does shared state live?**                                | `analyze kind=field_writers` + `race_writes` — the mutable-state surface |
| **Where does the async + pub/sub topology lead?**                | `analyze kind=pubsub` + `channel_ops` + `goroutine_spawns` + `unclosed_channels`   |
| **Does the team's structure match the code's structure? (Conway)** | `analyze kind=ownership` projected onto `get_communities` — author overlap per community |
| **Where is the stable core?**                                    | `analyze kind=stale_code older_than=365` — strata that haven't moved in a year |
| **Where would adding a new edge create a cycle?**                | `analyze kind=would_create_cycle from=A to=B` — pre-flight for proposed deps |

## Stress points

Stress points are graph signals that the design is fighting itself. Surface them explicitly in the review:

- **Cyclic dependency in the load-bearing layer** — `analyze kind=cycles severity=severe` ∩ a community with high `hotspots` rank
- **Architecture-spanning hotspot** — a symbol whose `community_crossings` is > 50% of its fan-in (the symbol "is" the integration layer, often by accident)
- **Mutable shared field reachable from a goroutine without a lock** — `analyze kind=race_writes` on a high-fan-in field
- **Contract orphan** — `contracts({action: check})` returns a provider with no consumer or a consumer with no provider, especially cross-repo
- **Inverted dependency** — a "lower" layer importing a "higher" one. Walk `get_dependencies` from each community to confirm directionality
- **Deployment ↔ code drift** — `analyze kind=images role=base ref=latest` (unpinned), `k8s_resources k8s_kind=ConfigMap` orphans

## Multi-repo architecture

When `get_active_project` shows >1 member:

- `analyze kind=cross_repo base_kind=calls` — typed cross-repo edges; the count is the *contract surface* the architecture must hold stable
- `contracts({action: check})` partitioned by repo pair — orphan providers / consumers across the boundary
- For each cross-repo edge, `get_test_targets` returns the cross-repo tests that exercise it; the absence of those tests is itself an architecture finding

## Review deliverable

The output of an architecture review is a markdown narrative with embedded diagrams (Mermaid / DOT) generated from the graph:

```
export_context({
  task: "architecture review of <repo / project>",
  format: "markdown",
  sections: [...]
})
```

The packet rides the same surface as `/gortex-quality-audit` but is structured as a *narrative* (modules, processes, surfaces, stress, recommendations) rather than a flat punch list.

## Checklist

- `graph_stats` + `get_active_project` before any analyzer
- `get_repo_outline` for the narrative skeleton
- `get_communities` + `get_processes` are the architectural primitives — pin the review on these
- `analyze kind=hotspots` + `cycles` for stress points
- Wire surface = `analyze kind=routes` + `contracts list`; data surface = `analyze kind=models` + `orphan_tables`; deployment = `k8s_resources` + `images`
- Concurrency / async = `pubsub` + `channel_ops` + `goroutine_spawns` + `race_writes` + `unclosed_channels`
- Multi-repo: `cross_repo` + `contracts check` partitioned by repo pair
- `ownership` ∩ `communities` for Conway's law alignment
- `export_context format=markdown` for the narrative
- `store_memory({kind: "decision"})` for every architectural decision the review surfaces — the next agent inherits the rationale
=== home/.hermes/skills/analysis/gortex-co-change/SKILL.md ===
---
name: gortex-co-change
description: "Find what changes together and hidden coupling."
version: 1.0.0
metadata:
  hermes:
    tags: [code-intelligence, mcp, co-change]
    category: analysis
    platforms: [linux, macos, windows]
    related_skills: [gortex]
---
# Co-change Analysis with Gortex

Use this when the user asks "what tends to change together with this?" — for refactor planning, ownership decisions, ADR rationale, or to find hidden coupling the graph's static edges don't capture (two files that always co-modify but have no import / call edge between them). Built on git blame + history + the symbol-churn surface `get_symbol_history` + the existing graph dependents view.

## Workflow (do not skip steps)

```
1. graph_stats                                                         -> Orient
2. gortex enrich blame all (CLI; once per session)                     -> Stamp last_authored / last_commit_at on every node
3. search_symbols({query: "<target symbol or file>"})                  -> Resolve the anchor
4. get_recent_changes({since_ts: <N months ago>})                       -> Per-file change frequency baseline
5. get_symbol_history                                                  -> Symbol-level churn this session (live hotspot signal)
6. analyze({kind: "blame", path_prefix: "<dir>/"})                      -> Per-symbol last-author rollup; cross-ref with churn
7. analyze({kind: "ownership", path_prefix: "<dir>/"})                  -> Per-author symbol / file counts — proxy for who-touches-it-most
8. analyze({kind: "hotspots", path_prefix: "<dir>/"})                   -> Coupling-by-fan-in/out — graph-level vs git-level coupling
9. get_dependents({id: "<anchor>", depth: 2})                           -> Static dependent set (graph coupling)
10. find_clones({path_prefix: "<dir>/", dead_only: false})              -> Near-duplicate functions — copy-paste co-evolution signal
11. detect_changes({scope: "all"})                                      -> If a diff already exists, project it onto the graph
12. diff_context({scope: "all"})                                        -> Per-file risk + per-symbol callers/callees overlay
```

## Co-change vs graph coupling

The two signals are complementary:

| Signal                                  | What it tells you                                                              | Tool                                                                |
| --------------------------------------- | ------------------------------------------------------------------------------ | ------------------------------------------------------------------- |
| **Graph coupling**                      | Static structure: who imports / calls / extends / implements whom              | `get_dependents` / `get_dependencies` / `analyze kind=hotspots`       |
| **Co-change coupling**                  | Historical / temporal: who tends to co-modify even without a static edge       | `get_recent_changes` + `analyze kind=blame` + `get_symbol_history`       |
| **Both align**                          | Healthy module — the structure reflects how the team edits it                   | Use either; co-change adds confidence                                |
| **Co-change > graph**                   | **Hidden coupling** — two files always co-modify but have no graph edge. Smell: missing abstraction, parallel hierarchies, copy-paste | `find_clones` is the corroborating evidence                          |
| **Graph > co-change**                   | Dead coupling — graph edge exists but the files don't co-evolve. Often safe to break | `analyze kind=dead_code` may flag the dependent              |

## Driving a refactor with co-change

1. Pick the anchor (file / symbol the user wants to refactor)
2. Compute co-change set: symbols whose `meta.last_authored` clusters with the anchor's, AND whose churn (`get_symbol_history` + `get_recent_changes`) overlaps the anchor's
3. Compute graph-coupling set: `get_dependents` + `get_dependencies` (depth 2)
4. Diff the two sets — the **co-change ∖ graph** delta is the hidden-coupling list and the highest ROI for "extract a shared abstraction"
5. Pair with `find_clones` on the co-change set — clone clusters that span the boundary are the copy-paste hidden coupling
6. `store_memory({kind: "convention", body: "X always changes with Y because Z"})` so the next agent inherits the invariant rather than re-discovering it

## Ownership signal

`analyze kind=ownership` projected onto the co-change set tells you who-pings-who for review. Two files that co-modify under different primary authors is a *coordination* signal — the team is implicitly maintaining a contract neither file's CODEOWNERS captures.

## Crossing the repo boundary

When the anchor lives in a multi-repo project (`get_active_project` shows >1 member):

- `analyze kind=cross_repo base_kind=calls repo=<anchor's repo>` gives the static cross-repo coupling
- For the temporal signal across repos, run `analyze kind=blame` per repo and look for authors who appear in both — that's the implicit cross-repo co-change channel

## Checklist

- `gortex enrich blame all` (CLI) at least once per session — the temporal analyzers need `meta.last_authored`
- `get_recent_changes` + `get_symbol_history` for the live churn signal
- `analyze kind=blame` + `ownership` for the per-symbol / per-author rollup
- `get_dependents` (graph coupling) **and** the churn surface (temporal coupling) — diff them; the delta is the hidden coupling
- `find_clones` to corroborate copy-paste-style hidden coupling
- `analyze kind=hotspots` for the graph-side coupling-by-fan-in/out view
- For multi-repo work: `analyze kind=cross_repo` on the static side + per-repo blame on the temporal side
- `store_memory({kind: "convention"})` when you discover a stable co-change relationship — saves the next agent from re-deriving it
=== home/.hermes/skills/analysis/gortex-episode-replay/SKILL.md ===
---
name: gortex-episode-replay
description: "Reconstruct what changed in a window (postmortem, release, PR)."
version: 1.0.0
metadata:
  hermes:
    tags: [code-intelligence, mcp, episode-replay]
    category: analysis
    platforms: [linux, macos, windows]
    related_skills: [gortex]
---
# Episode Replay with Gortex (timeline reconstruction)

Use this when the user wants to reconstruct what happened in a specific window — for a postmortem, a "what shipped between v1.2 and v1.3", a "what did Alice touch last week", or "walk me through what this PR actually changed". Pairs git history with the graph so the replay shows blast radius alongside the diff, not just the file list.

## Workflow (do not skip steps)

```
1. graph_stats                                                         -> Orient
2. gortex enrich blame all (CLI; once per session)                     -> Stamp meta.last_authored / meta.last_commit_at / meta.added_in
3. analyze({kind: "releases"})                                         -> Tag boundaries → maps a tag to the symbols added in it
4. analyze({kind: "blame", path_prefix: "<scope>/"})                   -> Per-symbol last-author rollup
5. get_recent_changes({since_ts: <window-start>})                      -> Symbols touched in the window
6. analyze({kind: "ownership", path_prefix: "<scope>/"})               -> Per-author symbol/file counts in the window
7. detect_changes({scope: "<all|staged|since-tag>"})                   -> The change-set's graph projection
8. diff_context({scope: "<same>"})                                     -> Graph-enriched diff: callers, callees, communities, per-file risk
9. For each material symbol in the window:
     get_symbol_history({id: "<id>"})                                  -> Per-session edit count (regression hotspot)
     explain_change_impact({ids: "<id>"})                              -> Blast radius of that one change
10. query_notes({since: <window-start>})                               -> Session decisions / bug notes recorded in the window
11. query_memories({tag: "decision", since: <window-start>})           -> Cross-session decisions in the window
12. export_context({format: "markdown",                                -> Hand the replay packet to PR / Slack / wiki
                    sections: ["changes", "impact", "decisions"]})
```

## Replay shapes

| Goal                                                  | Driver query                                                             |
| ----------------------------------------------------- | ------------------------------------------------------------------------ |
| "What shipped in v1.3?"                               | `analyze kind=releases` → symbols where `meta.added_in == "v1.3"`; then `diff_context` between v1.2..v1.3 tags |
| "What did Alice change last week?"                    | `analyze kind=ownership` filtered to Alice → cross-ref with `get_recent_changes since_ts=now-7d` |
| "What did this incident touch?"                       | Start with `/gortex-incident-investigation` → use its root-cause symbol set as the seed; replay walks `explain_change_impact` on each |
| "Postmortem narrative for a one-day window"           | Full chain above + `query_notes` + `query_memories` so commentary rides next to code in the markdown |
| "What did this PR actually change beyond the diff?"   | `detect_changes scope=staged` + `diff_context` + `explain_change_impact` per symbol → second-order blast that the literal diff hides |

## Reading the replay output

- `detect_changes` is the change-set's *graph* projection — every node a touched file owns, plus every directly-affected dependent. It is broader than `git diff --stat` because it captures symbols whose *meaning* changed, not just files whose *bytes* changed.
- `diff_context` adds the callers / callees / community / processes / per-file risk overlay. This is the artifact you embed in the PR description or postmortem.
- Per-symbol `explain_change_impact` surfaces the *second-order* blast (what depends on the things that changed). The replay is incomplete without this for any change touching a fan-in-heavy symbol.

## Crossing the merge boundary

For replays that span a merge or rebase, prefer `detect_changes scope=since-tag` over a literal commit-range diff. The graph projection survives rebases that the literal diff doesn't.

When the replay is across a release boundary, `analyze kind=releases` is the bridge: it stamps `meta.added_in` from git tags onto file nodes, so "symbols introduced in v1.3" is one filter away.

## Annotating the replay with prior context

`query_notes` and `query_memories` are how prior session decisions and cross-session invariants enter the timeline:

- `query_notes({since: <window-start>})` — every session note authored in the window; tag-filterable (`decision` / `bug` / `gotcha` / `follow-up`).
- `query_memories({tag: "decision", since: <window-start>})` — durable cross-session decisions stamped in the window.

These two surfaces are how a postmortem reads as a story rather than a list of file paths.

## Checklist

- `gortex enrich blame all` (CLI) at least once per session — the blame / releases / ownership analyzers need `meta.last_authored` to be populated
- `analyze kind=releases` for release-boundary replays
- `get_recent_changes` + `analyze kind=ownership` for the window-scoped change set
- `detect_changes` + `diff_context` to project the diff onto the graph
- `explain_change_impact` on every material symbol — second-order blast or it didn't happen
- `query_notes` + `query_memories` to ride commentary alongside the code timeline
- `export_context format=markdown` so the replay leaves the session as a shareable artifact
=== home/.hermes/skills/analysis/gortex-impact/SKILL.md ===
---
name: gortex-impact
description: "Assess what breaks if you change X before editing it."
version: 1.0.0
metadata:
  hermes:
    tags: [code-intelligence, mcp, impact]
    category: analysis
    platforms: [linux, macos, windows]
    related_skills: [gortex]
---
# Impact Analysis with Gortex

## Workflow

```
1. search_symbols({query: "X"})                                     -> Find the symbol ID
2. explain_change_impact({ids: "<id1>, <id2>"})                     -> Risk-tiered blast radius
3. get_dependents({id: "<symbol-id>", depth: 3})                    -> Detailed dependent tree
4. analyze({kind: "ownership", path_prefix: "<dir>/"})              -> Who owns this area (review pinging)
5. verify_change({id: "<id>", new_signature: "..."})                -> Check callers + interface implementors for signature-level breaks
6. contracts({action: "check"})                                     -> Cross-repo API breakage (HTTP/gRPC/GraphQL/topics)
7. analyze({kind: "would_create_cycle", from: "<a>", to: "<b>"})    -> Before adding a new dep
8. analyze({kind: "error_surface", path_prefix: "<dir>/"})          -> What error surface does this area produce — widening risk
9. get_test_targets({ids: ["<id1>", "<id2>"]})                      -> Tests to re-run (includes cross-repo)
10. analyze({kind: "coverage_gaps", path_prefix: "<dir>/"})         -> Undertested code in the change area — extra-risky refactor zones
11. check_guards({ids: ["<id1>"]})                                  -> Project guard rules from .gortex.yaml
12. flow_between({source_id, sink_id})                              -> Ranked dataflow paths between two symbols — catches consumers reached through helpers that get_dependents misses
13. taint_paths({source_pattern, sink_pattern})                     -> Pattern-driven dataflow sweep — every flow from a matching source to a matching sink
14. detect_changes({scope: "staged"})                               -> Pre-commit scope check
15. diff_context({scope: "staged"})                                 -> Graph-enriched diff for review
```

## Understanding Output

| Depth | Risk Level       | Meaning                  |
| ----- | ---------------- | ------------------------ |
| d=1   | **WILL BREAK**   | Direct callers/importers |
| d=2   | LIKELY AFFECTED  | Indirect dependencies    |
| d=3   | MAY NEED TESTING | Transitive effects       |

## Checklist

- search_symbols to find exact symbol IDs
- explain_change_impact with all symbols you plan to change
- Review risk level (LOW/MEDIUM/HIGH/CRITICAL)
- Check by_depth: d=1 items WILL BREAK
- Note affected_processes and affected_communities
- analyze kind=ownership path_prefix=<dir>/ — who should review (pinging policy without CODEOWNERS)
- verify_change for every signature change (catches contract violations across repos)
- contracts action=check when changing HTTP routes, gRPC methods, topics, env contracts
- analyze kind=error_surface path_prefix=<dir>/ — confirm the change does not widen the error surface
- analyze kind=coverage_gaps path_prefix=<dir>/ — areas with weak coverage need extra scrutiny
- check_guards so team conventions from .gortex.yaml block bad changes early
- get_test_targets to see which test files need re-running
- Before commit: detect_changes to verify scope, diff_context for graph-enriched review
=== home/.hermes/skills/analysis/gortex-pr-review-agent/SKILL.md ===
---
name: gortex-pr-review-agent
description: "Coding-agent review verdict via the gortex review verb."
version: 1.0.0
metadata:
  hermes:
    tags: [code-intelligence, mcp, pr-review-agent]
    category: analysis
    platforms: [linux, macos, windows]
    related_skills: [gortex]
---
# PR Review as a sub-agent (shell the review verb)

Use this when you are a coding agent (Codex / Claude Code / any CLI agent) and need a graph-grounded review verdict on a pending change **without** hand-walking the ten review gates yourself. Instead of orchestrating `detect_changes` / `diff_context` / `verify_change` / … one call at a time, shell the `gortex review` verb once and act on its terse, machine-first output.

The review engine runs the deterministic correctness rulepack, grounds every finding to an exact `file:line`, optionally folds in LLM findings, gates by confidence / severity, and prints a verdict — all server-side. You consume the result.

## Run the verb

```bash
# Terse, prose-free summary an agent can parse (one-line verdict + compact findings + cost):
gortex review --audience agent

# The same review as structured JSON when you want to branch on fields programmatically:
gortex review --audience agent --format json
```

Select the changeset the same way a human would:

- `--scope unstaged|staged|all|compare` — which working-tree changes to review (default `unstaged`)
- `--base <ref>` — review everything since a base ref (shorthand for compare)
- `--diff <file>` (or `--diff -` for stdin) — review a pasted unified diff instead of git
- `--use-llm` — additionally fold in LLM-found findings (needs a configured provider)
- `--repo <path>` — the repo the daemon tracks (default: current directory)

A daemon that tracks the repo must be running (`gortex daemon status`). The verb relays to the daemon; you never start the engine yourself.

## Read the terse output

`--audience agent` prints exactly three things, no narrative:

```
VERDICT: block (1 critical, 2 error)
findings:
  internal/svc/handler.go:7 error go-inverted-err-check — inverted error check
  internal/svc/loop.go:12 warning go-loop-query-call — query in loop
cost: in=1234 out=456 cache_r=2000 cache_w=0 usd=0.012000 elapsed=4.2s
```

- **Line 1** is the verdict — `block` / `review` / `approve` — with the kept-finding severity histogram in parentheses.
- The **findings** block has one compact line per finding: `file:line severity rule — message`. Each is anchored to a real new-side line, so you can open the file at that line directly.
- The **cost** line is the per-review token + USD accounting.

## Act on the verdict

| Verdict | What to do |
| ------- | ---------- |
| `block`  | Do **not** merge / proceed. Fix every `critical` / `error` finding at its `file:line`, then re-run `gortex review --audience agent` until the verdict clears. |
| `review` | Address the `warning` findings or justify each one in the PR thread before merging. |
| `approve` | No blocking findings — proceed. |

When you want the full reasoning behind a verdict (per-file risk, contracts, guards, coverage), drop the `--audience agent` flag for the readable human packet, or use the `/gortex-pr-review` playbook to walk the ten gates by hand.

## Checklist

- `gortex daemon status` first — the verb needs a daemon tracking the repo
- `gortex review --audience agent` for the terse, parseable summary; add `--format json` to branch on fields
- Open each finding at its printed `file:line` — the anchor is exact, not approximate
- On `block`, fix and **re-run the verb** until it clears; do not merge a blocking verdict
- Escalate to the human packet (drop `--audience agent`) or `/gortex-pr-review` when you need the full gate-by-gate reasoning
=== home/.hermes/skills/analysis/gortex-pr-review/SKILL.md ===
---
name: gortex-pr-review
description: "Graph-grounded review of a pending change or PR."
version: 1.0.0
metadata:
  hermes:
    tags: [code-intelligence, mcp, pr-review]
    category: analysis
    platforms: [linux, macos, windows]
    related_skills: [gortex]
---
# PR Review with Gortex (graph-grounded change review)

Use this when the user wants a code-review pass on a pending change — local staged diff, a branch about to merge, or a PR they're reading. The review walks the diff through the graph so the comments are grounded in real callers / contracts / coverage / guards, not surface-level style nitpicks.

## Workflow (do not skip steps)

```
1. graph_stats                                                         -> Orient
2. detect_changes({scope: "staged"})                                   -> The change-set's graph projection (use "all" / "since-tag" as needed)
3. diff_context({scope: "staged"})                                     -> Graph-enriched diff: callers, callees, communities, processes, per-file risk
4. For each changed symbol:
     explain_change_impact({ids: "<id>"})                              -> Risk-tiered blast radius
     verify_change({id: "<id>", new_signature: "<post-change sig>"})   -> Catch interface / contract breaks
5. contracts({action: "check"})                                        -> Provider ↔ consumer match across repos (HTTP / gRPC / topics / env / OpenAPI)
6. check_guards({ids: [<changed-ids>]})                                -> Team conventions from .gortex.yaml
7. analyze({kind: "would_create_cycle", from: "<from>", to: "<to>"})   -> If the diff adds a new import edge, pre-flight it
8. analyze({kind: "coverage_gaps", path_prefix: "<changed dir>/",      -> Did the change touch undertested code?
            min_pct: 0, max_pct: 50})
9. get_untested_symbols({path_prefix: "<changed dir>/"})               -> Symbols still uncovered after the change
10. get_test_targets({ids: [<changed-ids>]})                           -> Tests to re-run (cross-repo aware)
11. find_clones({path_prefix: "<changed dir>/", dead_only: true})      -> Did the change leave dead duplicates behind?
12. analyze({kind: "error_surface", path_prefix: "<changed dir>/"})    -> Did the change widen what gets thrown?
13. analyze({kind: "cross_repo", base_kind: "calls"})                  -> Cross-repo blast (when multi-repo)
14. For high-risk changed symbols (d=1 in explain_change_impact):
     preview_edit({edit: <the diff as a WorkspaceEdit>})               -> Speculative apply on the shadow graph; reports broken_callers / broken_implementors
15. surface_memories({task: "review <PR title>", symbol_ids: [...]})   -> Cross-session invariants on the touched symbols
16. export_context({format: "markdown",                                -> Review packet for the PR thread
                    sections: ["scope", "impact", "contracts",
                               "guards", "coverage", "tests",
                               "recommendations"]})
```

## Review priorities

Walk every PR through these gates, in order. A failure at gate N is a blocker — do not move to gate N+1 until N is addressed:

| Gate | Tool                                            | Blocker if … |
| ---- | ----------------------------------------------- | ------------ |
| **1. Scope**                | `detect_changes`                      | Touches files / symbols the PR description doesn't mention |
| **2. Signature safety**     | `verify_change` per signature change  | Callers / implementors break |
| **3. Contract safety**      | `contracts({action: check})`          | Orphan providers / consumers; cross-repo wire drift |
| **4. Convention compliance** | `check_guards`                        | Project rules from `.gortex.yaml` violated |
| **5. Coupling sanity**      | `analyze kind=would_create_cycle`     | New import edge introduces a cycle |
| **6. Coverage hygiene**     | `analyze kind=coverage_gaps` + `get_untested_symbols` | Changed code is uncovered or under-covered |
| **7. Test discoverability** | `get_test_targets`                    | The PR adds a symbol with no covering test target |
| **8. Dead duplication**     | `find_clones dead_only=true`          | Refactor left a dead duplicate of live code |
| **9. Blast verification**   | `preview_edit` on high-risk changes   | Speculative apply reports `broken_callers` or `broken_implementors` that the diff doesn't address |
| **10. Memory check**        | `surface_memories` on touched symbols | Diff contradicts a pinned invariant / decision / gotcha |

## Cross-repo PRs

When the PR touches a multi-repo project:

- `detect_changes` only sees the local repo's changes; also run `contracts({action: check})` to flag wire-side breakage in consumer repos
- `analyze kind=cross_repo base_kind=calls repo=<changed repo>` lists every consumer call that crosses out of the changed repo
- `get_test_targets` returns the cross-repo tests that exercise the affected wire path — those tests must be on the reviewer's run list

## Review deliverable

Hand the PR author a structured comment block, not a stream-of-consciousness comment:

```markdown
## Review — <PR title>

### Scope
- ✅ / ⚠ — `detect_changes` matches the PR description
- Touched: N files, M symbols, K cross-repo edges

### Impact
- High-risk: <id> · d=1 blast — see `explain_change_impact`
- Medium: …

### Contracts
- ✅ / ❌ — `contracts({action: check})` summary

### Guards
- ✅ / ❌ — `check_guards` summary

### Coverage
- Uncovered after this PR: <symbol ids> (`get_untested_symbols`)
- Coverage gap deltas: …

### Tests
- `get_test_targets` recommends: …

### Speculative apply (for high-risk changes)
- `preview_edit` flagged: broken_callers=…, broken_implementors=…

### Recommendations
1. …
2. …
```

`export_context format=markdown sections=[...]` produces this skeleton automatically.

## When the diff is already on disk

If the diff is in the working tree (or a feature branch checked out), `detect_changes` + `diff_context` see it directly. If the user pasted in a unified diff or a GitHub URL, parse it into a synthetic `WorkspaceEdit` and drive `preview_edit` to get the speculative report — same blast / broken-callers / broken-implementors signal without touching disk.

## Checklist

- `index_health` — confirm Gortex sees the working tree (`graph_stats` for counts)
- `detect_changes` + `diff_context` produce the graph-grounded scope + per-file risk
- Walk the **10 gates** above in order; do not skip ahead on a blocker
- `contracts({action: check})` is **mandatory** for any PR that touches a provider symbol — the orphan check catches wire drift the diff doesn't
- `check_guards` runs the team's `.gortex.yaml` rules — these encode conventions a reviewer would otherwise have to remember
- For high-risk changes, `preview_edit` the diff as a `WorkspaceEdit` and read `broken_callers` / `broken_implementors`; the speculative report is the highest-confidence signal in the review
- `surface_memories` on the touched symbols before finalising the review — a diff that contradicts a pinned invariant is a blocker, not a nit
- `export_context format=markdown` for the structured review block; never hand the user a stream-of-consciousness comment
- Cross-repo PRs: `contracts({action: check})` + `analyze kind=cross_repo` + `get_test_targets` (cross-repo aware)
=== home/.hermes/skills/analysis/gortex-quality-audit/SKILL.md ===
---
name: gortex-quality-audit
description: "Repo-scale quality scan — dead code, hotspots, clones."
version: 1.0.0
metadata:
  hermes:
    tags: [code-intelligence, mcp, quality-audit]
    category: analysis
    platforms: [linux, macos, windows]
    related_skills: [gortex]
---
# Quality Audit with Gortex (repo health scan)

Use this when the user wants a structured pass over a repo / directory looking for code-quality issues at scale — dead code, hotspots, cycles, churn, todos, coverage gaps, clones, anti-pattern smells, configuration drift. The output is a prioritised punch list, not a verdict; each finding is grounded in a graph query the user can re-run.

## Workflow (do not skip steps)

```
1. graph_stats                                                         -> Orient
2. gortex enrich blame coverage releases all (CLI; once)               -> Stamp the metadata the temporal + coverage analyzers need
3. analyze({kind: "dead_code", path_prefix: "<dir>/"})                 -> Symbols with zero incoming edges (excludes entry points + tests)
4. find_clones({path_prefix: "<dir>/", dead_only: true})               -> Dead duplicates of live code — the segment-unique diagnostic
5. analyze({kind: "hotspots", path_prefix: "<dir>/"})                  -> Over-coupled symbols by fan-in / fan-out / community crossings
6. analyze({kind: "cycles", path_prefix: "<dir>/"})                    -> Tarjan SCCs with severity
7. analyze({kind: "todos", path_prefix: "<dir>/"})                     -> TODO / FIXME / HACK nodes
8. analyze({kind: "stale_code", older_than: 365, path_prefix: "<dir>/"})  -> Code untouched 1y+ — refactor / delete candidates
9. analyze({kind: "stale_flags", older_than: 180})                     -> Feature flags whose every toggler is older than 6mo
10. analyze({kind: "coverage_summary", path_prefix: "<dir>/"})         -> Per-directory coverage rollup
11. analyze({kind: "coverage_gaps", path_prefix: "<dir>/",             -> Undertested symbols
             min_pct: 0, max_pct: 50})
12. get_untested_symbols({path_prefix: "<dir>/"})                      -> Symbols with zero covering tests
13. analyze({kind: "error_surface", path_prefix: "<dir>/"})            -> Functions and what they throw — risk concentration
14. analyze({kind: "field_writers"})                                   -> Mutability hotspots — fields ranked by write count
15. analyze({kind: "race_writes"})                                     -> Cross-language goroutine-reachable writes w/o lock
16. analyze({kind: "unclosed_channels"})                               -> Channels with sends but no close()
17. analyze({kind: "orphan_tables"})                                   -> Tables queried but missing a migration
18. analyze({kind: "unreferenced_tables"})                             -> Tables provided by a migration with zero readers
19. analyze({kind: "stale_flags"})                                     -> Dead-rollout flag candidates (rerun with smaller window if noisy)
20. analyze({kind: "sast", path_prefix: "<dir>/", severity: "high"})   -> CWE/OWASP-tagged security scan — 190+ rules across 8 languages
21. analyze({kind: "named", name: "<bundle>"})                         -> Named query bundles — sql-injection, hardcoded-secrets, ssrf, xxe, weak-crypto, …
22. analyze({kind: "unsafe_patterns", path_prefix: "<dir>/"})          -> Panic-prone / undefined-behavior primitives across all languages
23. search_ast({detector: "<lang-specific detector>"})                -> Targeted structural anti-pattern sweep (see list_inspections for the menu)
24. analyze({kind: "health_score", path_prefix: "<dir>/",             -> Composite per-file health grade — ranks the worst files first
             roll_up: "file"})
25. audit_agent_config                                                 -> Stale references in CLAUDE.md / AGENTS.md / .cursor/rules — config drift
26. contracts({action: "check"})                                       -> Orphan providers / consumers; HTTP / gRPC / topics / env drift
27. analyze({kind: "ownership", path_prefix: "<dir>/"})                -> Per-author rollup — who to ping per finding
28. export_context({format: "markdown",                                -> Hand the audit packet to PR / Slack / wiki
                    sections: ["findings", "ownership", "priorities"]})
```

## Prioritising findings

Findings are not equal. Rank by:

| Tier | Filter                                                                                   | Why this matters first |
| ---- | ---------------------------------------------------------------------------------------- | ---------------------- |
| **P0** | `analyze kind=sast severity=high` / `analyze kind=named` / `race_writes` / `unclosed_channels` / `weak-crypto` / `hardcoded-secret` / `http-client-no-timeout` | Correctness / security |
| **P1** | `cycles severity=severe` / `orphan_tables` / `contracts({action: check})` orphans / `audit_agent_config` stale refs | Real bugs latent in the graph |
| **P2** | `dead_code` ∩ `find_clones dead_only=true` / `stale_flags` / `stale_code` | Deletion candidates — easy ROI |
| **P3** | `hotspots top=20` / `coverage_gaps min_pct=0 max_pct=20` / `error_surface` widening | Refactor targets |
| **P4** | `todos` / `field_writers` heavy churn | Backlog signal |

Always pair every P0 / P1 finding with `analyze kind=ownership` on its path so the audit packet pings the right reviewer.

## Security & anti-pattern sweep

`analyze kind=sast` is the comprehensive pass — 190+ CWE/OWASP-tagged rules across 8 languages, filterable by `severity` / `cwe` / `tag`. `analyze kind=named` runs focused query bundles (sql-injection, hardcoded-secrets, ssrf, xxe, …). `search_ast` then targets specific structural smells — a representative subset of its bundled detectors:

| Detector                          | Languages                            |
| --------------------------------- | ------------------------------------ |
| `error-not-wrapped`              | Go                                   |
| `sql-string-concat`              | Go / Python / JS / TS / Ruby         |
| `weak-crypto`                    | Go / Python                          |
| `panic-in-library`               | Go                                   |
| `goroutine-without-recover`      | Go                                   |
| `http-client-no-timeout`         | Go                                   |
| `hardcoded-secret`               | Go / Python / JS / TS / Ruby         |
| `empty-catch`                    | Java / JS / TS / Python              |
| `java-string-equality`           | Java                                 |
| `python-mutable-default-arg`     | Python                               |

Run each `search_ast detector=<name>` once; the per-match `symbol_id` chains directly into `find_usages` / `apply_code_action` for follow-up.

## Audit deliverable

The output of a quality audit is a prioritised markdown packet, not a one-line "score":

```
# <Repo / dir> audit — <date>

## P0 — Correctness / security
- <finding> · <symbol id> · owner: <author> · evidence: <analyze kind=… ran>

## P1 — Real bugs latent in the graph
…

## P2 — Deletion candidates
…

## P3 — Refactor targets
…

## P4 — Backlog signal
…
```

`export_context format=markdown` is the producer.

## Checklist

- `gortex enrich blame coverage releases all` (CLI) before the audit — temporal + coverage analyzers need `meta.last_authored` / `meta.coverage_pct` / `meta.added_in`
- Walk the analyzers in the order above — earlier ones surface highest-priority findings
- Pair every finding with `analyze kind=ownership` so the audit packet has a routing column
- `analyze kind=sast` is the security backbone; add `search_ast` detectors for the language-specific smells it doesn't cover
- `audit_agent_config` catches stale references in the team's CLAUDE.md / AGENTS.md / IDE config — config drift is a real-world finding
- Rank by the P0..P4 tiers; do not hand the user a flat list
- `export_context format=markdown` for the packet
- `store_memory({kind: "reference", title: "<repo> audit YYYY-MM", body: "..."})` so the next audit can diff against this baseline
=== home/.hermes/skills/code-intelligence/gortex-cli/SKILL.md ===
---
name: gortex-cli
description: "Drive Gortex from the shell via the gortex CLI, no MCP surface."
version: 1.0.0
metadata:
  hermes:
    tags: [code-intelligence, mcp, cli]
    category: code-intelligence
    platforms: [linux, macos, windows]
    related_skills: [gortex]
---

# Gortex from the CLI (no MCP surface)

Every Gortex capability is reachable from the `gortex` command, so you can work the graph from a plain shell — no MCP server mounted, no extra tools in your context. Reach for this when the MCP surface is unavailable or impractical (a CI job, a low-context budget, a shell-only agent), or when you simply prefer the command line.

This is a **toolkit, not a mandate**: pick the verbs your task needs. The sequence below is a reference safety workflow for changing code — run the steps that apply and skip the rest. Most verbs accept `--format json` for machine-readable output and `--repo <path>` to target a tracked repo other than the cwd. They all talk to a running daemon, so `gortex daemon status` first if anything errors.

## Orient

```bash
gortex query stats
```

Confirms the daemon is up and prints node / edge counts for the active repo — the CLI analogue of a session-start orientation.

## Assemble the working set

```bash
gortex explore "make the indexer skip vendored files"
```

Shells smart_context: one call returns the minimal relevant symbols, sources, and an edit plan for a task description. Start here instead of reading files one at a time. Note the symbol IDs it surfaces — later verbs take them as `--ids` / `--symbols`.

## Pick up prior knowledge

```bash
gortex memory surface --task "make the indexer skip vendored files" --symbols internal/indexer/indexer.go::IndexFile,internal/indexer/skip.go::shouldSkip
```

Surfaces cross-session invariants, decisions, and gotchas anchored to your working set, ranked by overlap. If it returns nothing, move on — don't probe further.

## Before editing a file

```bash
gortex edit context internal/indexer/indexer.go
```

Shows the file's symbols, signatures, callers, and callees so you edit with the blast radius in view.

## Before a signature change

```bash
gortex edit verify --change 'internal/indexer/indexer.go::IndexFile=func(path string, opts Opts) error'
```

Checks every caller and interface implementor against the proposed signature and reports what would break. `--change` is repeatable (`id=new_signature`); pass a raw JSON array via `--changes` / `--changes-file` for bulk edits.

## Plan a multi-symbol refactor

```bash
gortex edit plan --ids internal/indexer/indexer.go::IndexFile,internal/indexer/skip.go::shouldSkip
```

Returns a dependency-ordered list of files and symbols to touch, so a multi-file change goes in the right order.

## Apply edits atomically

```bash
gortex edit batch --edits-file edits.json
```

Applies multiple edits in one atomic, dependency-ordered pass. Build `edits.json` as the edits array (or pass it inline with `--edits`, or on stdin with `--edits -`); add `--dry-run` to preview the unified diff first.

## After editing

```bash
gortex edit guards --ids internal/indexer/indexer.go::IndexFile
gortex edit tests --ids internal/indexer/indexer.go::IndexFile
```

`guards` evaluates team rules against the changed symbols; `tests` enumerates the test files and functions that exercise them so you know what to run.

## Close the loop

```bash
gortex feedback record --task "make the indexer skip vendored files" --useful internal/indexer/skip.go::shouldSkip
gortex memory store --kind decision --body "skip lives in shouldSkip; IndexFile stays signature-stable for the daemon hot path" --symbols internal/indexer/skip.go::shouldSkip
```

`feedback record` scores which `explore` suggestions were useful / not needed / missing, improving future context quality. `memory store` persists a durable invariant / decision / gotcha so the next agent inherits the lesson — set `--kind` honestly and anchor it with `--symbols`.

## When you need more

These verbs cover the dev-cycle workhorses. For the long tail of the catalog, discover and invoke any tool directly:

```bash
gortex tools list                       # the whole surface, by category + read/write class
gortex tools search "dataflow taint"    # find a tool by keyword
gortex call analyze --arg kind=dead_code --arg path=internal/indexer
```

`gortex call <tool> --arg key=value` is the generic escape hatch — it invokes any registered tool by name, so nothing in the MCP surface is out of reach from the shell. There are also dedicated verb groups worth knowing: `gortex analyze`, `gortex flow` / `gortex taint`, `gortex clones`, and the rest of the `gortex edit` and `gortex memory` families.
=== home/.hermes/skills/code-intelligence/gortex/SKILL.md ===
---
name: gortex
description: "Use for any task on a codebase indexed by the gortex daemon — searching symbols, finding usages/callers, reading code, tracing impact, refactoring, and multi-repo navigation. Prefer these graph tools over raw file reads or text search."
version: 1.0.0
metadata:
  hermes:
    tags: [code-intelligence, code-search, navigation, refactoring, mcp]
    category: code-intelligence
    platforms: [linux, macos, windows]
    related_skills: [gortex-add-test, gortex-architecture-review, gortex-cli, gortex-co-change, gortex-cross-repo-usage, gortex-dataflow-trace, gortex-debug, gortex-episode-replay, gortex-explore, gortex-extract-function, gortex-fix-all, gortex-impact, gortex-incident-investigation, gortex-onboarding, gortex-pr-review, gortex-pr-review-agent, gortex-quality-audit, gortex-refactor, gortex-rename, gortex-safe-edit]
---

# Gortex Code Intelligence

Gortex indexes repositories into an in-memory knowledge graph and serves it over MCP. On any indexed codebase its graph tools are faster, cheaper, and more accurate than reading whole files or grepping — they return exactly the symbol, caller set, or blast radius you asked for, with zero false positives.

## When to Use

- Searching for a symbol, function, type, or where something is referenced.
- Reading a single function/method without pulling its whole file.
- Understanding architecture, tracing call chains, or checking what a change breaks.
- Refactoring: renames, extractions, and multi-file edits that must stay consistent.
- Working across more than one repository from a single session.

## Prerequisites

- The `gortex` MCP server is registered in `~/.hermes/config.yaml` under `mcp_servers.gortex` (gortex's installer wires this for you).
- The gortex daemon is running and tracking the repo: check with `gortex daemon status` in a terminal, start it with `gortex daemon start --detach`, and track a repo with `gortex init` (or `gortex track <path>`).
- Confirm the graph is live at the start of a task by calling the `graph_stats` tool. If `total_nodes` is 0, call `index_repository` with `path: "."` first.

## How to Run

Call the gortex MCP tools directly. Translate the instinct to read or grep into the matching graph query. If you only have the terminal (no MCP tools), every tool below is reachable as `gortex call <tool> --arg k=v` (e.g. `gortex call read_file --arg path=<file>`) — there is no bare `gortex <tool>` verb.

### Search and navigation

| Instead of...                            | Use the gortex tool...                       |
|------------------------------------------|----------------------------------------------|
| Grepping for a symbol                    | `search_symbols` (BM25 + camelCase-aware)         |
| Grepping for references                  | `find_usages` (zero false positives)             |
| Hunting for callers                      | `get_callers` / `get_call_chain`                     |
| Globbing source files (`**/*.go`)         | `get_repo_outline` / `search_symbols`                |
| Many file reads to orient on a task      | `smart_context` (one call assembles the working set) |
| Literal / regex text the symbol index misses | `search_text` (trigram-accelerated grep)         |

### Reading source

| Instead of...                            | Use the gortex tool...                       |
|------------------------------------------|----------------------------------------------|
| Reading a whole file for one function    | `get_symbol_source` (≈80% fewer tokens)          |
| Reading a file to understand it          | `get_file_summary` / `get_editing_context`           |
| Reading a file to check a signature      | `get_symbol` (signature in `meta.signature`)         |
| Reading a non-indexed / raw file         | `read_file` (atomic, overlay-aware)              |

### Editing and refactoring

| Instead of...                            | Use the gortex tool...                       |
|------------------------------------------|----------------------------------------------|
| A whole-file string-match edit           | `edit_file` (no pre-read; atomic; auto-reindex)  |
| A read→edit roundtrip for one symbol     | `edit_symbol` (edit by ID)                       |
| Manual find-and-replace for a rename     | `rename_symbol` (updates cross-file references)  |
| Sequencing multi-file edits by hand      | `batch_edit` (dependency-ordered, atomic)        |
| Guessing what a change breaks            | `verify_change` / `get_dependents` (blast radius)    |

### Analysis

`analyze` is a unified dispatcher — pass `kind` for one of `dead_code`, `hotspots`, `cycles`, `coverage_gaps`, `todos`, `sast`, `impact`, `cross_repo`, and ~50 more. `get_architecture` gives a one-call architectural snapshot.

## Multi-repo scoping

The daemon can track several repositories at once. Scope your queries so results come from the right project:

- Call `get_active_project` to see the current scope and `set_active_project` to switch the session default.
- Most list/search tools accept a `repo` or `project` argument to target one repository for a single call without changing the session default.
- `list_repos` enumerates everything the daemon tracks; `track_repository` adds a new one.
- `analyze kind: "cross_repo"` and a `find_usages` partitioned by repo answer "who consumes this across all our services?".

## Quick Reference

1. `index_health` — confirm the daemon is up and oriented (`graph_stats` for node/edge counts).
2. `smart_context` with the task description — assemble the minimal working set.
3. `search_symbols` / `find_usages` / `get_symbol_source` — navigate and read.
4. `get_editing_context` then `edit_symbol` / `edit_file` / `rename_symbol` / `batch_edit` — edit safely.
5. `verify_change` / `get_test_targets` — check the blast radius before and after.

## Token economy

For list-shaped responses (`search_symbols`, `find_usages`, `analyze`, `get_callers`, `get_editing_context`, `smart_context`, …) pass `format: "gcx"` for the GCX1 compact wire format — round-trippable, ~27% fewer tokens. For reading source, pass `compress_bodies: true` to `read_file` / `get_symbol_source` / `get_editing_context` to elide function bodies to signatures (~30–40% of original tokens).

## Pitfalls

- Don't fall back to raw file reads / shell grep on an indexed repo "just to be quick" — the graph tools are both faster and more precise, and they keep your context budget intact.
- An empty result from `search_symbols` usually means the daemon hasn't finished warming or isn't tracking this repo — check `graph_stats` / `index_health` rather than assuming the symbol is absent.
- In a multi-repo session, an unexpected result set is often a scoping issue — verify `get_active_project` or pass an explicit `repo` argument.

## Verification

After edits, call `verify_change` (broken callers + interface implementors, cross-repo) and `get_test_targets` (the tests that cover what you touched) before declaring the task done.

## Task playbooks (slash commands)

`gortex install` also registers these per-task playbooks as Hermes slash commands. Reach for the one that matches your task:

- `/gortex-add-test`
- `/gortex-architecture-review`
- `/gortex-cli`
- `/gortex-co-change`
- `/gortex-cross-repo-usage`
- `/gortex-dataflow-trace`
- `/gortex-debug`
- `/gortex-episode-replay`
- `/gortex-explore`
- `/gortex-extract-function`
- `/gortex-fix-all`
- `/gortex-impact`
- `/gortex-incident-investigation`
- `/gortex-onboarding`
- `/gortex-pr-review`
- `/gortex-pr-review-agent`
- `/gortex-quality-audit`
- `/gortex-refactor`
- `/gortex-rename`
- `/gortex-safe-edit`
=== home/.hermes/skills/debugging/gortex-debug/SKILL.md ===
---
name: gortex-debug
description: "Debug a bug, trace an error, or find why something fails."
version: 1.0.0
metadata:
  hermes:
    tags: [code-intelligence, mcp, debug]
    category: debugging
    platforms: [linux, macos, windows]
    related_skills: [gortex]
---
# Debugging with Gortex

## Workflow

```
1. search_symbols({query: "<error or suspect>"})          -> Find related symbols
2. get_callers({id: "<suspect>"})                         -> Who calls it?
3. get_call_chain({id: "<suspect>"})                      -> What does it call?
4. get_editing_context({path: "<file>"})                  -> Full file context
5. get_processes({id: "<process>"})                       -> Trace execution flow
6. get_symbol_history                                     -> Symbols churning this session (regression hotspot)
7. explain_change_impact({ids: "<fix target>"})           -> Who else will feel the fix
```

## Debugging Patterns

| Symptom                 | Gortex Approach |
| ----------------------- | --------------- |
| Error message           | search_symbols for error-related names -> get_callers on throw sites; analyze kind=error_surface to map who throws what |
| Wrong return value      | get_call_chain on the function -> trace callees for data flow; flow_between({source_id, sink_id}) when you suspect the wrong value flows through helpers |
| Trace bad value to its origin | flow_between({source_id: producer, sink_id: consumer}) — ranked dataflow paths over value_flow / arg_of / returns_to. Faster than reading source for "where did this value come from?" |
| Find every taint into a sink | taint_paths({source_pattern: "name:Source", sink_pattern: "name:Sink"}) — every flow from any matching source to any matching sink (functions auto-expand to their params on the sink side) |
| Intermittent failure    | get_editing_context -> look for external calls, async deps; analyze kind=goroutine_spawns to find unowned background work |
| Channel deadlock        | analyze kind=channel_ops -> channels with sends but no receivers (or vice versa) |
| Performance issue       | find_usages -> find symbols with many callers (hot paths) |
| Recent regression       | detect_changes -> see what your changes affect. get_symbol_history flags symbols edited 3+ times this session |
| Flaky test              | get_untested_symbols near the suspect -> find coverage gaps the flake may hide |
| Stale index suspect     | index_health -> parse failures and stale files can mask the real bug |
| Stale-flag suspect      | analyze kind=stale_flags -> flags with every caller untouched for `older_than` days are dead-rollout candidates |
| Config drift            | analyze kind=config_readers -> who reads this env/viper key? Surfaces forgotten readers |
| Event/log volume spike  | analyze kind=event_emitters with level=error -> find every site that logs an error |
| Mutation race suspicion | analyze kind=field_writers id=<field> -> every function that writes the contended field |
| Annotation drift        | analyze kind=annotation_users name=Deprecated -> every site still using a deprecated API |
| Env var read/write mismatch | find_usages on cfg::env::<NAME> -> Resources/Dockerfile stages declaring it (EdgeUsesEnv) plus code-side os.Getenv consumers via the shared config_key node |
| K8s manifest blast radius | analyze kind=k8s_resources k8s_kind=ConfigMap -> orphan ConfigMaps. find_usages on a ConfigMap Resource ID surfaces every workload that envFroms or mounts it |
| Container image audit   | analyze kind=images role=base -> every external image and how many Dockerfile stages / K8s Resources pull it. Filter by tag=latest to find the unpinned ones |
=== home/.hermes/skills/debugging/gortex-incident-investigation/SKILL.md ===
---
name: gortex-incident-investigation
description: "Walk a production symptom back to its root cause."
version: 1.0.0
metadata:
  hermes:
    tags: [code-intelligence, mcp, incident-investigation]
    category: debugging
    platforms: [linux, macos, windows]
    related_skills: [gortex]
---
# Incident Investigation with Gortex

Use this when an alert fired, a deploy regressed, or production is misbehaving and the user needs to walk back from the symptom (log line, error message, broken endpoint, failing test in CI) to the root cause. Wraps the debug + impact + recent-changes paths into one ordered drill so the investigator never loses the thread.

## Workflow (do not skip steps)

```
1. graph_stats                                                         -> Confirm index + orient
2. surface_memories({task: "<symptom>"})                               -> Prior incidents / invariants on the same area
3. search_symbols({query: "<error string or symbol name from the alert>"})  -> Resolve the suspect symbol
4. get_recent_changes({since_ts: <alert_time - 24h>})                  -> Files/symbols changed in the suspect window
5. get_symbol_history                                                  -> Symbols churning this session (regression hotspot)
6. analyze({kind: "error_surface", path_prefix: "<suspect dir>/"})     -> Throw sites that match the symptom
7. analyze({kind: "event_emitters", level: "error",                    -> Every log/metric site that could have produced the alert
            path_prefix: "<suspect dir>/", name: "<event token>"})
8. get_callers({id: "<suspect symbol>"})                               -> Who calls the suspect — narrow blast
9. get_call_chain({id: "<suspect symbol>"})                            -> What the suspect calls — downstream contributors
10. flow_between({source_id: "<input>", sink_id: "<suspect>"})         -> Trace the bad value's path (when applicable)
11. taint_paths({source_pattern: "name:<external input>",              -> Every flow from a kind of source to the suspect
                  sink_pattern: "name:<suspect>"})
12. analyze({kind: "field_writers", id: "<contended field>"})          -> Race / mutation suspects
13. analyze({kind: "channel_ops"})                                     -> Channel deadlock / orphan recv (Go)
14. analyze({kind: "goroutine_spawns"})                                -> Unowned background work
15. explain_change_impact({ids: "<root-cause id>"})                    -> Blast radius of the proposed fix
16. get_test_targets({ids: ["<root-cause id>"]})                       -> Tests to add / re-run as regression coverage
17. store_memory({kind: "incident", title: "<incident slug>",          -> Persist root cause + fix + affected symbols
                  body: "...", symbol_ids: ["<id>"], importance: 5})
```

## Triage by symptom

| Symptom                                                  | First-stop tools                                             |
| -------------------------------------------------------- | ------------------------------------------------------------ |
| New 5xx / panic / crash log                              | `search_symbols` on the error → `analyze kind=error_surface` → `get_callers` on throw sites |
| Latency spike on one endpoint                            | `analyze kind=routes path=<route>` → `get_call_chain` on the handler → `analyze kind=external_calls` for hot stdlib / module hops |
| Background worker silently broken                        | `analyze kind=goroutine_spawns` / `channel_ops` → `get_callers` on the spawned target |
| Pub/sub event missing                                    | `analyze kind=pubsub name=<topic>` → publisher + subscriber sides; `taint_paths` from publisher to subscriber handler |
| Config-driven misbehavior after a deploy                 | `analyze kind=config_readers name=<KEY>` → callers; `analyze kind=stale_flags` if it's a feature flag |
| Data corruption / wrong value at a sink                  | `flow_between` from suspected source to sink; `taint_paths` when multiple sources are plausible |
| Cross-service / contract drift                           | `contracts({action: check})` → orphan providers/consumers; pair with `/gortex-cross-repo-usage` |
| Test regression after a refactor                         | `detect_changes` over the merge → `diff_context` for graph-enriched view → `get_test_targets` for the missing test signal |

## Walking the timeline

When the symptom appears tied to a window (between two deploys, between two commits, since N hours ago):

1. `analyze kind=blame` to stamp `meta.last_authored` on every blame-eligible symbol (one-time per session; cheap thereafter)
2. `get_recent_changes since_ts=<window-start>` to enumerate files / symbols touched
3. `detect_changes scope=all` to project the diff onto blast radius
4. Pair each touched symbol with `explain_change_impact` — anything in the d=1/d=2 buckets that overlaps the symptom area is your root-cause candidate
5. `get_symbol_history` flags symbols edited 3+ times *in this session*; combine with blame for "edited 3+ times in the suspect window"

## Closing the loop (mandatory)

After root-cause is found and the fix is shipped:
- `store_memory({kind: "incident", ...})` so the next on-call sees this pattern surfaced via `surface_memories`
- If the incident exposed an invariant the code lacked guard for (lock missing, contract unenforced), also `store_memory({kind: "invariant", importance: 5, pinned: true})`
- `save_note({tags: "incident", body: ...})` for the session-local timeline before context compacts

## Checklist

- `graph_stats` + `surface_memories` before any search — prior incident memories are the highest-value signal
- `search_symbols` on the actual error string / symbol from the alert — do not paraphrase
- `get_recent_changes` + `get_symbol_history` to scope the window
- `analyze kind=error_surface` + `event_emitters` to find the production sites of the symptom
- `get_callers` / `get_call_chain` / `flow_between` / `taint_paths` to walk from symptom toward root cause
- Concurrency-shape symptoms → `channel_ops` / `goroutine_spawns` / `field_writers` / `race_writes` / `unclosed_channels`
- `explain_change_impact` on the candidate fix; `get_test_targets` for regression coverage
- `store_memory({kind: "incident"})` is **mandatory** before declaring the investigation closed
=== home/.hermes/skills/navigation/gortex-cross-repo-usage/SKILL.md ===
---
name: gortex-cross-repo-usage
description: "Find who uses a symbol across all consumer repos."
version: 1.0.0
metadata:
  hermes:
    tags: [code-intelligence, mcp, cross-repo-usage]
    category: navigation
    platforms: [linux, macos, windows]
    related_skills: [gortex]
---
# Cross-Repo Usage with Gortex

Use this when the user needs to see who uses a symbol **across every consumer repo**, not just the one they happen to be in. Wraps `find_usages` + the cross-repo edge layer (`cross_repo_calls` / `cross_repo_implements` / `cross_repo_extends`) so the answer is partitioned by repo and includes contract-level consumers (HTTP / gRPC / topics) that wouldn't show up as a direct call edge.

## Workflow (do not skip steps)

```
1. get_active_project                                                  -> See current scope (single repo or multi-repo project)
2. list_repos                                                          -> See which repos are tracked
3. For each consumer repo not yet tracked:
     track_repository({path: "<consumer repo path>"})                  -> Index immediately, persist to config
4. set_active_project({name: "<multi-repo project>"})                  -> Switch scope so subsequent queries span repos
5. search_symbols({query: "X"})                                        -> Resolve the symbol ID in the providing repo
6. find_usages({id: "<id>"})                                           -> All references; group by repo prefix
7. analyze({kind: "cross_repo",                                        -> Repo-boundary-crossing edges with relation
            base_kind: "calls|implements|extends",
            repo: "<providing repo>"})
8. contracts({action: "check"})                                        -> Contract-level consumers (HTTP routes, gRPC methods, topics, env, OpenAPI)
9. get_test_targets({ids: [<id>]})                                     -> Cross-repo tests that exercise this symbol
10. export_context({format: "markdown"})                               -> Per-repo report for PR / Slack / docs
```

## Partitioning `find_usages` by repo

The graph stores repo as a prefix on each node ID. Group results by the leading path segment so the user sees one section per consumer:

| Repo                      | References |
| ------------------------- | ---------- |
| my-org/api-gateway        | 7 sites    |
| my-org/billing            | 3 sites    |
| my-org/notifications      | 1 site     |

`analyze kind=cross_repo` complements this — it reports the typed edges (calls / implements / extends) that physically cross the boundary, which is the count that matters for "is this safe to change without coordinating with consumer teams."

## Contract-level consumers

For published API surfaces, `find_usages` won't show consumers that go through the wire. `contracts({action: check})` catches:
- HTTP route consumers (client-side `fetch` / `http.Get` / generated SDK methods)
- gRPC method consumers (generated client stubs across repos)
- Pub/sub subscribers (NATS / Kafka / RabbitMQ / Redis / EventEmitter / Socket.IO)
- Env-var consumers (one process writes, another reads)
- OpenAPI / GraphQL schema consumers

## Onboarding a consumer repo

If a known consumer is missing from the tracked-repo list:
1. `track_repository` with its path — indexes immediately, persists to config
2. Re-run `find_usages` and `analyze kind=cross_repo` — the new edges materialise
3. `untrack_repository` only when you're done; leaving it tracked keeps subsequent cross-repo queries cheap

## Checklist

- `get_active_project` first — you may already be in multi-repo scope
- `track_repository` every consumer repo you care about; `find_usages` only sees what's indexed
- Partition the `find_usages` rows by repo prefix; the per-repo breakdown is the deliverable
- `analyze kind=cross_repo` for the typed-edge view (calls / implements / extends)
- `contracts({action: check})` for wire-level consumers that `find_usages` cannot see
- `get_test_targets` returns cross-repo tests too — run them, not just the local ones
- Hand the per-repo report to the user via `export_context` for PR descriptions / Slack
=== home/.hermes/skills/navigation/gortex-dataflow-trace/SKILL.md ===
---
name: gortex-dataflow-trace
description: "Trace where a value flows through the code."
version: 1.0.0
metadata:
  hermes:
    tags: [code-intelligence, mcp, dataflow-trace]
    category: navigation
    platforms: [linux, macos, windows]
    related_skills: [gortex]
---
# Dataflow Trace with Gortex (CPG-lite)

Use this when the user asks where a value flows — through assignments, function args, returns, channels, event buses, or pub/sub topics. Built on the CPG-lite edge layer (`value_flow` ∪ `arg_of` ∪ `returns_to`) plus the pub/sub + channel + emit layers, so flows survive crossing function and process boundaries that a plain caller-graph walk would lose.

## Workflow (do not skip steps)

```
1. smart_context({task: "<the flow you want to understand>"})         -> Working-set bundle
2. search_symbols({query: "<source>"})                                 -> Resolve the producing symbol ID
3. search_symbols({query: "<sink>"})                                   -> Resolve the consuming symbol ID
4. flow_between({source_id: "<src>", sink_id: "<sink>",                -> Ranked paths over value_flow ∪ arg_of ∪ returns_to
                 max_depth: 8, max_paths: 10})
5. taint_paths({source_pattern: "<pattern>",                           -> Every flow from any matching source to any matching sink
                sink_pattern: "<pattern>",
                max_depth: 8})
6. analyze({kind: "channel_ops"})                                      -> Channel producer/consumer mismatch (Go)
7. analyze({kind: "pubsub",                                            -> Pub/sub topics with publishers + subscribers
            transport: "nats|kafka|rabbitmq|redis|eventemitter|socketio",
            name: "<topic>",
            role: "publish|subscribe"})
8. analyze({kind: "event_emitters", level: "error"})                   -> Log/metric/span emit sites
9. get_call_chain({id: "<sink>"}) + get_callers({id: "<source>"})      -> Caller-graph cross-check for any path flow_between missed
10. export_context({format: "markdown"})                               -> Hand the trace to a PR / Slack / doc
```

## Pattern syntax for `taint_paths`

Each pattern is one or more clauses combined with AND:

| Clause             | Meaning                                          | Example                       |
| ------------------ | ------------------------------------------------ | ----------------------------- |
| `<bare token>`   | Substring match on the symbol name               | `Decode`                    |
| `exact:<name>`   | Exact name match                                 | `exact:HandleRequest`       |
| `path:<prefix>`  | Path prefix on the file the symbol lives in     | `path:internal/http/`       |
| `kind:<kind>`    | Restrict to `function` / `method` / `field` / etc. | `kind:method`               |

Functions auto-expand to their params on the sink side, so `taint_paths` with a function-shaped sink pattern reports flows into any of its parameters.

## Crossing process / transport boundaries

`flow_between` walks intra-process edges. To follow a value across a wire:

| Boundary                          | Tool                                                 |
| --------------------------------- | ---------------------------------------------------- |
| Channel (Go)                       | `analyze kind=channel_ops` — find the matching `recv` site, then `flow_between` from there |
| Pub/sub topic                      | `analyze kind=pubsub name=<topic>` — get every subscriber; `flow_between` from each |
| HTTP / gRPC / GraphQL contract     | `contracts({action: check})` — match providers to consumers; the contract ID is your bridge node |
| Env var (one process writes, another reads) | `analyze kind=config_readers name=<KEY>` -> consumers; `find_usages` on `cfg::env::<KEY>` |
| Cross-repo call                    | `analyze kind=cross_repo base_kind=calls` — typed boundary-crossing edges |

## Reading a ranked path

Each row from `flow_between` is a sequence of edges with the kind annotated (`value_flow` / `arg_of` / `returns_to`). The rank reflects path length + edge confidence. Treat the top-3 paths as the load-bearing ones; the long tail tends to be incidental cross-references.

## Checklist

- `smart_context` first — pick up the working set before asking flow_between for a path
- Resolve **both** source and sink to symbol IDs before calling `flow_between` (path search needs anchors)
- Use `taint_paths` when "every flow from a kind of source to a kind of sink" matters more than one specific pair
- Cross process boundaries with the right analyzer (`channel_ops` / `pubsub` / `config_readers` / `contracts`) — `flow_between` alone won't follow a value across the wire
- Cross-check with `get_call_chain` / `get_callers` when `flow_between` returns zero paths for a flow you're sure exists — the producer or consumer may not yet be in the dataflow layer
- `export_context` to hand the trace to a PR / Slack / doc without losing the structure
=== home/.hermes/skills/navigation/gortex-explore/SKILL.md ===
---
name: gortex-explore
description: "Understand how code works or trace an execution flow."
version: 1.0.0
metadata:
  hermes:
    tags: [code-intelligence, mcp, explore]
    category: navigation
    platforms: [linux, macos, windows]
    related_skills: [gortex]
---
# Exploring Codebases with Gortex

## Workflow

```
1. graph_stats                                  -> Confirm index, get node/edge counts
2. smart_context({task: "<what you want to understand>"}) -> One-call exploration bundle (start here)
3. get_communities                              -> See functional clusters (architecture overview)
4. search_symbols({query: "<concept>"})         -> Find symbols related to a concept
5. get_processes                                -> Discover execution flows
6. get_processes({id: "<process-id>"})          -> Trace a specific flow step by step
7. get_file_summary({path: "<file>"})           -> Symbols + imports for one file
8. get_editing_context({path: "<file>"})        -> Deep dive on a file (callers + callees)
9. export_context({...})                        -> Share findings as markdown/JSON (PRs, Slack, docs)
```

## Checklist

- Call index_health to confirm Gortex is running (graph_stats only when you need node/edge counts)
- Call smart_context first — one call replaces 5-10 exploration calls
- Call get_communities for architecture overview when smart_context is not enough
- Call search_symbols for the concept you want to understand
- Call get_processes to discover execution flows
- Call get_processes with id on relevant flows for step-by-step traces
- Call get_editing_context on key files for full symbol context
- Call export_context to hand a findings packet outside the session
- Read source files only for implementation details you actually need to edit
=== home/.hermes/skills/navigation/gortex-onboarding/SKILL.md ===
---
name: gortex-onboarding
description: "Structured tour of an unfamiliar repo."
version: 1.0.0
metadata:
  hermes:
    tags: [code-intelligence, mcp, onboarding]
    category: navigation
    platforms: [linux, macos, windows]
    related_skills: [gortex]
---
# Onboarding with Gortex (30-minute repo tour)

Use this when the user is new to a repo (or returning to one cold) and wants a structured tour: where to read first, what the architecture looks like, who owns what, what's load-bearing, and where to safely make a first edit. Composes the discovery-tier tools into a single ordered walk so the agent doesn't waste the user's first hour on undirected `Read` calls.

## Workflow (do not skip steps)

```
1. graph_stats                                                         -> Confirm Gortex is indexed; per-language / per-kind counts
2. get_active_project                                                  -> Is this single-repo or part of a multi-repo project?
3. list_repos (when multi-repo)                                        -> See sibling repos in the project
4. get_repo_outline                                                    -> Narrative single-call codebase overview (entry points, top communities, hot paths)
5. surface_memories({task: "onboarding <repo>"})                       -> Cross-session invariants / conventions already captured
6. distill_session                                                     -> Prior session digest for this workspace (decisions, pinned notes)
7. get_communities                                                     -> Functional clusters via Louvain — the implicit "modules"
8. get_processes                                                       -> Discovered execution flows (the implicit "use cases")
9. analyze({kind: "hotspots"})                                         -> Over-coupled symbols — where most edits should be careful
10. analyze({kind: "components"})                                      -> Component fan-in/out (UI projects)
11. analyze({kind: "routes"})                                          -> HTTP / gRPC / GraphQL / WS endpoints — the API surface
12. analyze({kind: "models"})                                          -> ORM models → tables — the data surface
13. analyze({kind: "ownership", path_prefix: "<dir>/"})                -> Per-author rollup — who to ping for what
14. contracts({action: "list"})                                        -> Detected API contracts (provider side)
15. audit_agent_config                                                 -> Project CLAUDE.md / AGENTS.md / .cursor/rules — read what the team told the agent
16. ask({question: "What does this repo do and how is it organised?"}) -> LLM summary grounded in the working set (only when llm provider configured)
```

## The five-minute version (when the user is in a hurry)

```
1. graph_stats
2. get_repo_outline
3. get_communities  (top 5)
4. surface_memories({task: "onboarding"})
5. audit_agent_config
```

Stop here unless the user asks for more depth. The outline + communities + memories triple covers "what is this repo / how is it carved up / what should I know before editing."

## The full tour (when the user wants 30 minutes)

The full `Workflow` above, plus per-community drilldowns:

1. From `get_communities`, pick the top 3 by node count
2. For each, `get_communities({id: <community-id>})` returns its members
3. `get_file_summary` on the community's central file (highest fan-in by symbol count)
4. `get_processes` on a process that crosses the community
5. `get_callers` / `get_call_chain` on the community's entry-point symbol

Result: the user has a concrete mental model of one community per drilldown.

## Where to make a first edit safely

After the tour, the user usually asks "where can I make my first change without breaking things?" Answer with:

1. `analyze kind=todos` — every TODO / FIXME node, filterable by author / tag — pick a small one in a low-fan-in symbol
2. `analyze kind=coverage_gaps min_pct=0 max_pct=50 path_prefix=<community dir>/` — undertested code where a test addition is a safe first PR
3. `analyze kind=stale_code older_than=180` — code untouched for 6+ months in a community the user just explored — usually safe to add a small clarifying refactor

Either path: hand off to `/gortex-safe-edit` (or `/gortex-add-test` for the coverage-gap path) for the actual change.

## Multi-repo onboarding

When `get_active_project` shows multiple members:

- `list_repos` first; pick the *primary* repo for the tour (usually the largest by node count or the one with the most `KindContract` provider nodes)
- `analyze kind=cross_repo base_kind=calls` shows the cross-repo coupling — onboarding must cover *who calls into* + *who is called from* the primary
- `contracts({action: list})` partitioned by repo shows the wire surface each repo exposes

## Onboarding artefact

The deliverable for an onboarding session is a markdown packet the user can keep:

```
export_context({
  task: "onboarding tour of <repo>",
  format: "markdown",
  sections: ["outline", "communities", "processes", "hotspots", "routes", "models", "memories"]
})
```

Hand that to the user; they can paste it into their notes / wiki / agent memory.

## Checklist

- `graph_stats` + `get_active_project` before any tool that reads the graph — confirm scope first
- `get_repo_outline` is the single-call narrative; if it's missing fields the user needs, fall back to the per-analyzer walks
- `surface_memories` + `distill_session` — prior agents may have left invariants / conventions / decisions that shape the tour
- `get_communities` + `get_processes` for the architectural skeleton
- `audit_agent_config` reads the team's own CLAUDE.md / AGENTS.md so the tour respects whatever conventions the team wrote down
- For multi-repo: pick the primary, then `analyze kind=cross_repo` for the boundary view
- End with `export_context format=markdown` so the user keeps the tour
- Suggest a safe first edit via `analyze kind=todos` / `coverage_gaps` / `stale_code` and hand off to `/gortex-safe-edit` or `/gortex-add-test`
=== home/.hermes/skills/refactoring/gortex-extract-function/SKILL.md ===
---
name: gortex-extract-function
description: "Extract code into a function or method via LSP refactor."
version: 1.0.0
metadata:
  hermes:
    tags: [code-intelligence, mcp, extract-function]
    category: refactoring
    platforms: [linux, macos, windows]
    related_skills: [gortex]
---
# Extract Function with Gortex (LSP refactor path)

Use this when the user wants to extract code into a new function / method / variable / constant via the **language server's refactor actions**, not by hand-editing braces. Maps onto LSP `refactor.extract.*` code actions; works wherever the underlying server supports them (gopls / tsserver / pyright / rust-analyzer / jdtls / kotlin-language-server / omnisharp / and others).

## Workflow (do not skip steps)

```
1. get_editing_context({path: "<file>"})                                -> See enclosing symbol, callers, callees
2. (optional) find_usages({id: "<enclosing-fn>"})                       -> Who calls the function you are about to split
3. get_code_actions({path: "<file>", range: {start, end},                -> Menu filtered to refactor.extract.* actions
                     only: ["refactor.extract.function",
                            "refactor.extract.method",
                            "refactor.extract.variable",
                            "refactor.extract.constant"]})
4. preview_edit({edit: <chosen action.workspaceEdit>})                  -> Speculative apply
   -> Inspect: touched / added / renamed / broken_callers / broken_implementors / impact
5. If the extraction crosses files (e.g. moving the helper to a sibling file):
     simulate_chain({steps: [...]})                                     -> Ordered chain with broken-caller carry-over
6. apply_code_action({path: "<file>", action_id: "<id>"})               -> Atomic temp+rename, UTF-16 column math
7. check_guards({ids: [<changed-ids>]}) + verify_change                  -> Convention + signature gate
8. get_test_targets({ids: [<old-id>, <new-id>]})                        -> Run tests for both the caller and the extract
9. detect_changes + diff_context                                        -> Final scope check
```

## Choosing the right extract action

| User intent                                | Code-action kind                  | Notes |
| ------------------------------------------ | --------------------------------- | ----- |
| Pull a block of statements into a helper   | `refactor.extract.function`             | Most servers infer params + return type from the selection. |
| Move statements into a method on a type    | `refactor.extract.method`               | Receiver / this binding chosen by the server. |
| Extract a sub-expression to a local        | `refactor.extract.variable`             | Best for naming repeated sub-expressions. |
| Extract a literal to a package-level const | `refactor.extract.constant`             | gopls and tsserver expose this; rust-analyzer uses `refactor.extract.module` for similar moves. |

If you don't know which kinds the server offers, call `get_code_actions` **without** `only` and inspect the menu first.

## Range selection

LSP positions are line + UTF-16 character offsets. The `apply_code_action` mapper handles this correctly, but when you compose the range yourself:
1. Get the file body via `get_symbol_source` (compress_bodies:false) or `get_file_summary`
2. Use the symbol's line numbers as anchors; expand to the precise statement boundaries
3. Pass `range: {start: {line, character}, end: {line, character}}` to `get_code_actions`

## When the server has no extract action

Some servers (yaml-language-server, json-language-server, bash-language-server) don't ship refactor actions. Fall back to `/gortex-safe-edit` and author the extract by hand — but still `preview_edit` first.

## Checklist

- `get_editing_context` to understand the enclosing symbol before selecting a range
- `get_code_actions` with `only: refactor.extract.*` — pick from the menu, don't invent
- `preview_edit` before `apply_code_action` (broken callers / blast radius)
- `simulate_chain` if the extract moves the helper to a new file with follow-on rename of call sites
- `check_guards`, `verify_change`, `get_test_targets` after the apply
- `get_symbol` on the new symbol to confirm Gortex indexed it under the expected ID
=== home/.hermes/skills/refactoring/gortex-fix-all/SKILL.md ===
---
name: gortex-fix-all
description: "Clear LSP diagnostics — one error, a file, or source.fixAll."
version: 1.0.0
metadata:
  hermes:
    tags: [code-intelligence, mcp, fix-all]
    category: refactoring
    platforms: [linux, macos, windows]
    related_skills: [gortex]
---
# Fix LSP Diagnostics with Gortex (LSP code-actions path)

Use this when the user wants errors / warnings cleared — for one symbol, one file, or the whole project. Bridges Gortex to the 18-language LSP coverage (gopls / tsserver / pyright / rust-analyzer / clangd / jdtls / kotlin-language-server / omnisharp / ruby-lsp / phpactor / lua-language-server / sourcekit-lsp / haskell-language-server / elixir-ls / ocamllsp / zls / terraform-ls / yaml-language-server / json-language-server / bash-language-server). No manual `Edit` of error messages.

## Workflow (do not skip steps)

```
1. subscribe_diagnostics({min_severity: 1, path_prefix: "<dir>/"})   -> Push notifications, replays initial state once
2. get_diagnostics({path: "<file>", wait: true, timeout_ms: 2000})   -> Poll form when you need a synchronous snapshot
3. get_code_actions({path: "<file>", range: {...}})                  -> Menu of fixes / refactors / source actions
4. For each chosen action:
     preview_edit({edit: <action.workspaceEdit>})                    -> Speculative apply on the shadow graph
     apply_code_action({path, action_id})                            -> Atomic on-disk apply (UTF-16 column math)
5. fix_all_in_file({path: "<file>"})                                 -> One-shot source.fixAll over an entire file
6. get_diagnostics({path: "<file>"})                                 -> Confirm the file is now clean
7. check_guards({ids: [<changed-ids>]}) + get_test_targets({ids})    -> Post-fix guardrails
8. unsubscribe_diagnostics                                           -> Only when narrowing scope; auto-fires on disconnect
```

## Diagnostic-fix patterns

| Situation                                              | Tool ordering | Notes |
| ------------------------------------------------------ | ------------- | ----- |
| One specific error                                     | `get_code_actions` at the diagnostic range -> pick one -> `apply_code_action` | Smallest possible edit. |
| Every error in a file                                  | `fix_all_in_file` | Bundles every server-suggested fix in a single round-trip. |
| Refactor offered as a code action (`refactor.*`)       | `get_code_actions` -> pick by `kind` (e.g. `refactor.extract`) -> `preview_edit` first | See `/gortex-extract-function`. |
| Source action (`source.organizeImports`, `source.fixAll`) | `get_code_actions` with the right `only` kind, or `fix_all_in_file` | Whole-file scope. |
| Watching diagnostics during a long edit session         | `subscribe_diagnostics` with `min_severity` + `path_prefix` filters | SHA-suppressed delta payloads; only changed files reach you. |

## Reading the diagnostic stream

- `initial_replay: true` on the first push — that's the synchronous snapshot of the current LSP state, not a new event.
- Every subsequent push carries only files whose `publishDiagnostics` SHA changed.
- `min_severity` 1 = error, 2 = warning, 3 = info, 4 = hint. Default to 1 unless you specifically need warnings.
- `path_prefix` is your scope filter — pin it to the area you're working on so the rest of the project doesn't drown the stream.

## When LSP cannot fix it

If `get_code_actions` returns an empty menu, the server doesn't know how to fix this diagnostic. Fall back to:
1. `search_symbols` for the symbol the diagnostic references
2. `get_editing_context` on the file
3. `/gortex-safe-edit` to author the edit by hand, with `preview_edit` to verify

## Checklist

- `subscribe_diagnostics` once per session, with `min_severity` + `path_prefix` scoped to the area you're touching
- `get_code_actions` is the menu — never invent fixes
- `preview_edit` (or `apply_code_action` directly when the action is small and well-known) before any disk write
- `fix_all_in_file` for whole-file source.fixAll — one round-trip beats N targeted fixes
- Re-run `get_diagnostics` after the fix; assume nothing
- `check_guards` and `get_test_targets` after the diagnostics are clean — fixing the LSP error is not the same as fixing the test
=== home/.hermes/skills/refactoring/gortex-refactor/SKILL.md ===
---
name: gortex-refactor
description: "Rename, extract, split, or restructure code safely."
version: 1.0.0
metadata:
  hermes:
    tags: [code-intelligence, mcp, refactor]
    category: refactoring
    platforms: [linux, macos, windows]
    related_skills: [gortex]
---
# Refactoring with Gortex

## Workflow

```
1. search_symbols({query: "X"})                                     -> Find the symbol ID
2. explain_change_impact({ids: "<id>"})                             -> Map blast radius
3. analyze({kind: "ownership", path_prefix: "<dir>/"})              -> Who should review (pinging policy)
4. analyze({kind: "coverage_gaps", path_prefix: "<dir>/"})          -> Where is the refactor risky (poor coverage)
5. verify_change({id: "<id>", new_signature: "..."})                -> Catch contract violations in callers + implementors
6. get_editing_context({path: "<file>"})                            -> See all symbols and relationships
7. find_usages({id: "<id>"})                                        -> Every reference to change
8. get_edit_plan({ids: ["<id1>", "<id2>"]})                         -> Dependency-ordered file list
9. batch_edit({edits: [...]})                                       -> Apply edits in order, re-indexing between steps
10. check_guards({ids: [...]})                                      -> Post-edit: team conventions from .gortex.yaml
11. get_test_targets({ids: [...]})                                  -> Tests to re-run (cross-repo aware)
12. detect_changes({scope: "all"})                                  -> Verify scope; diff_context for review
```

## Rename Symbol

- search_symbols to find the symbol ID
- explain_change_impact to assess blast radius
- verify_change before signature-changing renames — fails fast on interface breaks
- rename_symbol({id: "<id>", new_name: "<name>"}) — generates edits for definition + all references
- Review the generated edits, apply via batch_edit or edit_symbol (no Read→Edit roundtrip)
- check_guards, then detect_changes to verify only expected files changed

## Extract Module

- get_editing_context on the source file — see all symbols
- get_dependents on symbols to extract — find external callers
- explain_change_impact on symbols being moved
- analyze({kind: "would_create_cycle", from: "<new module>", to: "<old module>"}) before wiring imports
- suggest_pattern + scaffold from a comparable existing module — generates code, wiring, test stubs
- Extract code, update imports (find_import_path for correct paths)
- get_edit_plan + batch_edit for dependency-ordered atomic application
- check_guards, detect_changes to verify affected scope

## Split Function/Service

- get_call_chain on the function — understand all callees
- Group callees by responsibility
- get_callers to map all call sites that need updating
- find_implementations when splitting along an interface
- explain_change_impact for full blast radius
- Create new functions/services (scaffold from a similar example)
- Update callers (find_usages for precise locations, batch_edit to apply in order)
- check_guards, detect_changes to verify affected scope

## API Contract Changes

- Before changing an HTTP route, gRPC method, topic, env, or OpenAPI contract: contracts({action: "check"}) to find cross-repo consumers
- verify_change on the provider signature
- Coordinate consumer-side edits in the same batch_edit when repos are tracked together
=== home/.hermes/skills/refactoring/gortex-rename/SKILL.md ===
---
name: gortex-rename
description: "Rename a symbol and update every reference atomically."
version: 1.0.0
metadata:
  hermes:
    tags: [code-intelligence, mcp, rename]
    category: refactoring
    platforms: [linux, macos, windows]
    related_skills: [gortex]
---
# Rename Symbol with Gortex (cross-file coordinated rename)

Use this when the user wants to rename a function / method / type / variable / package and have every reference (definition, callers, tests, cross-repo consumers, doc-comments where applicable) updated atomically. Picks the right tool for the job — graph-coordinated `rename_symbol` for Gortex-known symbols, LSP `textDocument/rename` (surfaced as a `refactor.rename` code action where supported) for server-driven cases.

## Workflow (do not skip steps)

```
1. search_symbols({query: "<old name>"})                              -> Resolve the symbol ID(s)
2. winnow_symbols({...})                                              -> Disambiguate if multiple matches survive
3. explain_change_impact({ids: "<id>"})                               -> Blast radius (callers, implementors, processes)
4. find_usages({id: "<id>"})                                          -> Every reference (BM25-free; zero false positives)
5. verify_change({id: "<id>", new_signature: "<old sig with new name>"}) -> Catches interface breaks early
6. rename_symbol({id: "<id>", new_name: "<new>"})                     -> Generates dependency-ordered WorkspaceEdit (Gortex path)
   OR get_code_actions({path, range, only: ["refactor.rename"]})      -> LSP-driven path (when you want server-side rename semantics)
7. preview_edit({edit: <generated WorkspaceEdit>})                    -> Speculative apply; check broken_callers / broken_implementors
8. batch_edit({edits: [...]})                                         -> Atomic on-disk apply, re-index between steps
   OR apply_code_action({...})                                        -> If you went the LSP route
9. check_guards({ids: [<old, new>]})                                  -> Naming conventions, banned terms from .gortex.yaml
10. get_test_targets({ids: [<new-id>]})                               -> Tests to re-run; cross-repo aware
11. detect_changes + diff_context                                     -> Confirm no stray reference survived
```

## Choosing the right rename tool

| Situation                                                  | Tool                          | Why |
| ---------------------------------------------------------- | ----------------------------- | --- |
| Symbol is in the graph and rename is mechanical             | `rename_symbol`                  | Generates the WorkspaceEdit from edges; deterministic, fast, no LSP round-trip. |
| Symbol semantics depend on the server (TS shadowed locals, Java generics, Rust trait resolution) | `get_code_actions` + `apply_code_action` with `refactor.rename` | Server resolves identifier scoping the parser can't fully replicate. |
| Cross-repo rename (consumer repos tracked together)         | `rename_symbol` + `contracts({action: check})` | Graph carries cross-repo edges; `contracts` flags HTTP / gRPC / topic-side breakage. |
| Rename of a published API surface                           | `rename_symbol` **plus** `contracts({action: check})` and a deprecation shim | Pair with `/gortex-cross-repo-usage` to find consumer repos. |

## Cross-repo renames

`rename_symbol` covers every repo the graph knows about — track consumer repos first (`track_repository`) and switch `set_active_project` to the multi-repo scope, then run the rename. `/gortex-cross-repo-usage` is the partition view that confirms every consumer repo was touched.

## Renaming through interfaces

If the renamed symbol is part of an interface contract:
1. `find_implementations` on the interface — every implementor must be renamed in the same chain
2. `verify_change` against the interface's signature with the new name
3. `simulate_chain` with one step per implementor file so `broken_implementors` carries forward correctly
4. Apply via `batch_edit` (or `apply_code_action` if you went the LSP route per-implementor)

## Checklist

- `search_symbols` + `winnow_symbols` until exactly one ID resolves to the symbol you mean
- `explain_change_impact` + `find_usages` before authoring any edit
- `verify_change` on the new signature — catches interface breaks before anything is written
- Pick `rename_symbol` (graph path) or LSP `refactor.rename` (server path); never hand-author cross-file renames
- `preview_edit` / `simulate_chain` on the generated edits before disk
- `batch_edit` is dependency-ordered + re-indexes between steps — atomic in the sense that matters for the graph
- `check_guards`, `get_test_targets`, `diff_context` after the apply
=== home/.hermes/skills/refactoring/gortex-safe-edit/SKILL.md ===
---
name: gortex-safe-edit
description: "Preview an edit's blast radius on the shadow graph before writing."
version: 1.0.0
metadata:
  hermes:
    tags: [code-intelligence, mcp, safe-edit]
    category: refactoring
    platforms: [linux, macos, windows]
    related_skills: [gortex]
---
# Safe Edit with Gortex (speculative-execution path)

Use this **before** you touch `edit_file` / `edit_symbol` / `batch_edit` / `Edit` / `Write`. It runs the edit against the in-memory shadow graph first, reports broken callers + broken interface implementors + blast-radius rollup + suggested tests, and only promotes to disk once those signals are clean.

## Workflow (do not skip steps)

```
1. smart_context({task: "<what you intend to change>"})              -> Working-set bundle
2. surface_memories({task, symbol_ids})                              -> Cross-session invariants on the same symbols
3. get_editing_context({path: "<file>"})                             -> Pre-edit signature + caller map
4. preview_edit({edit: <LSP WorkspaceEdit>})                         -> Single-shot speculative apply on the shadow graph
   -> Inspect: touched / added / removed / renamed / broken_callers / broken_implementors / impact / suggested_tests
5. simulate_chain({steps: [{edit: <e1>}, {edit: <e2>}, ...],         -> Multi-step ordered simulation
                   inherit_overlay: true, stop_on_error: true,
                   keep: false})
   -> Use when the change is more than one WorkspaceEdit (cross-file refactor, signature + every caller, etc.)
6. If broken_callers or broken_implementors is non-empty            -> Fix the chain first, re-run preview_edit / simulate_chain
7. check_guards({ids: [<changed-ids>]})                              -> Team conventions from .gortex.yaml
8. verify_change({id: <id>, new_signature: "..."})                   -> Final signature check before disk write
9. batch_edit({edits: [...]})                                        -> Apply the same edits on disk in dependency order
10. detect_changes({scope: "all"}) + diff_context({scope: "all"})    -> Confirm the on-disk diff matches the simulated one
11. get_test_targets({ids: [<changed-ids>]})                         -> Tests to re-run (cross-repo aware)
```

## When to use `preview_edit` vs `simulate_chain`

| Situation                                              | Tool             | Why |
| ------------------------------------------------------ | ---------------- | --- |
| One self-contained `WorkspaceEdit` (e.g. one rename)        | preview_edit     | Single round-trip, simplest. |
| Several dependent edits that must be applied in order  | simulate_chain   | First-failing-step semantics; `inherit_overlay` carries state forward. |
| You want the simulated state to become the live overlay so subsequent queries see it | simulate_chain with `keep: true` | Promotes the final shadow state into a real overlay session — graph queries see post-edit reality without writing disk. |
| Optional LSP diagnostics on the simulated content      | Either, with `with_diagnostics: true` | Drives `didChange(simulated) → wait → didChange(original)` round-trip; concurrent sessions never observe simulated state as authoritative. |

## Reading the speculative report

- **broken_callers** — callers whose call sites won't type-check against the new signature. Fix or update every one before disk.
- **broken_implementors** — interface implementors that no longer satisfy the interface contract. Either widen the contract, update implementors, or revert.
- **impact** — `analysis.AnalyzeImpact` blast-radius rollup. The same risk tiers `/gortex-impact` reports.
- **suggested_tests** — what to feed to `get_test_targets` after the apply.
- **added / removed / renamed** — non-trivial-signature unambiguous-candidate heuristic; bare `func ()` voids reject as ambiguous, so you may see fewer "renamed" entries than you expect — that is correct behaviour.

## Checklist

- `smart_context` to orient a non-trivial task (skip for a single known symbol — use `search_symbols` / `get_symbol_source`)
- `surface_memories` on the same working set — pick up cross-session decisions / gotchas
- `preview_edit` (single edit) or `simulate_chain` (ordered chain) **before** any on-disk write
- Treat `broken_callers` / `broken_implementors` as blockers, not warnings
- `check_guards` + `verify_change` after the simulated diff is clean, before `batch_edit`
- `batch_edit` (dependency-ordered, atomic re-index between steps) over manual sequencing
- `detect_changes` + `diff_context` to confirm the on-disk diff matches the simulation
- `get_test_targets` to run the right tests, cross-repo aware
- If the edit is durable knowledge ("X must hold the lock", "this package never uses gob"), `store_memory` so the next agent inherits the lesson
=== home/.hermes/skills/testing/gortex-add-test/SKILL.md ===
---
name: gortex-add-test
description: "Add tests for under-tested code and coverage gaps."
version: 1.0.0
metadata:
  hermes:
    tags: [code-intelligence, mcp, add-test]
    category: testing
    platforms: [linux, macos, windows]
    related_skills: [gortex]
---
# Add Tests with Gortex (coverage-led)

Use this when the user wants tests added for under-covered code, untested symbols, or a specific regression. Walks the coverage analyzers first so the new test goes where it matters; uses `suggest_pattern` + `scaffold` to author the new test in the style of an existing one rather than from scratch.

## Workflow (do not skip steps)

```
1. gortex enrich coverage  (CLI)                                       -> Stamp meta.coverage_pct on executable symbols from cover.out
2. analyze({kind: "coverage_summary", path_prefix: "<dir>/"})          -> Per-directory rollup (avg / covered / partial / uncovered)
3. analyze({kind: "coverage_gaps",                                     -> Symbols inside [min_pct, max_pct) — the candidate list
            path_prefix: "<dir>/",
            min_pct: 0, max_pct: 50})
4. get_untested_symbols({path_prefix: "<dir>/"})                       -> Symbols with no covering test
5. For the chosen target symbol:
     get_editing_context({path: "<file>"})                             -> Signature, callers, callees
     get_callers({id: "<id>"})                                         -> How the symbol is invoked in real code (drives test inputs)
6. suggest_pattern({example_id: "<similar tested symbol>"})            -> Extracts the test-authoring pattern (source + registration + tests)
7. scaffold({kind: "test", from: "<example>", target: "<new test>"})   -> Generate test stub + wiring
8. preview_edit({edit: <generated WorkspaceEdit>}) -> apply / batch_edit -> Speculative apply, then on-disk
9. get_test_targets({ids: [<symbol-under-test>]})                      -> Confirms the new test file is discovered + maps to a run command
10. Run the test command; if green, re-check `analyze kind=coverage_gaps` to confirm the gap closed
11. check_guards + detect_changes + diff_context                       -> Standard post-edit gates
```

## Picking the right target

| Source of priority                  | Tool                                                        | Use when |
| ----------------------------------- | ----------------------------------------------------------- | -------- |
| User named the symbol               | `search_symbols` -> `get_untested_symbols` (confirm gap)            | Direct request. |
| Recent regression                   | `detect_changes` -> `analyze kind=coverage_gaps` on the change set   | "Add a test for what just broke." |
| Hot path with weak coverage         | `analyze kind=hotspots` -> filter by `coverage_pct` < 50           | High-impact gaps. |
| Whole directory                     | `analyze kind=coverage_summary` -> walk lowest-coverage subdirs   | "Cover this package." |
| Mutation hotspot without tests       | `analyze kind=field_writers` + `get_untested_symbols`              | Race / state-mutation risk. |

## Authoring the test

`suggest_pattern` returns the **shape** of a comparable test in the project (table-driven, parallel, golden-file, etc.) so the new test feels native. `scaffold` then materialises the file with the right imports, package, and registration. Hand-edit only the bits `scaffold` couldn't infer (inputs, expected values, edge cases).

When the target is interface-implementing or LSP-driven, also run `find_implementations` and ensure each implementor has at least one test in the new file (table-driven test with one row per implementor is idiomatic in this repo).

## Closing the loop

After the new test runs green:
1. `gortex enrich coverage` (CLI) to refresh `meta.coverage_pct`
2. Re-run `analyze kind=coverage_gaps` over the same scope
3. Confirm the symbol is no longer in the gap list; if it still is, the test exercises a different branch — add another row

## Checklist

- Run `gortex enrich coverage` (CLI) at least once per session so the coverage analyzers have data to work with
- `analyze kind=coverage_summary` for the per-directory rollup; `analyze kind=coverage_gaps` for the per-symbol list
- `get_untested_symbols` is the truth for "zero covering tests" — coverage_pct=0 ≠ "no test"; some tests register but don't execute
- `suggest_pattern` + `scaffold` for the test body — author in the project's style, not from scratch
- `preview_edit` even for new files; the speculative report flags broken_callers if you accidentally add a name that collides
- `get_test_targets` after the apply — the test must be discoverable + mapped to a run command
- Re-run `analyze kind=coverage_gaps` once the test is green; confirm the gap actually closed (a passing test that doesn't exercise the gap is theatre)
- If the test encodes a project-wide invariant ("Bar must hold the lock"), `store_memory kind:invariant` so the next agent inherits the constraint
