RFC-0011 — Label-Driven Intake & Difficulty Tiers across the Fleet

Status: Implemented (epic #123 — tiers classifier, poller, /from-issue, tier_profile/tier_floor, autonomy_tier, merge_policy) · Created: 2026-06-20 · Updated: 2026-06-20 · Extends: RFC-0002 (task contract), RFC-0006 (assurance levels), RFC-0009 (CI-gated auto-merge), RFC-0010 (code-aware planning + equivalence) · Affects: PFactory, AIFactory, TFactory, CFactory

Create an issue or work-item, label it factory:low | factory:medium | factory:hard, and the fleet picks it up, classifies its difficulty, routes it through PARR (plan → code → test → observe), and delivers a review-ready, correct result — uniformly across GitHub, GitLab, and Azure DevOps. The difficulty tier is the single knob that drives model, planning depth, human gate, verification floor, and merge behaviour. Rewrites and hard implementation/testing get the heaviest treatment.

1. The goal — “label it and the fleet runs it, honestly”

Today work enters the fleet ad hoc. A handful of GitHub-Actions-on-label workflows (pfactory:run / aifactory:run / tfactory:run) POST to each factory; GitLab and Azure DevOps have no intake path at all; and difficulty and model are inferred after a build rather than declared up front. This RFC makes intake uniform, declarative, and idempotent:

  1. A human (or another system) opens an issue/work-item and applies one difficulty label.
  2. A provider-agnostic poller reads new labelled items across every host behind one abstraction, classifies the tier, and routes it through PARR.
  3. The tier deterministically selects the routing policy in §3, so the same label produces the same treatment on every host, every time.

This is deliberately composition over invention: most of the machinery — the label classifier, the GitLab/ADO providers behind a GitProvider protocol, the contract’s tier fields, the VAL ladder (RFC-0006), the correlation key (RFC-0001), evidence gates (RFC-0001a), and migration/equivalence (RFC-0010) — already exists. This RFC adds a thin intake layer and, above all, a standard.

2. The taxonomy

Labels are the user-facing contract. Three difficulty labels drive routing; two lifecycle labels make intake idempotent and observable; the existing governance/routing and descriptive labels are carried through unchanged.

Label Role Meaning
factory:low difficulty Cheap, fast, unattended. Local model, no planning, auto-merge when green.
factory:medium difficulty Standard. A capable hosted model, light planning, async human approval.
factory:hard difficulty Heavy. Frontier model, full PFactory decomposition, blocking human approval, deepest verification.
factory:queued lifecycle Applied by the poller the instant an item is accepted. The idempotency marker.
factory:failed lifecycle Applied on a terminal intake/routing failure. The item is never silently dropped.

Carried-through (unchanged by this RFC):

The difficulty labels live in .github/labels.yml (the canonical set) and are synced to every host (§7).

3. Tier routing policy (normative)

The tier is the single input that selects every downstream decision. This table is normative — each per-repo module asserts against exactly this mapping (see the E2E harness, scripts/verify_rfc0011_e2e.py).

Tier model planning human gate (review_tier) VAL floor (RFC-0006) TFactory lanes merge
factory:low ollama:<m>haiku fallback skip none (auto) VAL-1 unit (+api) auto-merge when green + VAL ≥ floor + ci_parity=yes
factory:medium sonnet skip (lite) async approval (async) VAL-2 unit, api, integration merge after async approval
factory:hard opus PFactory full decompose blocking (blocking) VAL-3 (+ equivalence if rewrite) unit, api, integration, mutation (+ equivalence) blocking approval, then merge

Notes:

Precedence and migration

The classifier is scoped-label tolerant: it recognises the factory:<tier> form and ignores unrelated labels.

4. The autonomy_tier contract field

The tier is carried into the task contract (RFC-0002) as an additive, back-compatible field, so every downstream service reads the same value rather than re-deriving it:

"execution": {
  "autonomy_tier": "low | medium | hard",
  "review_tier":   "auto | async | blocking",
  "model":         "ollama:... | haiku | sonnet | opus",
  "skip_planning": true,
  "change_mode":   "create | modify | migration"
}

execution.autonomy_tier is already merged into apis/task-contract.schema.json. Semantics:

PFactory’s emitter writes autonomy_tier from the classified tier; AIFactory’s fast path builds the execution block inline for low/medium; TFactory reads it to select the VAL floor and lane rigor; the RFC-0009 merge policy reads it (with review_tier) to decide auto / hold-async / hold-blocking.

5. How it composes the rest of the stack

RFC-0011 is the intake and routing layer; it delegates the actual work to RFCs already in place:

Pipeline view:

issue/work-item + factory:<tier>
   → poller (provider-agnostic reader; factory:queued on accept)
      ├── low / medium → AIFactory governed ingest (skip / skip-lite plan, RFC-0002 fast path)
      └── hard         → PFactory full decompose → human approve → emit contract
   → AIFactory build → open PR/MR
   → host CI + TFactory lanes (VAL floor by tier; +equivalence for a rewrite)
   → RFC-0009 merge policy (auto | hold-async | hold-blocking by tier)
   → CFactory threads PLAN→CODE→TEST→MERGED by issue number (RFC-0001)

6. The cross-tracker contract

The whole point is uniformity across hosts. The standard is:

The GitHub fast path (the existing *:run on-label workflows) is kept for low-latency interactive use; the poller is the uniform floor that guarantees 100% pickup including on GitLab/ADO.

7. Labels, templates, and the difficulty applier

8. Honesty + safety (carried forward)

9. Rollout

  1. Standard + contract. This RFC + execution.autonomy_tier (done).
  2. Labels + templates. labels.yml, label-sync, sync_labels.py, the issue forms, and the difficulty→label applier.
  3. Tier classifier + profile. classify_tier (highest-wins, migration⇒hard) and the tier→config profile that overrides execution / review_tier / tfactory blocks, wired into the contract emitter.
  4. Poller + governed ingest. The provider-agnostic poller with SQLite + factory:queued idempotency and factory:failed handling, plus the /api/tasks/from-issue endpoint.
  5. Merge policy + TFactory rigor. The RFC-0009 merge policy parameterised by tier, and the TFactory tier_floor → VAL floor + lane rigor.
  6. E2E. One labelled test issue per tier on the demo repo (+ a rewrite), asserting each emits its exact §3 row; repeat the low case on GitLab and ADO to prove the cross-tracker contract.

10. Acceptance

A contributor opens an issue/work-item on any supported host and applies one difficulty label. The poller picks it up exactly once (idempotent under retries, restarts, and DB loss), classifies the tier (highest-wins; a rewrite forces hard), and routes it through PARR with the §3 policy: the emitted contract carries execution.autonomy_tier and the matching model / planning / review_tier / lanes; a low, green, ci_parity=yes change auto-merges with no human; a hard change is held for blocking approval and a rewrite additionally proves equivalence — and the same behaviour is observable on GitHub, GitLab, and Azure DevOps without host-specific code in the orchestrator.