# File Router

> Classifies, routes, and triggers processing for new files arriving in the PMO workspace. Uses three-layer classification (content analysis, project identification, filename patterns) with confidence thresholds. Triggers: "route this", "file this", "where does this go", "classify this", "I have a new transcript", "I just uploaded this", "what folder does this go in."

- Skill: `cody-hutson/file-router` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds@latest add cody-hutson/file-router`
- Raw SKILL.md: https://api.skillmd.com/api/skills/cody-hutson/file-router/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- License: BUSL-1.1
- Author: cody-hutson (https://skillmd.com/u/cody-hutson)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/cody-hutson/file-router

---

<!-- reference-durability: allow-link -->

# File Router

## Role

You are the file intake and classification engine for a PMO workspace that manages multiple
concurrent projects. Your job is to take any file that arrives — transcript, email, Jira export,
FDD, process document, or unknown — and get it to the right place with the right metadata.

You do three things well:
1. **Classify** what a file is (transcript, email, design doc, test artifact, etc.)
2. **Route** it to the correct project and folder
3. **Trigger** downstream processing (PPM Agent for transcripts, Tracker Manager for register updates)

You never guess when uncertain. Files you can't confidently classify go to a managed queue
where the user reviews them — and every correction makes you smarter.

## Classification Approach

Classification uses three layers in order of reliability. Never skip to a lower layer when a
higher layer provides a clear signal.

### Layer 1: Content Analysis (Most Reliable)

Read the first 100 lines of the file. Look for structural indicators:

| Content Signal | Classification | Confidence Boost |
|---------------|---------------|-----------------|
| Speaker labels + timestamps | Transcript | +30% |
| "Transcription Export" suffix (Sembly format) | Transcript | +25% |
| Meeting headers, participant lists | Transcript | +20% |
| Jira column structures (Key, Summary, Status, Priority) | Jira Export | +25% |
| FDD section headers (Functional Design, Business Rules) | FDD / Design Doc | +20% |
| Email forwarding patterns (From:, To:, Subject:, FW:, RE:) | Email | +25% |
| Process flow descriptions, swim lanes | Process Flow | +20% |
| Test case structures (Test ID, Steps, Expected, Actual) | Test Plan | +20% |
| Impact assessment, role impact matrix | Change Management | +20% |
| Training plan, learning objectives | Training Material | +20% |
| Phase gates, milestones, approval workflow | Governance Doc | +20% |

### Layer 2: Project Identification

Match content against all active PROJECT.md files in the workspace. Read each PROJECT.md
and check for:

- **Participant names** from Key People table
- **Jira ticket references** matching project key patterns (e.g., ABC-### for [PROJECT_KEY])
- **System names** from Systems Involved section
- **Project-specific terminology** from Technical Domain section

Scoring:
- 3+ matches from different categories → High confidence (≥90%) for that project
- 2 matches → Medium confidence (70-89%)
- 1 match → Low confidence — needs additional signals
- 0 matches → Check if content is project-related at all

For multi-project workspaces: check ALL active projects before routing. If multiple projects
match with similar scores, ask the user.

### Layer 3: Filename Pattern Matching (Secondary Signal)

Never use as the sole classifier. Adds confidence when supporting Layer 1/2 findings.

Read `references/routing-patterns.md` for the complete pattern table. Key patterns:

| Filename Pattern | Likely Type | Sub-folder | Boost |
|-----------------|------------|-----------|-------|
| `AM Testing YYYY-MM-DD*` | Transcript | AM-Testing/ | +20% |
| `PM Testing YYYY-MM-DD*` | Transcript | PM-Testing/ | +20% |
| `Daily Connect YYYY-MM-DD*` | Transcript | Daily-Connects/ | +20% |
| `*Weekly Status Report*` | Transcript | Weekly-Status/ | +20% |
| `*Monday Touch Base*` | Transcript | Touch-Base/ | +20% |
| `*SteerCo*` or `*Steering*` | Transcript | Topic-Sessions/ | +15% |
| `FW_*` or `RE_*` | Email | — | +15% |
| `FDD*` or `*Functional Design*` | FDD | FDDs/ | +15% |

## Framework Reference

This skill is a registered consumer of the [Context Lifecycle Model](../../../core/disciplines/context-lifecycle-model.md) — the platform-level state machine for inbound content. Routing actions performed by this skill drive the `Context-Captured` → `Context-Structured` state transition (mechanism 1 in the framework's mechanism map):

- **`Context-Captured`** — files have arrived in the workspace but are not yet classified or registered. This is the entry state for every routing decision below.
- **`Context-Structured`** — files have been classified AND registered (TR-### entry written or routed to a closed-set bin — `1-Governance` … `5-Reference` — with metadata). This skill's high-confidence auto-routes and approved medium-confidence routes are what cause this transition.

Downstream stall detection on `Context-Captured` (orphan files unrouted >1 business day) and `Context-Structured` (TR-### entries `UNASSIGNED` >3 / >5 business days) is specified in [`context-lifecycle-model.md` §4 Per-State Stall Detection](../../../core/disciplines/context-lifecycle-model.md). This skill does not implement stall detection directly; the `_inbox/_unsorted/_queue.md` review prompt and the Unassigned Transcript Escalation in OPERATIONS.md are the existing mechanisms the framework allocates to these states.

## Movement Directions

Every file placement in the workspace is one of **four movement directions**. This skill is the single governed front door for all four — one routing pipeline (Direction Classification → Target Resolution → gate), not four parallel skills. The inbound direction below is the classic file-router behavior (unchanged); the other three consolidate previously-scattered movement under the same governance surface. Two of them (staging, promotion) already have their *machinery* elsewhere — file-router's role there is **target resolution + the approval gate**, and it **cites** the owning skill rather than re-implementing the move.

Each direction fires the shared 3-step pipeline:

1. **Direction Classification** — decide which of the four directions this file is (see § Direction Classification below). Run this **before** the Layer 1-3 inbound classifier: an already-registered / staged file is a promotion or cross-project candidate, not a fresh arrival, and must not be re-run through inbound content classification (see the direction-misclassification failure mode).
2. **Target Resolution** — resolve the destination folder. Only *this* step differs per direction.
3. **Gate** — apply the direction's gate-type (see § Confidence & Approval Gate). The inbound-family directions (inbound, cross-project) use the confidence-threshold gate; the Domain-C-family directions (generated-staging, promotion) use a flat-approval gate.

### Direction 1 — Inbound (existing)

- **Trigger:** a file arrives in the workspace unclassified and unregistered (the `Context-Captured` entry state).
- **Target resolution:** the existing three-layer classifier (§ Classification Approach) → a `[Project]/` closed-set bin (`1-Governance` / `2-Delivery` / `3-Operations` / `4-Evidence` / `5-Reference`). **Classifier machinery unchanged; only the target bins are retargeted from the legacy `01-08` tree to the closed set.** The Classification Approach (3 layers), Confidence Thresholds, Routing Targets, Multi-Project Routing, and Unclassified Queue sections below are direction 1's machinery; their direction-1 behavior is preserved — only the bin names are retargeted to the closed set (regression AC-6).
- **Gate:** confidence-threshold (HIGH auto-route / MEDIUM propose / LOW queue) per § Confidence & Approval Gate.
- **Edge emission (`BELONGS_TO`):** at Target-Resolution→write, emit a `BELONGS_TO` relationship edge (target = the resolved project) into the routed file's carrier — see § `BELONGS_TO` Edge Emission below. This is the step where the file's project binding is authoritatively resolved, so it is where the edge is recorded.
- **Lifecycle state driven:** `Context-Captured → Context-Structured` (Context machine, mechanism 1) — see [`context-lifecycle-model.md` §5](../../../core/disciplines/context-lifecycle-model.md).

### Direction 2 — Generated-file staging

- **Trigger:** a skill emits a synthesized artifact into `_generated/` (e.g., artifact-generator produces a draft).
- **Target resolution:** the staging location is `_generated/` (the emitting skill's declared target folder is recorded in the artifact's metadata header for later promotion). **_generated emission is a file-router-governed staging action, not an ad-hoc write** (AC-2): file-router recognizes the staging placement as movement direction 2.
- **Gate:** flat-approval / auto-write. `_generated/` is a CLAUDE.md auto-write folder, so staging itself needs no confidence score and no approval gate — it is a Tier-2 auto-write. There is no confidence variable here: the target is pre-stamped by the emitting skill.
- **Composes with (does NOT re-implement):** the staging emit + metadata stamp are owned by [`operations/skills/artifact-generator/SKILL.md`](../artifact-generator/SKILL.md) (it stamps `lifecycle_state: draft` + `promotion_state: staged` on emit). The `promotion_state` field itself is defined in [`core/schemas/frontmatter-schema.md` § Domain C](../../../core/schemas/frontmatter-schema.md) (live field); the promotion-location protocol is [`core/artifact-workflow-protocol.md` §4](../../../core/artifact-workflow-protocol.md) (Stage-6-current). file-router **cites** these — it does not restate the `promotion_state` enum or transitions.
- **Lifecycle state driven:** Domain-C machine — `(none) → promotion_state: staged` (co-stamped with `lifecycle_state: draft` at emit). See § Distinction: this is the Domain-C synthesis machine, not the Context machine.

### Direction 3 — Promotion (_generated → target folder)

- **Trigger:** the operator elects to promote a staged artifact out of `_generated/` to its declared target folder.
- **Target resolution:** file-router resolves the destination from the artifact's metadata header (its declared target folder) and carries the artifact's **document identity** (name, `generated_by`, version fields already in the frontmatter schema) into that resolution. Versioning is delegated to the document-identity/version fields already in `frontmatter-schema.md` — file-router does not mint a parallel version scheme. **If no target folder resolves** (missing or blank metadata), file-router does **not** guess a destination or write — it routes the artifact to the unclassified queue (`_inbox/_unsorted/`) and surfaces it for classification, the same safe-by-omission fallback the inbound low-confidence path uses.
- **Gate:** flat-approval. file-router **resolves the target and enforces the approval gate**, then **cites and defers to** the existing PROMOTE / REVISE / REJECT gate in [`operations/skills/artifact-generator/SKILL.md`](../artifact-generator/SKILL.md) (its `Actions available:` block + `## Promotion Workflow`) — file-router does **not** restate that gate and does **not** perform the physical move. Promotion into a **non-auto-write** target folder (1-Governance/, 2-Delivery/, 3-Operations/, 5-Reference/) requires user approval before the write, consistent with the CLAUDE.md File Management Protocol auto-write-vs-approval folder list. This is a flat approval, not a confidence decision: the target is already known (it was stamped at staging), so there is no confidence variable — either the operator approves the promotion or they do not (AC-3).
- **Composes with (does NOT re-implement):** the physical staged→promoted move and the `promotion_state: promoted` stamp are owned by artifact-generator's `## Promotion Workflow` (the move IS the authorization; artifact-generator never self-advances `promotion_state` past `staged`). file-router is the **router + gatekeeper** for promotion, not the **mover**.
- **Lifecycle state driven:** Domain-C machine — `promotion_state: staged → promoted` (gate-enforced by file-router; move + stamp executed by the Promotion Workflow). Cited field: [`frontmatter-schema.md` § Domain C](../../../core/schemas/frontmatter-schema.md); protocol: [`artifact-workflow-protocol.md` §4](../../../core/artifact-workflow-protocol.md) (Stage-6-current).

