Summary
gate-54 rule (f) $REF TARGETS requires that a string $ref "resolve to a schema key in the same register file set (case-exact)".
ADR-066 explicitly sanctions cross-app relations — a property on one app's schema that references an object owned by another app. By construction, that target is not in the referencing app's register file set. So rule (f) and ADR-066 contradict each other: a correctly-declared cross-app relation is a permanent gate-54 failure, and there is no dialect to express it.
Rule (b) RELATION SHAPE closes the other exit: a format: uuid property with a relation-shaped description and no $ref is also a finding. So for a cross-app relation the author can fail (f) by naming the foreign schema, or fail (b) by not naming it.
Measured on pipelinq (12 findings, 6 of them this shape)
| Property |
Foreign owner |
Rule hit |
lead.decisions → $ref: 'Decision' |
decidesk (ADR-066; decidesk owns making + eIDAS signing) |
(f) |
ticket.decisions → $ref: 'Decision' |
decidesk |
(f) |
ticket.contract → $ref: 'Contract' |
Shillinq (owns contract lifecycle) |
(f) |
ticket.caseReference |
Procest (the case it converted into) |
(b) |
receiptTemplate.organizationId |
organisation, an OpenRegister concept |
(b) |
posZReport.store |
the store plane — ADR-080 D2/D3 says store discovery is OpenRegister's |
(b) |
Each description already names the owner explicitly, e.g.
"References decidesk Decision objects (ADR-066 cross-app relation); Decidesk owns the making and eIDAS signing."
So the intent is documented in-tree; there is simply no machine-readable way to say it.
Why "just drop the $ref" is the wrong fix
Deleting $ref: 'Decision' would silence rule (f) on ticket.contract/lead.decisions (they are not all format: uuid, so (b) would not always fire) — but it destroys the only structured statement of what the property points at, to satisfy a checker. That is the same trade as deleting an inconvenient @spec, and I have not done it in pipelinq#743.
⚠️ There is also a live trap for anyone tempted to "fix" it locally: pipelinq does have a local contract schema, so $ref: 'Contract' → $ref: 'contract' makes gate-54 green in one character. It would also silently repoint a Shillinq relation at a different, local object. A case-insensitive-looking near-miss is exactly where this gate can be satisfied by making the data model wrong.
Suggested fix
A first-class cross-app form, so the relation stays declared and machine-readable, e.g.
{ "type": "string", "format": "uuid",
"$ref": "decidesk:decision" }
or a sibling key (x-openregister-external-ref) carrying { "app": "decidesk", "schema": "decision" }, with rule (f) resolving app:schema against the declared app dependency set rather than the local register, and rule (b) accepting its presence as "has a canonical ref".
Until then rule (f) cannot be satisfied by any correct cross-app schema, which makes gate-54 unclosable for every app that has one — i.e. every app ADR-066 applies to.
What was fixed locally
pipelinq#743 fixes the 6 findings with genuine local targets (5 ticket relations gained $ref, and task.requestId was repointed from the retired request schema to its ticket successor) and leaves these 6 red with this reason.
Summary
gate-54 rule (f) $REF TARGETS requires that a string
$ref"resolve to a schema key in the same register file set (case-exact)".ADR-066 explicitly sanctions cross-app relations — a property on one app's schema that references an object owned by another app. By construction, that target is not in the referencing app's register file set. So rule (f) and ADR-066 contradict each other: a correctly-declared cross-app relation is a permanent gate-54 failure, and there is no dialect to express it.
Rule (b) RELATION SHAPE closes the other exit: a
format: uuidproperty with a relation-shaped description and no$refis also a finding. So for a cross-app relation the author can fail (f) by naming the foreign schema, or fail (b) by not naming it.Measured on pipelinq (12 findings, 6 of them this shape)
lead.decisions→$ref: 'Decision'ticket.decisions→$ref: 'Decision'ticket.contract→$ref: 'Contract'ticket.caseReferencereceiptTemplate.organizationIdposZReport.storeEach description already names the owner explicitly, e.g.
So the intent is documented in-tree; there is simply no machine-readable way to say it.
Why "just drop the $ref" is the wrong fix
Deleting
$ref: 'Decision'would silence rule (f) onticket.contract/lead.decisions(they are not allformat: uuid, so (b) would not always fire) — but it destroys the only structured statement of what the property points at, to satisfy a checker. That is the same trade as deleting an inconvenient@spec, and I have not done it in pipelinq#743.contractschema, so$ref: 'Contract'→$ref: 'contract'makes gate-54 green in one character. It would also silently repoint a Shillinq relation at a different, local object. A case-insensitive-looking near-miss is exactly where this gate can be satisfied by making the data model wrong.Suggested fix
A first-class cross-app form, so the relation stays declared and machine-readable, e.g.
{ "type": "string", "format": "uuid", "$ref": "decidesk:decision" }or a sibling key (
x-openregister-external-ref) carrying{ "app": "decidesk", "schema": "decision" }, with rule (f) resolvingapp:schemaagainst the declared app dependency set rather than the local register, and rule (b) accepting its presence as "has a canonical ref".Until then rule (f) cannot be satisfied by any correct cross-app schema, which makes gate-54 unclosable for every app that has one — i.e. every app ADR-066 applies to.
What was fixed locally
pipelinq#743 fixes the 6 findings with genuine local targets (5
ticketrelations gained$ref, andtask.requestIdwas repointed from the retiredrequestschema to itsticketsuccessor) and leaves these 6 red with this reason.