mirror of
https://github.com/microsoft/BCQuality.git
synced 2026-08-06 17:36:53 +01:00
Retire the orchestrator's hardcoded ~28-token signal catalog by compiling a routing index from article front-matter. New tools/Build-RoutingIndex.ps1 scans all knowledge layers and emits routing-index.json (signal-token -> domain + backing articles), seeded from the migrated legacy catalog (routing-seed.json) so recall is never below today. Optional per-article 'signals:' front-matter (validator rule R24) is the authored precision path; domain-normalization reconciles front-matter/orchestrator/feedback domain vocabularies. Artifact is a runtime build (gitignored), orchestrator-facing only (not the lean agent-replayed knowledge index). CI guard Test-RoutingIndex.ps1 asserts determinism, seed recall floor, no orphaned signals, and normalization completeness; wired into the index workflow. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
113 lines
6 KiB
Markdown
113 lines
6 KiB
Markdown
# Routing index
|
|
|
|
The **routing index** (`routing-index.json`) is the orchestrator-facing companion
|
|
to the [knowledge index](../tools/Build-KnowledgeIndex.ps1). Where the knowledge
|
|
index lets the *agent* enumerate candidate articles inside the CLI run, the
|
|
routing index lets the *orchestrator* decide — deterministically, before any
|
|
tokens are spent — which review domains a PR touches and which articles back each
|
|
detection signal.
|
|
|
|
It exists to retire a shadow catalog. The PR-review orchestrator historically
|
|
carried a hand-written ~28-token regex catalog (`$BcqSignalCatalog`) hardcoded in
|
|
`Invoke-CopilotPRReview.ps1`. That catalog never read the `domain` / `keywords`
|
|
front-matter every article already declares, so its blind spots were exactly
|
|
where findings were missed. The routing index moves that knowledge into content:
|
|
signals and their domains are compiled *from articles*, so a new article — even a
|
|
community one — becomes routable the moment it lands, with no orchestrator edit.
|
|
|
|
## What it is (and is not)
|
|
|
|
- **It is** an orchestrator-side artifact. It is consumed by `Build-ReviewManifest`
|
|
(behind the `BCQ_INDEX_V2` flag) to score per-domain suspicion and shortlist
|
|
candidate articles. It is **never** fed into the CLI prompt, so it can be richer
|
|
than the lean knowledge index without inflating the token-paid path.
|
|
- **It is not** committed. Like `knowledge-index.json`, it is a runtime artifact
|
|
rebuilt over each consumer's already-pruned clone (`tools/Build-RoutingIndex.ps1`)
|
|
and is `.gitignore`d.
|
|
- **It is not** the knowledge index. The two are siblings built from the same
|
|
front-matter; the knowledge index is agent-replayed, the routing index is not.
|
|
|
|
## How signals are compiled
|
|
|
|
`Build-RoutingIndex.ps1` composes signals from three sources, highest precision
|
|
first:
|
|
|
|
1. **Seed** (`tools/routing-seed.json`) — the migrated legacy catalog. Ingested
|
|
first so routing recall is never below what the old hardcoded catalog caught.
|
|
Each seed signal is then enriched with **every** article whose (normalized)
|
|
domain matches, so recall is complete-by-construction rather than limited by a
|
|
hand-maintained token→article map.
|
|
2. **Article-declared** (`signals:` front-matter, optional) — the authored,
|
|
highest-precision path. Use it when an article catches a specific AL construct
|
|
the seed does not name.
|
|
3. **Keyword-derived** (`-IncludeKeywordSignals`, off by default) — soft signals
|
|
from article keywords at reduced weight. Off by default because keywords are
|
|
lowercase-kebab and match noisily, and the online-evals feedback shows the agent
|
|
already over-fires. Prefer authored `signals:` for precision.
|
|
|
|
## The optional `signals:` front-matter block
|
|
|
|
Knowledge articles may declare an **optional** `signals:` block. When omitted, the
|
|
article is still routed via the seed + its domain (fully back-compatible — no
|
|
existing article must change). Each entry is either a bare token string or a
|
|
mapping:
|
|
|
|
```yaml
|
|
domain: performance
|
|
keywords: [lock, readonly, isolation]
|
|
signals:
|
|
- LockTable # bare token -> pattern \bLockTable\b, domain = article domain
|
|
- token: ReadIsolation
|
|
pattern: '\bReadIsolation\b' # explicit regex (optional)
|
|
domain: performance # override domain (optional; normalized)
|
|
```
|
|
|
|
- `token` (required) — stable signal id; need not be an AL identifier verbatim.
|
|
- `pattern` (optional) — regex matched against **added** diff lines. Defaults to
|
|
`\b<token>\b`.
|
|
- `domain` (optional) — defaults to the article's `domain`, then normalized.
|
|
|
|
The front-matter validator (`.github/scripts/validate_frontmatter.py`, rule R24)
|
|
enforces this shape; `signals` is the only optional key in the otherwise-closed
|
|
knowledge key set.
|
|
|
|
## Domain normalization
|
|
|
|
Three vocabularies describe the same domains and must be reconciled:
|
|
|
|
| Front-matter (article) | Orchestrator (`$DomainMap`) | Feedback (`byDomain`) |
|
|
|------------------------|-----------------------------|-----------------------|
|
|
| `ui` | `Accessibility` | `accessibility` |
|
|
| `error-handling` | `Error Handling` | — |
|
|
| `web-services` | `Web Services` | — |
|
|
| `breaking-changes` | `Breaking Changes` | — |
|
|
| `events` / `interfaces`| `Events` / `Interfaces` | — |
|
|
| `telemetry` | `Privacy` (folded) | — |
|
|
|
|
`routing-seed.json → domain-normalization` maps front-matter domains to the
|
|
orchestrator's canonical TitleCase taxonomy so seed signals (authored in that
|
|
taxonomy) attach to the right articles. `appsource` is a documented **pass-through**
|
|
domain (indexed, but has no review leaf skill, so it routes only via the
|
|
`al-code-review` super-skill). The CI guard fails if a new front-matter domain
|
|
appears that is neither normalized nor a pass-through, keeping the map complete as
|
|
content grows.
|
|
|
|
## Layer-replication story
|
|
|
|
The whole process is open and per-layer. A partner running the pipeline against
|
|
their own `community/` or `custom/` layers gets routing for free: their articles'
|
|
front-matter (and any `signals:` they add) compile into the same index by the same
|
|
generator. No Microsoft-only data is required to build Tier 1. The later Tier 2
|
|
feedback overlay (precision weights distilled from online evals) is a separate,
|
|
aggregate-only artifact that multiplies onto these Tier-1 weights — each layer
|
|
carries its own overlay produced from its own feedback.
|
|
|
|
## Files
|
|
|
|
| File | Role |
|
|
|------|------|
|
|
| `tools/Build-RoutingIndex.ps1` | Generator (scan layers → `routing-index.json`) |
|
|
| `tools/routing-seed.json` | Content-owned seed: migrated catalog + domain-normalization + object-kind hints |
|
|
| `tools/routing-index.schema.json` | JSON Schema for the compiled artifact |
|
|
| `.github/scripts/Test-RoutingIndex.ps1` | CI guard (determinism, recall floor, no orphans, normalization complete) |
|
|
| `.github/workflows/knowledge-index.yml` | Runs both index guards on PR/push to main |
|