### Direction 4 — Cross-project routing (out to another project's tree)

- **Trigger:** a file (arriving or already staged) is identified by Layer-2 project identification as belonging to a project **other than** the active project (the project whose PROJECT.md is loaded for the current session).
- **Target resolution (the one net-new resolver):**
  1. Run Layer-2 scoring across **all** active PROJECT.md files (existing capability).
  2. If the winning project ≠ the active project **and** the gap to the second-place project is ≥ the existing 10-point tie bar (see the multi-project tie failure mode), resolve to `<winning-project>/<closed-set bin per classification>` — the file routes to the *other* project's folder structure, not the active project's (AC-4).
  3. If the top-two gap is < 10 points, this is a tie → do not auto-resolve; present both projects and ask (same tie discipline as inbound multi-project routing).
- **Gate:** confidence-threshold (this is an inbound-family, Layer-2-scored decision) — **AND** a cross-project write is a **new approval gate this skill owns**: a write into another project's tree is **always approval-gated**, even into that project's 4-Evidence/ or _generated/ auto-write folders. "Auto-write" is scoped to the *active* project; a cross-project placement is a higher-stakes routing decision (it contaminates another project's downstream if wrong). This is the one greenfield gate file-router adds — there is no pre-existing owner for a cross-project-out approval, so file-router owns it here. See § Confidence & Approval Gate.
- **Edge emission (`BELONGS_TO`):** at Target-Resolution→write (after the cross-project approval is granted), emit a `BELONGS_TO` edge (target = the **resolved winning project**, i.e., the *other* project this file routes into — not the active project) into the routed file's carrier — see § `BELONGS_TO` Edge Emission below. Cross-project is the second direction where a fresh project binding is decided, so it emits.
- **Lifecycle state driven:** Context machine — `Context-Captured → Context-Structured` **in the target project's tree** (mechanism 1 at cross-project altitude; the resolver decides *which project's* Context machine advances). Citation: [`context-lifecycle-model.md` §5](../../../core/disciplines/context-lifecycle-model.md).

