A typed reference names exactly one scheme, so a relation with two legitimate target families cannot be declared
- Dominant language
- Python
- Stars
- 21
- Forks
- 2
- Avg merge
- 6h 33m
- Merged PRs (30d)
- 91
Description
`Reference.scheme` is a single `str`:
```python
@dataclass(frozen=True)
class Reference:
field: str
scheme: str
required: bool = True
many: bool = False
```
`many` says the field holds *several codes*. Nothing says the field holds a code from *either of several schemes*. So a relation whose legitimate targets span two families cannot be declared at all, and the record has to either narrow it dishonestly or drop it into prose.
## The motivating case
`examples/constitution` declares:
```toml
[luria.schemes.BOUNDARY.references]
overrides = { scheme = "PRACTICE", many = true, required = false }
```
A boundary that overrides another **boundary** is rejected:
```
record/boundaries.d/BOUNDARY-003.md: `overrides: BOUNDARY-001` is not a PRACTICE code
— a BOUNDARY document's `overrides` names a PRACTICE document
(luria.toml: schemes.BOUNDARY.references.overrides)
```
That is not a contrived shape. Constitutions carry authorization carve-outs constantly — a permission that beats a prohibition — and the example already smuggles one in as prose, in `BOUNDARY-001`'s body:
> What it does not do is expand. A request that merely *sounds* like the class is not in it…
That sentence is doing the work an `overrides` edge to another boundary would do, unchecked, which is exactly the arrangement typed references exist to replace ([ADR-060](https://github.com/dmarx/luria/blob/main/record/decisions.d/ADR-060.md)).
The same shape shows up outside this example: an anthology where a practice may cite either a paper or a dataset; a spec that supersedes either a spec or an RFC.
## The workarounds, and why neither is good
**Two fields** — `overrides_practice` and `overrides_boundary`. The relation is one relation; splitting it by target type puts the type system's job in the field name, and every consumer of the graph now has to know to union two edges. It also reads as two different relations to anyone browsing the frontmatter.
**One loose field** — drop the reference, use `requires`. That is the failure [ADR-060](https://github.com/dmarx/luria/blob/main/record/decisions.d/ADR-060.md) was written against: `requires` is satisfied by any truthy value, so it checks presence and nothing else.
## Suggested shape
`scheme` accepts a list, and the check becomes membership rather than equality:
```toml
overrides = { scheme = ["PRACTICE", "BOUNDARY"], many = true, required = false }
```
A single string keeps working and means a one-element list, so no existing config changes. The error message becomes "names a PRACTICE or BOUNDARY document". Edge construction in `luria/edges.py` is unaffected — the edge is already keyed by field name, and the target's scheme is read from the resolved code.
## Relation to [#141](https://github.com/dmarx/luria/issues/141)
This is Proposal 6 territory — typed references elaborating into named graph edges — and it is the narrow, pre-contract version of the target refinements that proposal defers (`Ref[LIT where status = Active]`). A union of schemes is strictly less expressive than a refinement predicate and needs none of the contract machinery, so it can land independently if that is the preferred order.
It is worth stating what this is **not**, since [#141](https://github.com/dmarx/luria/issues/141) is careful about the boundary: no predicate language, no `when`, no conditions on the edge. Just "this field's code may come from any of these declared families", which is a widening of an existing check rather than a new kind of check.
Contributor guide
Research direction
Start by locating the Reference definition and the validation path that emits the PRACTICE-only error, then inspect luria/edges.py and the examples/constitution configuration. The change is done when a scalar scheme remains valid, a scheme list accepts any listed target family, the diagnostic names the allowed families, and edge construction remains unaffected.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python
- Domain
- tooling
- Issue type
- Feature
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 68/100