fix(gateway): resolve InteractionCoordinationService handoff factory via optional DI token (#1145)
Fixes stack#1145 — InteractionCoordinationService fails Nest DI on compiled boot.
Root cause
The constructor's 3rd param handoffIdFactory: () => string = () => crypto.randomUUID() had a TypeScript default but no @Inject. Under emitDecoratorMetadata (tsc) — and under the gateway's vitest unplugin-swc with decoratorMetadata: true — the reflected design:paramtypes entry is Function. CoordModule lists the service as a bare provider, so Nest tries to resolve token Function, finds no provider, and throws argument Function at index [2]. Direct-construction unit tests (which pass the factory positionally) and tsx dev runs masked it.
Fix (preferred, lowest blast radius)
Import Optional from @nestjs/common; export HANDOFF_ID_FACTORY = Symbol('HANDOFF_ID_FACTORY').
Decorate the 3rd param @Optional() @Inject(HANDOFF_ID_FACTORY), keeping the = () => crypto.randomUUID() default.
@Inject(...) overrides the reflected Function token; @Optional() + no provider registered means Nest injects undefined, so the TS default applies → production handoff-id generation is unchanged.
coord.module.ts NOT modified; existing positional construction in interaction-coordination.service.test.ts NOT modified.
apps/gateway/src/coord/interaction-coordination.di.test.ts (new regression test — boots real CoordModule via @nestjs/testing; overrides only AuthGuard to isolate the module from the app-global AuthModule)
Red-first proof (in-process; SWC emits paramtypes, so no compiled-dist needed)
FAIL src/coord/interaction-coordination.di.test.ts > CoordModule DI (compiled-metadata boot)
Nest can't resolve dependencies of the InteractionCoordinationService
(Symbol(COORDINATION_PORT), Symbol(COORDINATION_CONFIG), ?).
... the argument Function at index [2] is available in the CoordModule module.
Independent gates (author = pi ≠ verifier ≠ reviewer)
Integrator-verify: PASS — lineage/path-fence (exactly the 2 files; parent == base 9185b0cc); focused coord 9/9; full gateway 697 passed / 17 skipped; typecheck/lint/build rc=0. Masking re-derivation: reverted ONLY the service fix (kept the AuthGuard-override test) → test went RED again with Function at index [2] → the guard override does NOT mask the DI defect; the test is genuine.
Code + security review: PASS — fix mechanism correct; undefined→TS-default keeps production crypto.randomUUID(); no provider added; guard override is orthogonal to provider-constructor resolution (separate Nest code paths) and lives only in the test; no secrets, no auth bypass in production.
Traceability: issue #1145; design 18e0488d0; plan 575ad743a; base-pin SSOT 5f3ababd8. Branched off next @ 9185b0cc; disjoint from #1166 (types) so the merge into current next is conflict-free. Closes #1145.
## fix(gateway): resolve InteractionCoordinationService handoff factory via optional DI token (#1145)
Fixes stack#1145 — `InteractionCoordinationService` fails Nest DI on **compiled boot**.
### Root cause
The constructor's 3rd param `handoffIdFactory: () => string = () => crypto.randomUUID()` had a TypeScript default but no `@Inject`. Under `emitDecoratorMetadata` (tsc) — and under the gateway's vitest `unplugin-swc` with `decoratorMetadata: true` — the reflected `design:paramtypes` entry is `Function`. `CoordModule` lists the service as a bare provider, so Nest tries to resolve token `Function`, finds no provider, and throws `argument Function at index [2]`. Direct-construction unit tests (which pass the factory positionally) and `tsx` dev runs masked it.
### Fix (preferred, lowest blast radius)
- Import `Optional` from `@nestjs/common`; export `HANDOFF_ID_FACTORY = Symbol('HANDOFF_ID_FACTORY')`.
- Decorate the 3rd param `@Optional() @Inject(HANDOFF_ID_FACTORY)`, **keeping** the `= () => crypto.randomUUID()` default.
- `@Inject(...)` overrides the reflected `Function` token; `@Optional()` + **no provider registered** means Nest injects `undefined`, so the TS default applies → production handoff-id generation is unchanged.
- `coord.module.ts` NOT modified; existing positional construction in `interaction-coordination.service.test.ts` NOT modified.
### Files (2, path-fenced)
- `apps/gateway/src/coord/interaction-coordination.service.ts` (fix)
- `apps/gateway/src/coord/interaction-coordination.di.test.ts` (new regression test — boots real `CoordModule` via `@nestjs/testing`; overrides only `AuthGuard` to isolate the module from the app-global `AuthModule`)
### Red-first proof (in-process; SWC emits paramtypes, so no compiled-dist needed)
```
FAIL src/coord/interaction-coordination.di.test.ts > CoordModule DI (compiled-metadata boot)
Nest can't resolve dependencies of the InteractionCoordinationService
(Symbol(COORDINATION_PORT), Symbol(COORDINATION_CONFIG), ?).
... the argument Function at index [2] is available in the CoordModule module.
```
### Independent gates (author = pi ≠ verifier ≠ reviewer)
- **Integrator-verify:** PASS — lineage/path-fence (exactly the 2 files; parent == base 9185b0cc); focused coord 9/9; full gateway 697 passed / 17 skipped; typecheck/lint/build rc=0. **Masking re-derivation:** reverted ONLY the service fix (kept the AuthGuard-override test) → test went RED again with `Function at index [2]` → the guard override does NOT mask the DI defect; the test is genuine.
- **Code + security review:** PASS — fix mechanism correct; `undefined`→TS-default keeps production `crypto.randomUUID()`; no provider added; guard override is orthogonal to provider-constructor resolution (separate Nest code paths) and lives only in the test; no secrets, no auth bypass in production.
Traceability: issue #1145; design `18e0488d0`; plan `575ad743a`; base-pin SSOT `5f3ababd8`. Branched off `next` @ `9185b0cc`; disjoint from #1166 (types) so the merge into current `next` is conflict-free. `Closes #1145`.
root cause: emitDecoratorMetadata reflected the third constructor parameter as Function and Nest attempted to resolve it
fix: optional HANDOFF_ID_FACTORY injection token, no production provider, preserving undefined -> crypto.randomUUID() default and unchanged positional construction
TDD: real CoordModule red at Function index [2], then green; test overrides only unrelated AuthGuard because its AUTH provider comes from AppModule's global AuthModule context
Closes#1145
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
fix(gateway): resolve InteractionCoordinationService handoff factory via optional DI token (#1145)
Fixes stack#1145 —
InteractionCoordinationServicefails Nest DI on compiled boot.Root cause
The constructor's 3rd param
handoffIdFactory: () => string = () => crypto.randomUUID()had a TypeScript default but no@Inject. UnderemitDecoratorMetadata(tsc) — and under the gateway's vitestunplugin-swcwithdecoratorMetadata: true— the reflecteddesign:paramtypesentry isFunction.CoordModulelists the service as a bare provider, so Nest tries to resolve tokenFunction, finds no provider, and throwsargument Function at index [2]. Direct-construction unit tests (which pass the factory positionally) andtsxdev runs masked it.Fix (preferred, lowest blast radius)
Optionalfrom@nestjs/common; exportHANDOFF_ID_FACTORY = Symbol('HANDOFF_ID_FACTORY').@Optional() @Inject(HANDOFF_ID_FACTORY), keeping the= () => crypto.randomUUID()default.@Inject(...)overrides the reflectedFunctiontoken;@Optional()+ no provider registered means Nest injectsundefined, so the TS default applies → production handoff-id generation is unchanged.coord.module.tsNOT modified; existing positional construction ininteraction-coordination.service.test.tsNOT modified.Files (2, path-fenced)
apps/gateway/src/coord/interaction-coordination.service.ts(fix)apps/gateway/src/coord/interaction-coordination.di.test.ts(new regression test — boots realCoordModulevia@nestjs/testing; overrides onlyAuthGuardto isolate the module from the app-globalAuthModule)Red-first proof (in-process; SWC emits paramtypes, so no compiled-dist needed)
Independent gates (author = pi ≠ verifier ≠ reviewer)
9185b0cc); focused coord 9/9; full gateway 697 passed / 17 skipped; typecheck/lint/build rc=0. Masking re-derivation: reverted ONLY the service fix (kept the AuthGuard-override test) → test went RED again withFunction at index [2]→ the guard override does NOT mask the DI defect; the test is genuine.undefined→TS-default keeps productioncrypto.randomUUID(); no provider added; guard override is orthogonal to provider-constructor resolution (separate Nest code paths) and lives only in the test; no secrets, no auth bypass in production.Traceability: issue #1145; design
18e0488d0; plan575ad743a; base-pin SSOT5f3ababd8. Branched offnext@9185b0cc; disjoint from #1166 (types) so the merge into currentnextis conflict-free.Closes #1145.