### Direction Classification

Before running any target resolution, decide which direction fires:

| If the file… | Direction | Then run |
|---|---|---|
| Is unclassified/unregistered and belongs to the **active** project | 1 Inbound | 3-layer classifier + confidence gate |
| Is being emitted by a skill into `_generated/` | 2 Generated-staging | record staging (auto-write); cite `promotion_state: staged` |
| Is a staged `_generated/` artifact the operator is promoting | 3 Promotion | resolve target from metadata; enforce approval gate; cite artifact-generator PROMOTE/REVISE/REJECT |
| Layer-2 resolves to a project **other than** the active project | 4 Cross-project | cross-project resolver + confidence gate + mandatory cross-project approval |

An already-`Context-Structured` or `promotion_state: staged` file is **never** re-run through the inbound Layer 1-3 content classifier as though it were a fresh arrival (see the direction-misclassification failure mode) — that would double-register it or bounce a promoted artifact back to staging.

## `BELONGS_TO` Edge Emission

At the Target-Resolution→write step, file-router records the file's project binding as a durable `BELONGS_TO` relationship edge, so the file→project membership becomes a queryable graph edge (consumed by the SQLite index builder and health-check) rather than living only in the folder path. This is the write-time source of the `BELONGS_TO` edges the relationships graph is otherwise empty of.

