Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 0 additions & 5 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,6 @@
"bin": [
"hydra-gates/bin/hydra-gates"
],
"autoload": {
"psr-4": {
"OCA\\OpenRegister\\Contract\\": "hydra-gates/contracts/"
}
},
"require": {
"php": ">=8.1"
},
Expand Down
17 changes: 13 additions & 4 deletions docs/hydra/operations/app-health.md
Original file line number Diff line number Diff line change
Expand Up @@ -287,10 +287,19 @@ day. Neither number was a decision; both were the absence of one.

The cost was not theoretical. hydra-gates v1.8.0 shipped an
`ObjectServiceInterface` **without `patchObject()`**, and because the package
claims `OCA\OpenRegister\Contract\` in its composer autoload — a *longer*
psr-4 prefix than openregister's own — a stale copy in **any** app's vendor
directory defined that contract for the whole instance. Measured:
softwarecatalog's vendor was supplying openregister's interface. Separately,
*used to claim* `OCA\OpenRegister\Contract\` in its composer autoload — a
*longer* psr-4 prefix than openregister's own — a stale copy in **any** app's
vendor directory defined that contract for the whole instance. Measured:
softwarecatalog's vendor was supplying openregister's interface.

> ✅ **That mechanism is gone.** The prefix was removed from the package
> (ConductionNL/.github#531); the contracts still ship, but a consumer now opts
> in from its own test bootstrap behind `interface_exists()`, which is
> order-independent. Four apps needed the opt-in — buildiq, decidiq, filinq and
> stackiq. The others either ship their own contract stubs (dossiq, pipelinq,
> shillinq), own the real one (openregister), or never reference it.

Separately,
nc-vue's AI-companion singleton landed in 2.7.0, so every app below it rendered
a **second** companion hex beside hermiq's on every page. Both fixes had existed
upstream for weeks.
Expand Down
46 changes: 46 additions & 0 deletions hydra-gates/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,52 @@ gap, not a pass — see *Reading a green*.

---

## The shipped OpenRegister contracts are opt-in, not autoloaded

`hydra-gates/contracts/` ships `ObjectServiceInterface` and
`ObjectEntityInterface` so a leaf app can typehint OpenRegister's data-access
surface under PHPUnit without the OpenRegister app installed.

They are **not** autoloaded. This package used to declare

```json
"autoload": { "psr-4": { "OCA\\OpenRegister\\Contract\\": "hydra-gates/contracts/" } }
```

and that was a bug, not a convenience. The prefix is *longer* than
openregister's own `OCA\OpenRegister\` → `lib/`, PSR-4 is longest-prefix-wins,
and the package installs into every consumer's `vendor/` — so whichever app's
autoloader registered first defined OpenRegister's contract **for the whole
process**. Measured on a real instance: softwarecatalog's vendored copy was
supplying openregister's own interface, and a v1.8.0 copy without
`patchObject()` broke callers of the real one (ConductionNL/.github#531).

A consumer that needs these interfaces requires them from its own test
bootstrap, behind a guard:

```php
foreach (['ObjectEntityInterface', 'ObjectServiceInterface'] as $contract) {
if (interface_exists('\\OCA\\OpenRegister\\Contract\\' . $contract) === false) {
$shipped = __DIR__ . '/../vendor/conduction/hydra-gates/hydra-gates/contracts/' . $contract . '.php';
if (file_exists($shipped) === true) {
require_once $shipped;
}
}
}
```

Put it **before** anything that implements the interface — a stub entity that
`implements \OCA\OpenRegister\Contract\ObjectEntityInterface` fatals inside the
bootstrap otherwise, which is a dead run rather than a failed test. And put it
in the bootstrap `phpunit.xml` actually loads: several apps ship two or three.

`interface_exists()` rather than a fallback autoloader, because
`spl_autoload_register` appends relative to *registration order*, and
registration order across independently loaded apps is exactly the thing nobody
controls. Asking whether the interface is resolvable is order-independent.

---

## What it needs at runtime

`bash`, `git`, `python3` (about twenty gates are Python helpers) and `node`
Expand Down
Loading