# How the routing.json Priority Array Determines PRIMARY Skill Selection

> Learn how the routing.json priority array in reverse-skill determines primary skill selection by acting as a tie-breaker for identical keyword scores. Higher precedence for earlier array positions.

- Repository: [ZhaoXu/reverse-skill](https://github.com/zhaoxuya520/reverse-skill)
- Tags: internals
- Published: 2026-08-28

---

**The `priority` array in [`routing.json`](https://github.com/zhaoxuya520/reverse-skill/blob/main/routing.json) serves as a deterministic tie-breaker that selects the PRIMARY skill when multiple routes achieve identical keyword match scores, with earlier positions in the array indicating higher precedence.**

The `reverse-skill` repository implements a rule-based routing system to dispatch reverse-engineering tasks to specialized handlers. Central to this architecture is [`skills/config/routing.json`](https://github.com/zhaoxuya520/reverse-skill/blob/main/skills/config/routing.json), which defines keyword matching rules, a `fallbackId`, and the ordered `priority` array that resolves competition between equally-scored candidates.

## The PRIMARY Selection Algorithm

The routing engine implemented in `skills/scripts/master-route.ps1` executes a three-phase selection process that transforms user hints into deterministic skill assignments.

### Phase 1: Keyword Scoring and Candidate Generation

Each route defined in the `routes` object contains a `keywords` array with inclusion and exclusion patterns. The engine iterates through every route and increments a score for each matching rule. According to lines 5-7 of [`routing.json`](https://github.com/zhaoxuya520/reverse-skill/blob/main/routing.json), this process builds a candidate set where each route ID maps to its accumulated match count.

### Phase 2: Tie-Breaking with the Priority Array

When multiple routes share the maximum score, the router consults the top-level `priority` array (lines 24-29) to break the deadlock. This ordered list of route IDs functions as a precedence hierarchy; the router selects the first route ID appearing in the `priority` array among the tied candidates. This ensures that even with overlapping keyword patterns, the system produces a single, predictable PRIMARY assignment.

### Phase 3: Fallback Assignment

If no keyword rules match the input hint, the candidate set remains empty. In this scenario, the router immediately returns the `fallbackId` specified in `meta.scoring` (typically `"R0"`), ensuring the system always has a valid skill to execute.

## Implementation in master-route.ps1

The PowerShell entry point `skills/scripts/master-route.ps1` materializes this logic by loading the configuration, calculating scores, and applying the priority-based tie-breaker:

```powershell

# Load routing configuration from repository root

$cfg = Get-Content -Raw -Path "$PSScriptRoot/../config/routing.json" | ConvertFrom-Json

# 1. Score each route based on keyword matches

$scores = @{}
foreach ($id in $cfg.routes.Keys) {
    $score = 0
    foreach ($kw in $cfg.routes[$id].keywords) {
        if ($Hint -match $kw.must -and -not ($Hint -match $kw.exclude)) {
            $score++
        }
    }
    $scores[$id] = $score
}

# 2. Identify highest-scoring candidates

$maxScore = ($scores.Values | Measure-Object -Maximum).Maximum
$candidates = $scores.GetEnumerator() |
              Where-Object { $_.Value -eq $maxScore } |
              Select-Object -ExpandProperty Key

# 3. Resolve ties using priority order; fallback if no matches

$primary = $cfg.priority | Where-Object { $candidates -contains $_ } | Select-Object -First 1
if (-not $primary) { $primary = $cfg.meta.fallbackId }

# 4. Load the selected skill definition

$skillPath = Join-Path $PSScriptRoot ".." $cfg.routes[$primary].skill

```

This implementation strictly follows the algorithm documented in the configuration metadata: it respects the `priority` array only after establishing score parity, and respects the `fallbackId` only when the candidate set is empty.

## Routing Configuration Structure

The [routing.json](https://github.com/zhaoxuya520/reverse-skill/blob/main/skills/config/routing.json) file schema separates concerns between scoring rules and precedence ordering:

```json
{
  "meta": {
    "scoring": "每条关键字规则命中后计入候选集；按 priority 数组顺序取『命中分数最高』的为 PRIMARY；分数并列时 priority 靠前者胜出；未命中任何规则时回退 fallbackId。",
    "fallbackId": "R0"
  },
  "priority": ["R4", "R1", "R2", "R3"],
  "routes": {
    "R1": {
      "skill": "skills/core/apk.yml",
      "keywords": [{"must": "apk", "exclude": ""}]
    },
    "R4": {
      "skill": "skills/core/ios.yml", 
      "keywords": [{"must": "ipa|ios", "exclude": "android"}]
    }
  }
}

```

The `meta.scoring` field explicitly documents that when scores are equal, the route appearing earlier in the `priority` array wins. This separation allows operators to adjust routing precedence without modifying complex keyword regex patterns.

## Summary

- The `priority` array determines tie-breaking precedence only after keyword scoring identifies multiple candidates with identical maximum scores.
- Routes appearing earlier in the `priority` list possess higher authority; the router selects the first matching candidate from this ordered list.
- When no keywords match, the system defaults to the `fallbackId` specified in `meta.scoring` within [routing.json](https://github.com/zhaoxuya520/reverse-skill/blob/main/skills/config/routing.json).
- The [master-route.ps1](https://github.com/zhaoxuya520/reverse-skill/blob/main/skills/scripts/master-route.ps1) script implements this logic deterministically, ensuring predictable skill selection even with ambiguous input hints.
- Validation scripts like [verify-routing-coherence.ps1](https://github.com/zhaoxuya520/reverse-skill/blob/main/skills/scripts/verify-routing-coherence.ps1) ensure the `priority` array remains synchronized with documentation.

## Frequently Asked Questions

### What happens if two routes tie in score but only one appears in the priority array?

The route listed in the `priority` array wins; if both appear, the one with the lower array index (earlier position) is selected. Routes absent from the `priority` array cannot become PRIMARY unless they uniquely achieve the highest score without ties, though the fallback mechanism still applies if they are the explicit `fallbackId`.

### Can the priority order be changed without modifying keyword rules?

Yes. The `priority` array operates independently of the scoring mechanism. Reordering route IDs in this list immediately changes tie-breaking behavior without affecting how individual routes accumulate keyword match scores, allowing non-invasive routing adjustments.

### Where is the fallback skill defined when no keywords match?

The fallback route ID is defined in [`routing.json`](https://github.com/zhaoxuya520/reverse-skill/blob/main/routing.json) under the `meta.fallbackId` property, conventionally set to `"R0"`. This ID must correspond to a valid key in the `routes` object, as implemented in [master-route.ps1](https://github.com/zhaoxuya520/reverse-skill/blob/main/skills/scripts/master-route.ps1).

### How does the system validate routing configuration changes?

The repository includes [verify-routing-coherence.ps1](https://github.com/zhaoxuya520/reverse-skill/blob/main/skills/scripts/verify-routing-coherence.ps1), which cross-references the `priority` array against the human-readable table in [`MASTER-ROUTING.md`](https://github.com/zhaoxuya520/reverse-skill/blob/main/MASTER-ROUTING.md) and validates that all referenced route IDs exist, preventing runtime errors due to configuration drift.