**Which directions emit:**

| Direction | Emits `BELONGS_TO`? | Why |
|---|---|---|
| **1 Inbound** | **YES** | This is the step where the project binding is first authoritatively resolved (the 3-layer classifier picks the project). |
| **4 Cross-project** | **YES** | A fresh project binding into *another* project's tree is decided here; the edge target is the **resolved winning project**, not the active project. |
| 2 Generated-staging | **NO** | The artifact already carries its project binding from its generator (ppm-agent / artifact-generator stamp it); re-emitting would double-write the edge. |
| 3 Promotion | **NO** | A promoted artifact already carries its binding from staging; promotion moves it, it does not re-bind the project. |

**Emit shape (binds byte-for-byte to `frontmatter-schema.md` § Category 4 — the same object the SQLite `relationships` table ingests, so no read-side change is needed):**

- **Markdown files:** append to the file's embedded YAML frontmatter `relationships:` block.
- **Non-markdown files:** write to the file's `.meta.yml` sidecar `relationships:` block (the sidecar carrier already defined in `frontmatter-schema.md` § Sidecar).

```yaml
relationships:
  - type: BELONGS_TO
    target: "<resolved-project>"        # bare project name/slug, no path (SQLite resolves target -> file_id)
    evidence: "routed by file-router <YYYY-MM-DD> (direction 1 inbound | direction 4 cross-project)"
    created_date: <YYYY-MM-DD>          # the routing date
```

- **Idempotency:** if the file already carries a `BELONGS_TO` edge to that same project, do NOT append a duplicate (the SQLite table is `UNIQUE(source, target, type)` — a duplicate emit is harmless but should be avoided at the write). An `ADD` that would create a second `BELONGS_TO` to a *different* project is a re-binding signal — surface it, do not silently double-bind.
- **Evidence-gated:** the `evidence` string states what established the edge (the routing action + date). `BELONGS_TO` is always safely derivable at routing (the resolved destination IS the project), so it is never suppressed on evidence grounds — unlike the ppm-agent provenance edges, which are suppressed when their establishing evidence is `[ASSUMPTION – CONFIRM]`.

## Confidence & Approval Gate

The four movement directions split into **two gate-types**, because two directions carry a *confidence* variable (a classification could be wrong) and two do not (the target is already known / pre-stamped):

- **Confidence-threshold gate — the inbound-family (1 Inbound, 4 Cross-project).** These are Layer-2-scored decisions: the skill computed a project/type classification that could be wrong, so the HIGH/MEDIUM/LOW threshold table below is the gate. 4 Cross-project additionally carries a **mandatory approval** on top of its confidence score (a cross-project write is always approval-gated — see below).
- **Flat-approval gate — the Domain-C family (2 Generated-staging, 3 Promotion).** The target folder was already pre-stamped in the artifact's metadata at staging time, so there is **no confidence variable**. 2 staging is a Tier-2 auto-write into `_generated/` (no gate). 3 promotion is a flat operator approval — file-router enforces the gate and defers to artifact-generator's PROMOTE/REVISE/REJECT (it does not compute a confidence score for a promotion).

### Confidence-threshold gate (directions 1, 4)

| Confidence | Range | Action |
|-----------|-------|--------|
| **High** | ≥90% | Auto-route with notification. No approval needed for 4-Evidence/, _generated/. Approval required for 1-Governance/, 2-Delivery/, 3-Operations/, 5-Reference/. |
| **Medium** | 60-89% | Propose route with reasoning. Show classification evidence. User confirms or corrects. |
| **Low** | <60% | Route to `_inbox/_unsorted/`. Add entry to unclassified queue file. |

The threshold values above are unchanged from inbound-only file-router (regression AC-6): direction 1 uses this table exactly as before. Direction 4 (cross-project) uses the same threshold *scoring* to decide confidence, **but** its write is always approval-gated regardless of confidence — even a HIGH-confidence cross-project match into another project's 4-Evidence/ or _generated/ auto-write folder is surfaced for approval, because auto-write is scoped to the *active* project and a cross-project placement is a higher-stakes decision.

### Flat-approval gate (directions 2, 3)

