Pattern-072: Registries that Grow into Architectural Shapes

Status

Proven — Promoted from Emerging on 2026-05-16 by CIO per methodology-audit-policy-updates-2026-03-16.md self-approval authority. Promotion-to-Proven trigger fired via #1094 ENGINE-DELETION close-out (commit d48bc1d0, merged 2026-05-15 14:38 PST): Slack handler dispatch routes through intent_service.process_intent which dispatches via the task_type registry to canonical handlers — fourth meaningful behavior-deciding consumer of the registry, landing without violation of the formalization discipline (typed enum / documented consumer set / explicit default policy / register-time validation, all four invariants intact). Originally filed Emerging 2026-05-15 by Lead Developer per CIO disposition (May 15) following Architect’s observation during #1017 Phase 1 ratification + Lead Dev’s methodology memo. Slot 072 allocated after 12l pre-filing slot-availability check. Methodology-29 (“Pattern Formation via Successful Imitation”) instance applied to registries rather than to code shapes. Three-consumer recognition threshold + fourth-consumer Proven trigger both fired within ~6 hours on 2026-05-15 — Pattern-072 is the framework’s first sub-day Emerging-to-Proven promotion.

The four consumers (Proven-promotion evidence)

# Consumer When Behavior decision
1 task_type → model config dispatch (original) Pre-existing Route to per-task model (cheap vs. premium)
2 #1004 calibration telemetry 2026-04-27 Partition BoundaryEnforcer probe set by task_type
3 #1017 output-filter profile dispatch 2026-05-15 AM Filter on/off + Tier 1/2 policy (user_visible / internal / mixed)
4 #1094 Slack handler EXECUTION dispatch 2026-05-15 PM Replaces OrchestrationEngine + WorkflowFactory chain; routes via intent_service.process_intent → task_type registry → canonical handlers

Architect’s e2e-suite probe registry remains a prospective fifth instance per the May 15 design proposal; if it adopts the discipline cleanly when implemented, it becomes confirmation rather than promotion trigger.

Formalization-discipline check (verified at promotion)

Product Relevance

Architecture / Methodology — Recognition discipline for a specific evolution shape that affects how teams maintain typed enumerations as they accrue consumers. Users will not encounter this pattern directly; agents and engineers maintaining the codebase will reach for it when deciding whether to formalize an annotation that’s outgrown its original purpose.

Context

Some annotations start single-purpose. A string field on a method signature, a tag on a model, an enum used for one routing decision. The annotation does one job and the surface is small.

Then a second consumer reuses the annotation for a related but distinct purpose. Now two pieces of code make decisions based on the annotation’s value. The original single-purpose framing still mostly fits.

Then a third consumer reuses the annotation. The annotation is no longer single-purpose; it’s a taxonomy. Multiple unrelated parts of the system query it to decide behavior. Adding a new value to the annotation now affects all three consumers — sometimes silently, sometimes via a fail-closed default, sometimes by quietly producing wrong behavior in a consumer whose maintainer hasn’t been told about the registry’s new role.

This pattern names the recognition trigger (third meaningful consumer is the formalization threshold) and prescribes the formalization discipline (typed enum, documented consumer set, explicit default policy, register-time validation).

Where this surfaced

#1017 OUTPUT-CONTENT-FILTER Phase 1 design (2026-05-15) made task_type the third meaningful consumer:

  1. task_type → model config dispatch (original, single-purpose) — LLMClient.complete(task_type=..., prompt=...) selected per-task model configuration (task_type="intent_classification" → cheap model; task_type="conversation" → premium model). One job: route to the right model.
  2. #1004 calibration telemetry (Apr 27, 2026) — per-task-type detection-effectiveness analysis. BoundaryEnforcer probe set keyed by task_type so calibration accuracy could be partitioned by where the LLM was being called from. Second meaningful consumer using the annotation for behavior partitioning.
  3. #1017 output-filter profile dispatch (May 15, 2026) — task_type → filter profile (user_visible / internal / mixed) determining whether outputs get PII redaction + boundary-category check. Third meaningful consumer; behavior decision (filter on/off) keyed by the annotation.

Architect’s observation in the parallel e2e-suite design proposal (May 15) flagged a fourth candidate consumer: “the probe registry is the same shape — a catalog of typed entries dispatched at consumption time.” That observation became the Proven-promotion criterion for this pattern: if the e2e probe registry adopts the formalization discipline without rediscovery, the pattern’s value is empirically confirmed.

The pattern names the converging shape so future similar annotations have a citable framework — and a recognition trigger before consumer divergence becomes a coordination cost.

Problem

The Failure Mode

Annotation X created for single consumer A
  → "X is just for A; small surface; no governance overhead needed"

Consumer B uses X for related purpose
  → "X works for B too; nice reuse; still small"

