[Discuss] Extensible "role" architecture for context items (examples, rubrics, and future types)
Follow up to: #3586250+s ## Problem/motivation In [#3584775] we removed `AiContextItemType` and made `ai_context_item` bundleless, concluding that bundles exist for entities that need *different fields*, and that the proposed good/bad example use case is a same-schema distinction (see also the [Discuss] thread on context item bundles). The agreed direction was: model the distinction as a field, provide UX separation via dedicated creation forms, and revisit bundles only if a genuinely distinct-fields requirement appears. This follow-up is about doing that well rather than ad hoc. The content review work will likely need several "kinds" of context items at once (good examples, bad examples, evaluation rubrics), and we don't want to add a new one-off flag field every time a new kind is imagined. We need a typing architecture that is extensible, but that doesn't repeat the bundle mistake at a smaller scale. ## Framing: three orthogonal axes The entity already expresses distinct axes, and conflating them is the main design risk: | Axis | Question | Mechanism today | |---|---|---| | **Role / purpose** | What is this content for, and how is it consumed? | (this ticket) | | **Structure** | Is this standalone or a child of another item? | `parent` + `subcontext_type` | | **Applicability** | Where/when/for whom does it apply? | `scope` plugin system, tags | Consequences: - **Subcontext is not a role.** It is defined by a relationship (`parent`), and a single exclusive `type` field would make "a good-example subcontext" inexpressible. It stays as-is. (Its `subcontext_type` required/conditional flag is a *consumption behavior* declaration, which makes it a conceptual cousin of roles — but on the structure axis.) - **Scope-able distinctions are not roles.** If the difference is "what topic/section/use case this is for," that's tags/scopes. ## Role-admission test A role exists **only** for content that ai_context's own selection/prompt-assembly pipeline must treat differently. Applying the test to candidates discussed so far: **Possible candidates** (all driven by content review): - **Good example** — injected into prompts as a positive few-shot example, separated from instructions. - **Bad example** — injected as a negative example. - **Evaluation rubric** — consumed only by the review pipeline, never injected into generation prompts. **Rejected candidates** (with reasons, to prevent re-litigating): - **Guardrails/constraints** — AI core's guardrails system owns enforcement semantics (it can halt processing). ai_context should remain a content source that guardrails can pull from; we integrate, not duplicate. - **Persona/brand voice** — an applicability distinction, not a consumption distinction. Use tags/use-case scope. - **Output format specs** — just regular instruction content; no distinct consumption. - **Workflow/procedure** — no distinct behavior; regular context with a tag. **Undecided:** - **Starter templates** (boilerplate for authoring new context items, excluded from selection entirely). Could be a `template` role (gets revisions/moderation/translation for free, but every selection path must exclude it) or could live outside the entity entirely (config/MDXEditor plugin — unselectable by construction, but new UI). Decide separately; the authoring picker is an MDXEditor concern either way. ## Architecture options **Option A — plain enum field, extended ad hoc.** A `list_string` field (like `subcontext_type`); add allowed values as needs arise. Cheap, but not third-party extensible, and per-role behavior (forms, prompt placement, locking) scatters across switch statements. **Option B — role plugin system.** An `AiContextItemRole` plugin type mirroring the existing `AiContextScope` pattern (attribute discovery, manager, dynamic allowed values feeding a single `role` base field). Each plugin encapsulates label, dedicated creation route/form (the UX separation agreed in the bundle discussion), locked/default values, and its prompt/consumption role. Truly extensible; one bundleless entity, one schema, no Field UI/config fragmentation. Cost: a public plugin API is itself hard to change after release — the same reversibility asymmetry that argued against bundles argues against prematurely publishing this contract. **Option C — phased (recommended).** Ship a `role` base field now whose allowed values come through a small seam (manager/`allowed_values_function`) marked `@internal`. Graduate to the full Option B plugin contract once requirements are proven. Note: content review may deliver three roles at once, satisfying the graduation trigger quickly — the seam keeps that promotion non-breaking either way. **Hard boundary (carried over from the bundle decision):** roles are for same-field-schema variants only. If a future kind needs genuinely different fields (e.g., a structured glossary with term/definition fields, or dynamic/computed context with source configuration), that is a submodule-owned bundle or separate entity — not a role. ## Open questions to settle before implementation 1. **State shape:** single exclusive enum (`good_example` / `bad_example` / `rubric`, empty = normal context) vs. orthogonal fields ("is example?" + polarity). Depends on whether an item can be both usable context *and* an example. Needs the content-review consumption sketch — this determines cardinality and defaults, which are the hard-to-reverse parts. 2. **Default semantics:** explicit `usable` value vs. empty field meaning "normal context" (affects queries and the update hook for existing items). 3. **Field properties:** revisionable (likely yes — role changes are meaningful history), translatable (likely no). 4. **Permissions:** bundles gave per-bundle permissions for free; if content review needs e.g. "only reviewers create bad examples," role-based access needs custom logic. Name the requirement or explicitly defer. 5. **Ownership:** does the base module define all roles, or does a content-review submodule register example/rubric roles via the seam? (The latter matches the original suggestion that content review own its own concepts.) 6. **Starter templates:** in-entity role vs. external mechanism (see above). ## Selection mechanism (explicitly deferred) Whatever architecture lands, `role` must become a first-class input to selection and prompt assembly (scope matching, `GetRelevantAiContextItems` / `ListAiContextItems`, system prompt building). The candidates already show roles differ in *selection semantics*, not just labels: filtered-by-relevance (default), example injection (good/bad), consumed-by-a-different-pipeline (rubric), never-selected (template, if in-entity). The typing architecture must leave room for roles to declare *how* they are consumed, not just what they're called — but the selection design itself is a follow-up once this architecture is decided. ## Tasks - [ ] Get the content-review consumption sketch (how good/bad/rubric are actually used) — blocks question 1 - [ ] Decide state shape, default semantics, and field properties - [ ] Decide Option C seam design and role ownership (base module vs. submodule) - [ ] Decide starter-template placement (separate ticket if needed) - [ ] Create follow-up ticket for role-aware selection/prompt assembly - [ ] Implement `role` field + update hook + dedicated creation forms for initial roles ## AI usage - [x] AI assisted issue (Fable 5)
issue