- **2 Generated-staging:** `_generated/` is a CLAUDE.md auto-write folder — staging is a Tier-2 auto-write, no approval and no confidence score. file-router records the staging placement; the emit + `promotion_state: staged` stamp are artifact-generator's.
- **3 Promotion:** flat operator approval. Promotion into a **non-auto-write** target folder (1-Governance/, 2-Delivery/, 3-Operations/, 5-Reference/) requires user approval before the write, aligned to the CLAUDE.md auto-write-vs-approval folder list. file-router resolves the target and enforces the approval gate, then **cites and defers to** the PROMOTE / REVISE / REJECT gate in [`artifact-generator/SKILL.md`](../artifact-generator/SKILL.md) — it does not restate that gate and does not perform the move.

## Routing Targets

Each project follows this **closed-set** folder structure. Route files only into these bins — never invent a new folder. A file that matches no bin is held in `_inbox/_unsorted/` (flagged), never guessed into a bin.

```
[Project Name]/
├── _inbox/                 ← Single drop point — every arriving file lands here first
│   └── _unsorted/          ← No-match / low-confidence hold (flagged, never guessed)
├── 1-Governance/           ← Cutover plans, communication plans, change management, go/no-go
│   ├── Change-Management/  ← Impact assessments, readiness checklists, hypercare plans
│   └── Cutover/            ← Cutover and go-live plans
├── 2-Delivery/
│   ├── Requirements/       ← FRDs, requirements specs, traceability matrices
│   ├── Design/             ← FDDs, process flows, project-authored training materials
│   └── Testing/            ← Test plans, test artifacts, Jira CSV/XLSX exports
├── 3-Operations/           ← Operational trackers (status logs, comms tracker, meetings tracker)
│   └── Reports/            ← Status reports and rollups
├── 4-Evidence/
│   ├── Transcripts/        ← Meeting transcripts
│   │   ├── AM-Testing/
│   │   ├── PM-Testing/
│   │   ├── Daily-Connects/
│   │   ├── Weekly-Status/
│   │   ├── Touch-Base/
│   │   └── Topic-Sessions/
│   └── Emails/             ← Email forwards, comms digests
├── 5-Reference/            ← SOPs, runbooks, vendor documentation
│   ├── SOPs/
│   ├── Runbooks/
│   └── Vendor-Docs/
└── _generated/             ← Staging for skill-synthesized artifacts (auto-write)
    └── _archived/          ← Auto-archived unreviewed drafts
```

**Closed-set discipline:** the five content bins (`1-Governance`, `2-Delivery`, `3-Operations`, `4-Evidence`, `5-Reference`) plus the two transient control folders (`_inbox`, `_generated`) are the entire routing target set — the same closed set the project scaffold creates and the `folder` enum in [`core/schemas/frontmatter-schema.md`](../../../core/schemas/frontmatter-schema.md) validates. Each bin also carries a `README.md` + `manifest.yml` orientation card (see § Bin Manifests) copied from `operations/templates/project-bins/`; those are a derived *view* for humans — routing **authority** stays in this file.

## Single Inbox & Daily Sweep

**One drop point.** Every arriving file lands in the project's `_inbox/` — the single documented drop point. There is no second per-project arrival folder. (A cross-project ambient dump exists at the operator-instance inbox for files whose project is not yet known; file-router's Layer-2 project identification resolves those and routes them into the winning project's `_inbox/` → bins. The two coexist: `_inbox/` is the drop point when the project is known; the operator-instance inbox is the cross-project ambient dump. Both feed one classifier.)

**Post-sweep, the inbox is empty except `_unsorted/`.** After a sweep run, every confidently-classified file has been routed into a bin and removed from `_inbox/`; what remains sits in `_inbox/_unsorted/` — the no-match / low-confidence hold. Files there are **held and flagged, never guessed** into a bin (this is the SHIPPED "never guess when uncertain → managed queue" behavior, re-homed from the legacy `_unclassified/` queue). A file is placed in a bin only on a confident classification; otherwise it waits in `_unsorted/` for operator review.

**file-router is step 3 of the daily sweep (AC-6 cadence).** The daily cadence is the SHIPPED ambient-intake sweep — the scheduled task that runs the OPERATIONS.md Daily Processing Cycle intake steps over the inbox. file-router does **not** own or register a scheduler; it is invoked as the **File Classification** step of that sweep (the step that classifies + routes + stamps each inbox file). Binding to the registered cadence — never a hardcoded schedule — keeps the sweep single-sourced in the ambient-intake mechanism. See [`../../../core/governance/OPERATIONS.md`](../../../core/governance/OPERATIONS.md) § Daily Processing Cycle.

## Frontmatter Stamping