Consumer C uses X for unrelated purpose
  → X is now a registry; three distinct decision points read it
  → adding new value Y to X requires understanding A, B, and C
  → maintainer of Y might know A and B (their direct context) but not C
  → C's behavior changes silently when Y is added
  → no one notices until Y produces a wrong decision in C's surface

The asymmetry that makes this load-bearing:

Without formalization, every value-addition becomes a tax. With formalization, the tax is paid once (documenting the registry + its consumers + the default policy) and amortized.

Where this could surface

Five candidates in PM’s codebase:

The pattern’s value is that the third-consumer threshold gives a clear moment to formalize — before the registry accumulates enough consumers that formalization becomes excavation work.

Solution

Recognition trigger

A registry hits the formalization threshold when the third unrelated consumer reads its values to make a behavior decision (not just for observability — observability is a passive reader). The third behavior-deciding consumer is the moment to formalize.

Formalization discipline (apply at the threshold)

  1. Typed enumeration — if the registry is a free-form string, promote to a typed enum (StrEnum / Enum) so add/rename surfaces to grep and type-checker. The cost of conversion is small at threshold; large later.

  2. Documented consumer set — at the registry’s definition site, a comment listing every place a value is read for behavior decisions. The list is part of the contract; future-maintainers who add a value know to check the consumer set.

  3. Explicit default policy for new entries — when a new value is added, what happens in each consumer? Three common policies:
    • Fail-closed (e.g., #1017’s “unknown task_type defaults to user_visible profile”) — new values get the safer behavior; explicit opt-out required to relax
    • Fail-open — new values get the minimal behavior; explicit opt-in required to enforce
    • Compile-error — new values must be classified in every consumer at type-check time (only works with exhaustive pattern matching)

    The policy choice depends on the registry’s stakes. Default to fail-closed for safety-relevant registries.

  4. Register-time validation (optional but valuable) — if any consumer registers special-case behavior for specific values, that registration co-locates with the consumer’s definition. Future-maintainers can grep for the value and see all special cases without crawling through every consumer’s logic.

What this is NOT

Architectural reasoning

Three structural properties make this pattern load-bearing:

  1. Behavior decisions on string values are silent. A consumer that does if task_type == "conversation": ... doesn’t fail when a new task_type is added; it just doesn’t match. The new task_type takes whatever fall-through behavior the consumer has. If that’s “do nothing,” the new feature ships broken. If it’s “default to behavior X,” the new feature ships with X. Either way, the consumer’s maintainer wasn’t consulted.

  2. Multiple consumers create multiple-decision surface. A registry with three behavior-deciding consumers has, at minimum, three places where adding a value changes behavior. Without governance, the value-adder needs to verify all three; with governance, the consumer set is documented and the default policy specifies what happens.

  3. Cost of formalization is monotone with consumer count. At one consumer: trivial. At three consumers: easy to do, requires touching three call sites. At ten consumers: hard, requires understanding all ten + their interaction. The third-consumer threshold isn’t arbitrary; it’s the last point at which formalization is still cheap.

Forces / when to apply

Apply at threshold (third meaningful consumer arrives):

Apply during code review when:

Don’t apply when:

Code references (reference instance: task_type)

The reference instance — task_type in PM’s LLMClient — became the canonical example after #1017 made it three-consumer:

services/llm/clients.py:complete — original single-consumer (model config dispatch):

async def complete(self, task_type: str, prompt: str, ...) -> str:
    task_config = MODEL_CONFIGS.get(task_type, MODEL_CONFIGS["reasoning"])
    # ... per-task-type model resolution

services/ethics/output_filter.py:_PROFILE_REGISTRY — #1017 third consumer (behavior dispatch):

_PROFILE_REGISTRY: Dict[str, str] = {
    "conversation": Profile.USER_VISIBLE,
    "question_answering": Profile.USER_VISIBLE,
    # ... 10 user_visible task_types
    "intent_classification": Profile.INTERNAL,
    "general": Profile.MIXED,
}

def profile_for(task_type: str) -> str:
    """Return the filter profile for a task_type, defaulting to user_visible.

    Unknown task_types fall back to `user_visible` (fail-closed default per
    Architect Q6 ratification — new task_types must opt out of filtering
    explicitly, not opt in).
    """
    profile = _PROFILE_REGISTRY.get(task_type, Profile.USER_VISIBLE)
    ...

ADR-061 v1.1 amendment (2026-05-15) memorializes the recognition:

“Profile dispatch via task_type: the existing task_type parameter (already required at every LLMClient.complete() call site) drives filter-profile selection. … Unknown task types default to user_visible (fail-closed).”

What’s NOT yet done that the pattern prescribes:

These are explicit follow-ups when the registry advances (e.g., a fourth consumer lands per the Proven-promotion criterion).

Anti-pattern recognition

Code-review signals that this pattern should be applied or is being violated:

Healthy signals:

Relationship to other patterns

Promotion criteria

To Proven:

Promotion-blocking signals:

Cross-references

— Lead Developer, 2026-05-15