Skip to content

Commit be4e58f

Browse files
rubenvdlindeConduction Release Bot
andauthored
refactor(capability): the tool-grant grammar moves to OpenRegister (ADR-099 §5) (#563)
* refactor(capability): the tool-grant grammar moves to OpenRegister (ADR-099 §5) The hermiq half of a PR pair. Requires openregister#2868; this cannot merge until a build carrying OCA\OpenRegister\Service\Capability ships. ADR-099 §5 keeps two grant axes and forbids merging them. The CAPABILITY axis — may agent X use tool T — is not agent-specific and resolves against ToolRegistryFacade, which already lives in OpenRegister, so it moves to sit beside what it resolves against. The Agent.tools BINDING stays here. WHAT MOVED: ToolGrantSet, ToolGrantCodec, ToolGrantResolver, ToolReachResolver, ToolGrantResolutionException, and four of their five test files. Verbatim — only the namespace changed. That codec carries a measured scar (35 of 87 tools parsed wrong) and ADR-095's persistence constraint, and a rewrite-while-moving reopens both. ToolGrantResolverTest STAYS here, repointed. It builds its catalog from hermiq's real tool providers — nine of them — so it is an integration test against a catalog, not a unit test of the grammar. It belongs where the catalog is. 🔴 THERE IS DELIBERATELY NO STUB FOR THE NEW NAMESPACE, and there must never be one. hermiq's test bootstrap maps OCA\OpenRegister\ at tests/Stubs/, so the naive move would have resolved the grammar to a stub — and fifteen test classes exercise its BEHAVIOUR. A stubbed copy would not merely drift, it would make fifteen test classes validate a fake while reporting green. This repo has already paid for that on a THREE-METHOD facade: tests/Stubs/.../ToolRegistryFacade.php drifted by one method and broke a whole matrix leg. This is ~2,400 lines of security-relevant parsing. So bootstrap.php, phpstan.neon and psalm.xml all read the REAL source at ../openregister/lib/Service/Capability. Both topologies put OpenRegister beside hermiq — CI checks additional apps out at server/apps/openregister next to server/apps/hermiq, and the dev tree mirrors it — so one relative path serves both, and no CI change is needed. When it is absent nothing is registered: the class is genuinely missing and the run fails loudly, which is the point. TWO FILES NEEDED NEW IMPORTS THAT NOTHING WOULD HAVE FLAGGED. FacadeToolInvoker and ToolLoop referenced these classes with no `use` at all, because they shared the namespace. A relocation that only rewrote files carrying an explicit import would have left them referencing classes that no longer exist — and PHP would not have said so until the line ran, as a fatal in the middle of a tool call. CheckOpenRegisterCompatibility now names ToolGrantResolver. The grammar used to live here, so no OpenRegister could be too old to supply it; now one can, and without this a stale instance surfaces as a bare "Class not found" from inside a tool call at whatever hour an agent next ran, instead of a clear message at upgrade time. CONSERVATION CHECK, measured rather than assumed: development runs 1834 tests / 5776 assertions; this branch runs 1776 / 5623. The difference is 58 tests and 153 assertions — exactly what openregister#2868 gained. Nothing was dropped in transit. phpcs, phpmd, psalm and phpstan clean; psalm and phpstan were RED on this branch until they were pointed at the real source, which is how the missing type resolution was found rather than assumed away. * fix(capability): declaration-only stubs so static analysis can still see the grammar CI found the hole my relative path did not cover. `phpstan.neon` and `psalm.xml` pointed at ../openregister/lib/Service/Capability, which resolves in the PHPUNIT job — that one checks additional apps out at server/apps/openregister next to server/apps/hermiq — but the PHP-QUALITY job does a plain actions/checkout with no additional apps at all. PHPStan aborted with `Scanned directory /home/runner/work/hermiq/openregister/lib/Service/Capability does not exist`. I had verified the path against ONE job's layout and generalised from it. 🔴 THE STUBS THROW, AND THAT IS THE WHOLE DESIGN. A stub that RETURNED plausible values would make fifteen test classes validate a fake while reporting green — the failure this repo already paid for on a THREE-METHOD facade, where tests/Stubs/Service/Mcp/ToolRegistryFacade.php drifted by one method and broke a whole matrix leg. This is ~2,400 lines of security-relevant parsing. So the split is: signatures for the analysers, behaviour from the real source. * php-quality — PHPStan/Psalm resolve the class from tests/Stubs and type-check the thirteen call sites. Without this every one is an unknown-class error and the gate goes quiet on all of them, which is strictly worse than a stub. * phpunit — tests/bootstrap.php maps the REAL source under the longer PSR-4 prefix, which wins over the blanket tests/Stubs mapping regardless of order. Measured both ways: 1776 tests / 5623 assertions green with the real source present; 91 loud errors without it. A missing dependency cannot masquerade as a passing suite. A signature drift is therefore caught by the test run, which exercises the real class. This file can fail to DESCRIBE a drift; it cannot hide one. ToolGrantResolutionException is the one exception and is constructible: ToolLoop throws it and ToolLoopTest catches it, so a throwing constructor would break a test that never touches the grammar. It carries its grants and nothing else — there is no logic in it to get wrong. * docs(agent-identity): task 3.2 done, with what the relocation actually cost Records the two things that were not visible when 3.2 was written: the stub directory would have swallowed the move silently, and two files referenced the grammar with no import at all because they shared its namespace. Also records the conservation check — 58 tests and 153 assertions left hermiq and exactly 58 and 153 arrived in openregister — because that is the measurement that distinguishes a relocation from a quiet drop. * fix(manifest): three em-dashes in copy a reader sees (gate-96) Gate 96 (manifest-copy-style) merged to .github/main today and fires full-tree, so it lands on hermiq's existing manifest rather than on anything this branch changed. Three user-visible strings break voice.md section 8 — a page description, the setup step, and a walkthrough step. Fixed rather than excluded: an em-dash in shipped copy is the tell the rule exists for, the rewrite costs a colon or a full stop, and the gate is full-tree by design so it would keep blocking every future PR until somebody did this. Verified with the gate's own checker: 3 findings before, 0 after, across all 150 manifest strings including the src/manifest.d/ fragments. --------- Co-authored-by: Conduction Release Bot <release-bot@conduction.nl>
1 parent d0473ce commit be4e58f

43 files changed

Lines changed: 753 additions & 3384 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎lib/Controller/ChatStreamController.php‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,8 @@
4242
use OCA\Hermiq\Service\Engine\SanitizesForSaveTrait;
4343
use OCA\Hermiq\Service\ToolAccessRequestService;
4444
use OCA\Hermiq\Service\Engine\StreamYieldChannel;
45-
use OCA\Hermiq\Service\Engine\ToolGrantResolutionException;
46-
use OCA\Hermiq\Service\Engine\ToolGrantResolver;
45+
use OCA\OpenRegister\Service\Capability\ToolGrantResolutionException;
46+
use OCA\OpenRegister\Service\Capability\ToolGrantResolver;
4747
use OCA\OpenRegister\Db\ObjectEntity;
4848
use OCA\OpenRegister\Service\ObjectService;
4949
use OCP\AppFramework\Controller;

‎lib/Controller/McpRunController.php‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@
5858

5959
use OCA\Hermiq\AppInfo\Application;
6060
use OCA\Hermiq\Service\Engine\RunStepBus;
61-
use OCA\Hermiq\Service\Engine\ToolGrantResolver;
61+
use OCA\OpenRegister\Service\Capability\ToolGrantResolver;
6262
use OCA\Hermiq\Service\Engine\ToolLoop;
6363
use OCA\Hermiq\Service\Llm\RunTokenService;
6464
use OCA\Hermiq\Service\ToolSearchService;

‎lib/Controller/ToolOversightController.php‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,9 @@
3939
use DateTimeImmutable;
4040
use DateTimeZone;
4141
use OCA\Hermiq\AppInfo\Application;
42-
use OCA\Hermiq\Service\Engine\ToolGrantResolver;
43-
use OCA\Hermiq\Service\Engine\ToolGrantSet;
44-
use OCA\Hermiq\Service\Engine\ToolReachResolver;
42+
use OCA\OpenRegister\Service\Capability\ToolGrantResolver;
43+
use OCA\OpenRegister\Service\Capability\ToolGrantSet;
44+
use OCA\OpenRegister\Service\Capability\ToolReachResolver;
4545
use OCA\Hermiq\Service\ToolAccessRequestService;
4646
use OCA\OpenRegister\Db\AuditTrail;
4747
use OCA\OpenRegister\Db\AuditTrailMapper;

‎lib/Mcp/HermiqToolProvider.php‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@
9494
use OCA\Hermiq\AppInfo\Application;
9595
use OCA\Hermiq\Service\CourseRecommendationEngine;
9696
use OCA\Hermiq\Service\DelegationService;
97-
use OCA\Hermiq\Service\Engine\ToolReachResolver;
97+
use OCA\OpenRegister\Service\Capability\ToolReachResolver;
9898
use OCA\Hermiq\Service\MemoryService;
9999
use OCA\Hermiq\Service\NcNative\MailReadService;
100100
use OCA\Hermiq\Service\NcNative\NcNativeWriteService;

‎lib/Mcp/NcMailToolDescriptors.php‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
namespace OCA\Hermiq\Mcp;
3131

3232
use OCA\Hermiq\AppInfo\Application;
33-
use OCA\Hermiq\Service\Engine\ToolReachResolver;
33+
use OCA\OpenRegister\Service\Capability\ToolReachResolver;
3434
use OCA\Hermiq\Service\NcNative\MailReadService;
3535

3636
/**

‎lib/Mcp/NcNativeWriteToolDescriptors.php‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
namespace OCA\Hermiq\Mcp;
2929

3030
use OCA\Hermiq\AppInfo\Application;
31-
use OCA\Hermiq\Service\Engine\ToolReachResolver;
31+
use OCA\OpenRegister\Service\Capability\ToolReachResolver;
3232

3333
/**
3434
* Descriptor source for the NC-native write tools.

‎lib/Repair/CheckOpenRegisterCompatibility.php‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
namespace OCA\Hermiq\Repair;
4646

4747
use OCA\OpenRegister\Event\AgentRunRequestedEvent;
48+
use OCA\OpenRegister\Service\Capability\ToolGrantResolver;
4849
use OCA\OpenRegister\Service\Mcp\ToolRegistryFacade;
4950
use OCP\Migration\IOutput;
5051
use OCP\Migration\IRepairStep;
@@ -81,6 +82,15 @@ class CheckOpenRegisterCompatibility implements IRepairStep {
8182
private const REQUIRED_CLASSES = [
8283
ToolRegistryFacade::class => 'OCA\OpenRegister\Service\Mcp\ToolRegistryFacade (OpenRegister #297)',
8384
AgentRunRequestedEvent::class => 'OCA\OpenRegister\Event\AgentRunRequestedEvent (OpenRegister #306)',
85+
// 🔴 ADDED WITH THE ADR-099 §5 RELOCATION. The capability grammar used to
86+
// live in this app, so no version of OpenRegister could be too old to
87+
// supply it. Now it can — and without this entry a stale instance would
88+
// surface as a bare "Class ... not found" from inside a TOOL CALL, at
89+
// whatever hour an agent next ran, rather than as a clear message at
90+
// upgrade time. `ToolGrantResolver` is the one to name: every other moved
91+
// class arrived in the same commit, and listing five would suggest they
92+
// can be present independently.
93+
ToolGrantResolver::class => 'OCA\OpenRegister\Service\Capability\ToolGrantResolver (ADR-099 §5 relocation)',
8494
];
8595

8696
/**

‎lib/Repair/SeedHydraTriageAgent.php‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@
6666
namespace OCA\Hermiq\Repair;
6767

6868
use OCA\Hermiq\AppInfo\Application;
69-
use OCA\Hermiq\Service\Engine\ToolGrantResolver;
69+
use OCA\OpenRegister\Service\Capability\ToolGrantResolver;
7070
use OCA\OpenRegister\Db\ObjectEntity;
7171
use OCA\OpenRegister\Db\SchemaMapper;
7272
use OCA\OpenRegister\Service\ObjectService;

‎lib/Service/Assistant/AssistantService.php‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838
use OCA\Hermiq\Service\Engine\MessageHistoryHandler;
3939
use OCA\Hermiq\Service\Engine\ResponseGenerationHandler;
4040
use OCA\Hermiq\Service\Engine\SanitizesForSaveTrait;
41-
use OCA\Hermiq\Service\Engine\ToolGrantResolver;
41+
use OCA\OpenRegister\Service\Capability\ToolGrantResolver;
4242
use OCA\Hermiq\Service\GuardrailBlockedException;
4343
use OCA\Hermiq\Service\GuardrailPolicyService;
4444
use OCA\OpenRegister\Db\ObjectEntity;

‎lib/Service/DelegationService.php‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,8 @@
4646
use OCA\Hermiq\AppInfo\Application;
4747
use OCA\Hermiq\Service\Engine\DelegationContext;
4848
use OCA\Hermiq\Service\Engine\DelegationFrame;
49-
use OCA\Hermiq\Service\Engine\ToolGrantResolver;
50-
use OCA\Hermiq\Service\Engine\ToolReachResolver;
49+
use OCA\OpenRegister\Service\Capability\ToolGrantResolver;
50+
use OCA\OpenRegister\Service\Capability\ToolReachResolver;
5151
use OCA\OpenRegister\Db\AuditTrailMapper;
5252
use OCA\OpenRegister\Db\ObjectEntity;
5353
use OCA\OpenRegister\Service\ObjectService;

0 commit comments

Comments
 (0)