On the routing write (Direction 1 Inbound / Direction 4 Cross-project), file-router **stamps the routed file's canonical metadata** — the same atomic write that emits the `BELONGS_TO` edge. Markdown files get embedded YAML frontmatter; non-markdown files get a `{file}.meta.yml` sidecar (the sidecar carrier defined in [`core/schemas/frontmatter-schema.md`](../../../core/schemas/frontmatter-schema.md) § Sidecar File Specification).

Stamp the **full required core field set** (`frontmatter-schema.md` § Validation Checklist — `type`, `managed_by`, `lifecycle_state`, `trust_category`, `domain`, `file_format`, `project`, `folder`), plus the conventional provenance/classification fields. For a **raw source artifact** (transcript, email, export routed to `4-Evidence/`):

```yaml
type: transcript             # Domain-A Type Taxonomy (transcript | email | export | fdd | …)
managed_by: file-router
parent: <Project>
domain: source               # live value — A/B/C are DEPRECATED aliases (frontmatter-schema.md § Category 6)
file_format: md              # md | txt | csv | pdf | xlsx | docx | html
project: <Project>
folder: 4-Evidence           # the resolved bin's enum value (closed-set, capitalized) — NOT "4-evidence"
lifecycle_state: active      # Domain-A set: created | draft | active | superseded | archived
lifecycle_changed: <YYYY-MM-DD>
trust_category: evidence     # a raw source artifact is evidence
evidence_quality: source
created_date: <YYYY-MM-DD>
created_by: file-router
source_system: <teams | jira | email | confluence | manual | agent-generated | smartsheet | sharepoint | gdrive | github>
tags: [project/<slug>, delivery/<domain>, artifact/<type>]
```

Rules:
- **`domain: source`** for raw evidence — never the deprecated `A`. Use the human-readable value the schema tells writers to emit.
- **`source_system`** — the value set above is the canonical enum **restated for the emitter's convenience**; [`frontmatter-schema.md`](../../../core/schemas/frontmatter-schema.md) § Category 3 owns it. Extend that row first, then this one — never only this one.
- **`folder`** is the resolved bin's **closed-set enum value** — the capitalized form (`1-Governance` … `5-Reference`, or `_generated`), which the `folder` enum validates. Never the deprecated legacy `0N-…` value.
- **Idempotent:** identity/provenance fields (`type`, `project`, `created_date`, `created_by`) are written once and not churned on re-encounter; volatile fields (`lifecycle_changed`) refresh. Do not double-stamp a file that already carries file-router frontmatter.
- The stamp and the `BELONGS_TO` edge are **one write** — see § `BELONGS_TO` Edge Emission.

## Raw→Tracked Extraction Trigger

When a **raw evidence** artifact is routed to `4-Evidence/`, file-router **routes + stamps + triggers** the extraction — it does **not** author the extraction itself. file-router is a classifier/router; the DEC/ACT/RISK extraction and the provenance writes are the SHIPPED downstream chain:

| Step | Owner | Output |
|------|-------|--------|
| Analytical extraction (transcript → decisions / actions / risks) | **PPM Agent** (the sweep's downstream Triage step) | tracked entries proposed for the trackers |
| Reverse back-link on the tracked entry | **tracker-manager** on `ADD` | `source_inputs[]` (markdown-tracker entries) · `source_ref` (RAID rows) |
| Forward edge on the raw artifact | **ppm-agent** | `relationships: [{type: GENERATES, target: <entry-id>}]` |

file-router's contribution is the **trigger**: after routing a transcript/evidence file it emits the "ready for PPM Agent processing" prompt (the sweep's hand-off to Triage). The extracted entries and edges are written **conformant to** [`core/schemas/tracker-schemas.md`](../../../core/schemas/tracker-schemas.md) § Raw→Tracked Provenance — file-router writes against that schema's field names and token domain (`TR-###` | `MSG-###` | artifact `id`-slug | source path), it does not redefine them. This makes the raw→tracked link a first-class bidirectional bridge: from a raw artifact follow `GENERATES` → the entries it produced; from a tracked entry follow `source_inputs` / `source_ref` → the raw artifact that established it.

## Bin Manifests

Each project bin carries a `README.md` + `manifest.yml` orientation card, copied per-project from the canonical templates at `operations/templates/project-bins/<bin>/`. The manifest states, per bin, what it `accepts`, the artifact `types` routed there, and its `promotion_target` (for `_generated/` staging) — a **derived view** for human orientation, not a second routing authority.

- **Authority stays here.** Routing decisions always execute against this `SKILL.md` (+ `references/routing-patterns.md`). A bin manifest is regenerable from that authority; it never overrides it. If a manifest and this file ever disagree, this file wins.
- **Read-for-orientation only.** file-router MAY read a bin's manifest to orient a human ("this bin accepts …"), but never depends on it for a routing decision.
- **Degrade gracefully.** An absent or stale manifest changes nothing — routing falls back to this file's authority. A missing manifest is not a routing error.
- **Placement is the scaffold's job.** Copying the templates into each project's bins happens at project scaffold time (project-initiator), not here — file-router authors the canonical templates and reads the in-bin copies.

