|
| 1 | +# ADR-0008: Explicit specialization metadata for language-specific actions |
| 2 | + |
| 3 | +- **Status:** accepted |
| 4 | +- **Date:** 2026-03-21 |
| 5 | +- **Deciders:** @Aksem |
| 6 | +- **Tags:** actions, architecture, languages |
| 7 | + |
| 8 | +## Context |
| 9 | + |
| 10 | +After [ADR-0006](0006-shared-action-types-as-the-action-identity-contract.md), |
| 11 | +FineCode treats shared action definition types as the architectural identity |
| 12 | +contract. After [ADR-0007](0007-single-registration-per-action-definition.md), |
| 13 | +projects also cannot model language variants by registering the same generic |
| 14 | +action definition multiple times under different names. |
| 15 | + |
| 16 | +Multi-language actions therefore need an explicit way to represent that one |
| 17 | +action definition is a language-specific specialization of another. That |
| 18 | +relationship must be discoverable without depending on project-local |
| 19 | +registration names or naming conventions. |
| 20 | + |
| 21 | +Name-based discovery is too fragile for that role. A project may register a |
| 22 | +Python lint action under any project-local name, and the discovery mechanism |
| 23 | +must still recognize both: |
| 24 | + |
| 25 | +1. which language the specialized action targets |
| 26 | +2. which parent action definition it specializes |
| 27 | + |
| 28 | +Those are intrinsic properties of the action design, not of project |
| 29 | +configuration. |
| 30 | + |
| 31 | +## Related ADRs Considered |
| 32 | + |
| 33 | +- [ADR-0006](0006-shared-action-types-as-the-action-identity-contract.md) — |
| 34 | + establishes action definition types as the identity contract. This ADR |
| 35 | + extends that rule to specialization relationships between action definitions. |
| 36 | +- [ADR-0007](0007-single-registration-per-action-definition.md) — |
| 37 | + establishes that a project may register a given action definition at most |
| 38 | + once. This ADR defines how language-specific specialization is represented |
| 39 | + without relying on duplicate registrations. |
| 40 | + |
| 41 | +## Decision |
| 42 | + |
| 43 | +FineCode will represent language-specific specialization through **explicit |
| 44 | +metadata on the shared action definition itself**. |
| 45 | + |
| 46 | +A language-specific action definition must declare: |
| 47 | + |
| 48 | +- the language it targets |
| 49 | +- the parent action definition it specializes |
| 50 | + |
| 51 | +This metadata is part of the shared action contract. It must not be inferred |
| 52 | +from project-local registration names, and it must not live only in project |
| 53 | +configuration. |
| 54 | + |
| 55 | +Discovery and dispatch mechanisms may use that metadata to find specialized |
| 56 | +actions, but routing behavior remains a separate concern owned by handlers or |
| 57 | +other orchestration logic. |
| 58 | + |
| 59 | +## Consequences |
| 60 | + |
| 61 | +- **Discovery is independent of project-local names.** A project may choose any |
| 62 | + registration name for a specialized action without breaking discovery. |
| 63 | +- **Specialization becomes part of the contract package.** The relationship |
| 64 | + between a specialized action and its parent is expressed alongside the action |
| 65 | + definition itself rather than being split across naming conventions or project |
| 66 | + configuration. |
| 67 | +- **Language alone is not sufficient.** Multiple parent actions may have |
| 68 | + specializations for the same language, so discovery needs both the target |
| 69 | + language and the parent action definition. |
| 70 | +- **Project configuration has a narrower role.** Configuration may control |
| 71 | + whether an action is registered or how it is executed, but it does not define |
| 72 | + the architectural specialization relationship. |
| 73 | + |
| 74 | +### Alternatives Considered |
| 75 | + |
| 76 | +**Inheritance from the parent action definition.** Rejected because the |
| 77 | +specialization relationship does not always imply substitutability. Some |
| 78 | +language-specific actions may need payload or contract differences that make |
| 79 | +inheritance an unreliable architectural signal. |
| 80 | + |
| 81 | +**Registration-time metadata in project config.** Rejected because it places an |
| 82 | +intrinsic property of the shared action contract into project-local |
| 83 | +configuration, where it can drift and where callers cannot rely on it across |
| 84 | +environments. |
| 85 | + |
| 86 | +**Name conventions.** Rejected because project-local registration names are not a |
| 87 | +stable architectural contract. |
| 88 | + |
| 89 | +## Related Decisions |
| 90 | + |
| 91 | +- Refines [ADR-0006](0006-shared-action-types-as-the-action-identity-contract.md) |
| 92 | +- Supports [ADR-0007](0007-single-registration-per-action-definition.md) |
| 93 | + |
| 94 | +## Implementation Notes |
| 95 | + |
| 96 | +The current implementation represents this metadata with class-level fields on |
| 97 | +the `Action` base class for the target language and the parent action |
| 98 | +definition. |
| 99 | + |
| 100 | +Helpers that discover language-specific actions should use that explicit |
| 101 | +metadata rather than parsing registration names. |
| 102 | + |
| 103 | +When a specialized action extends its parent payload with ecosystem-specific |
| 104 | +fields, dispatch-oriented construction should rely only on values available from |
| 105 | +the parent payload. Any specialized fields therefore need defaults that are |
| 106 | +meaningful for dispatch-based invocation, while workflows that need non-default |
| 107 | +values should invoke the specialized action directly or use a more specific |
| 108 | +orchestration path. |
0 commit comments