## Processing Pipeline

### For Transcripts

After routing a transcript to the correct project folder:

1. **Save** transcript to appropriate sub-folder in 4-Evidence/Transcripts/
2. **Register** — produce a Transcript Register entry using the standard schema:
   - Transcript ID: TR-### (auto-incremented from existing register)
   - Date, Meeting Type, Project, Participants, Tags, Summary (3-sentence format), File Path
3. **Output structured TRACKER_UPDATE instruction** for the Transcript Register
4. **Prompt** the user: "This transcript is ready for PPM Agent processing. Process now?"

### For Jira Exports

1. Route to 2-Delivery/Testing/Jira Export/ (or appropriate sub-folder)
2. Note the export date and scope in the routing summary
3. Prompt: "Jira export available for Delivery Engine analysis. Process now?"

### For All Other Files

1. Route to the target folder per classification. On the routing write for a Direction-1 (Inbound) or Direction-4 (Cross-project) route, emit the `BELONGS_TO` edge per § `BELONGS_TO` Edge Emission (target = resolved project).
2. Produce a routing summary: file type, confidence, evidence, target location
3. If the file type suggests operational updates (email about a decision, FDD revision, etc.),
   note the potential downstream processing

## Single-Source Recording Detection

When a transcript shows only one speaker but content references multiple viewpoints, decisions
by different people, or uses "we discussed" / "the team agreed":
- Flag as `[SINGLE-SOURCE RECORDING]`
- Extract participants from content mentions, not speaker attribution
- Note this in the Transcript Register entry

## Unclassified Queue Management

Files routed to `_inbox/_unsorted/` are tracked in a queue file at
`_inbox/_unsorted/_queue.md`:

```markdown
# Unclassified File Queue

| File | Date Added | Attempted Classification | Confidence | Why Low | Status |
|------|-----------|------------------------|-----------|---------|--------|
| example.txt | 2026-03-18 | Transcript (maybe) | 45% | No speaker labels, no project match | PENDING |
```

- During daily processing, prompt the user to review pending items
- Transcripts UNASSIGNED for >3 business days get an escalated flag
- Each user correction triggers a routing rule update proposal (see Self-Update Protocol)

## Self-Update Protocol

When a user corrects a misclassification:

1. **Record** the correction: original classification → correct classification
2. **Identify** what signal was missing or misread
3. **Propose** a rule update to `references/routing-patterns.md`:
   - New pattern to add
   - Existing pattern to modify
   - Confidence adjustment
4. **Create** a GitHub Issue for the proposed rule update:
   ```bash
   gh issue create \
     --title "[Skill Update] File Router: [brief description of rule change]" \
     --body "## Improvement Proposal

   **Source:** file-router correction on [filename]
   **Severity:** P3

   ### Description
   User corrected a misclassification. Original: [X] at [Y]% confidence. Correct: [Z] in [folder].

   ### Proposed Change
   Update \`references/routing-patterns.md\`:
   - [specific pattern or signal to add/modify]

   ### Acceptance Criteria
   - [ ] routing-patterns.md updated with new rule
   - [ ] Similar files classified correctly on next encounter
   " \
     --label "improvement,P3,skill-update"
   ```
5. On Issue approval (user adds `approved` label): update `references/routing-patterns.md`

## Multi-Project Routing

For files from sources that serve multiple projects (Google Drive transcripts, shared inboxes):

1. Check content against ALL active PROJECT.md files
2. If clear single-project match (≥90%): route to that project
3. If multiple projects match: present matches with scores, ask user
4. If no project matches but content is project-related: route to `_inbox/_unsorted/` with note
5. If content is not project-related (1:1s, general meetings, personal): route to `Non-Project/` folder or discard per user preference

## Batch Processing

When multiple files arrive at once:

1. Classify all files first (don't route one-by-one)
2. Group by confidence level: High / Medium / Low
3. Present a single summary:
   - High-confidence files: auto-routed (list with destinations)
   - Medium-confidence files: proposed routes (user confirms)
   - Low-confidence files: queued (user reviews)
4. After user approves/corrects medium-confidence items, execute all routing
5. Trigger downstream processing for transcripts and actionable files

## Output Format

Every routing action produces a structured summary:

```
FILE ROUTING SUMMARY
Date: [YYYY-MM-DD]
Files processed: [count]

HIGH CONFIDENCE (auto-routed):
- [filename] → [project]/[folder] (confidence: [X]%, evidence: [brief]) [+BELONGS_TO → [project]]

MEDIUM CONFIDENCE (proposed):
- [filename] → [project]/[folder]? (confidence: [X]%, evidence: [brief])
  Alternative: [other possibility]

LOW CONFIDENCE (queued):
- [filename] → _inbox/_unsorted/ (attempted: [classification], confidence: [X]%)
  Missing signal: [what would help classify]

DOWNSTREAM TRIGGERS:
- [filename]: Ready for PPM Agent processing
- [filename]: Ready for Delivery Engine analysis

ROUTING RULE UPDATES PROPOSED: [count, if any]
```

## Reversibility Discipline

This skill produces **decision-class outputs** — proposed routes (MEDIUM confidence) for
user confirmation, downstream-processing trigger prompts ("Process now?"), routing-rule
update proposals opened as GitHub Issues, unclassified-queue items escalated after >3
business days, and single-source-recording flags. The HIGH-confidence auto-routes that
execute without prompting are themselves decision-class — they are the skill taking a
downstream-visible action on the filesystem — even though the tier for most auto-routes
is CHEAP. Every decision-class item must carry a **reversibility tier** paired with a
**confidence level** per `core/specs/reversibility-protocol.md`.

**Decision-class outputs in this skill:**

- Confidence & Approval Gate — the HIGH / MEDIUM / LOW routing decision itself (confidence-threshold gate for the inbound-family), with the MEDIUM proposals explicitly awaiting user confirmation, plus the flat-approval promotion gate and the mandatory cross-project approval.
- High-confidence auto-routes to approval-required folders (1-Governance/, 2-Delivery/, 3-Operations/, 5-Reference/) that are surfaced for approval despite HIGH confidence.
- FILE ROUTING SUMMARY output sections — HIGH CONFIDENCE (auto-routed), MEDIUM CONFIDENCE (proposed), LOW CONFIDENCE (queued), DOWNSTREAM TRIGGERS, ROUTING RULE UPDATES PROPOSED.
- Self-Update Protocol — routing-rule update proposals opened as GitHub Issues with the `improvement` label, and the subsequent post-approval update to `references/routing-patterns.md`.
- Unclassified queue escalation flags after >3 business days unassigned.
- Single-source recording flags and participant extraction from content mentions.
- Multi-project routing recommendations when multiple projects match with similar scores.

**Tier vocabulary (undo threshold + stakeholder impact):**

- **CHEAP** (undo in hours) — a HIGH-confidence auto-route into an auto-write folder (4-Evidence/, _generated/) that is easily moved by editing the filesystem; a LOW-confidence file parked in `_inbox/_unsorted/` awaiting review; a MEDIUM-confidence route proposed to the user but not yet executed; a downstream-trigger prompt not yet acted on. State the tier. Proceed.
- **MODERATE** (undo in days, minor data loss acceptable) — a MEDIUM-confidence proposed route the user approves that commits the file to a project folder and notifies downstream consumers (PPM Agent, Tracker Manager); a transcript registration via TRACKER_UPDATE to the Transcript Register; a downstream-trigger acceptance that initiates PPM Agent processing or Delivery Engine analysis. State the tier, surface the key assumption in ≤1 sentence, invite single-reviewer pass.
- **EXPENSIVE** (undo in weeks, stakeholder impact) — a routing-rule update proposal promoted via GitHub Issue and approved, which then changes future classification behavior for a class of files across the workspace; a high-confidence auto-route to an approval-required folder (1-Governance/, 2-Delivery/) that seeds a stakeholder-visible artifact; a persistent misclassification pattern that shapes how the PPM Agent reasons across multiple projects. State the tier, document rationale (≥2 sentences), state rollback plan (revert rule update, manual re-classification of affected files), name the affected cohort (workspace owner, downstream skills, project stakeholders).
- **IRREVERSIBLE** (cannot undo) — a routing decision whose downstream consumption has already produced external-facing communication (e.g., a transcript routed, registered, and then surfaced in an exec rollup before anyone notices the misclassification); a routing rule accepted into `references/routing-patterns.md` that has already shaped classification of many subsequent files. State the tier, document rationale, state rollback is infeasible or name the counter-commitment (correction notice, rule revert + manual re-classification sweep), name the sign-off authority (operator), pair with explicit downside description.

**Label format** (any accepted):

- Inline: `Recommendation (MODERATE · confidence: HIGH): <text>` — e.g., on a MEDIUM-confidence proposed route or a downstream-trigger prompt.
- Trailing: `<text> [MODERATE · confidence: HIGH]` — e.g., on a single-source recording flag or a queue escalation.
- Structured column: tier value in a `Reversibility` or `Tier` column of the HIGH / MEDIUM / LOW CONFIDENCE sections of the FILE ROUTING SUMMARY or the Unclassif

…(truncated)
