@harness-engineering/orchestrator 0.9.1 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.mts CHANGED
@@ -1,10 +1,11 @@
1
1
  import * as _harness_engineering_types from '@harness-engineering/types';
2
2
  import { Issue, AgentEvent, WorkflowConfig, TokenUsage, ConcernSignal, ScopeTier, EscalationConfig, IssueRoutingDecision, Result, WorkflowDefinition, BackendDef, RoutingConfig, WorkspaceConfig, HooksConfig, AgentBackend, SessionStartParams, AgentSession, AgentError, TurnParams, TurnResult, CheckScriptDefinition, OutputRetentionConfig, RoutingDecision, RoutingUseCase, ContainerConfig, SecretConfig, AgentConfig, LocalModelStatus, CustomTaskDefinition, MaintenanceConfig, TokenScope, AuthToken, AuthTokenPublic, IndexedFileKind, SessionSearchResult, ReindexStats, SessionSummarizationConfig, SessionSummary, SessionSummaryMeta, SessionsConfig, GatewayEvent, NotificationEnvelope, NotificationDeliveryResult, NotificationSinkConfig, NotificationsConfig } from '@harness-engineering/types';
3
- import { IssueTrackerClient, Issue as Issue$1, TrackerConfig, eventSourcing, CacheMetricsRecorder, ArchiveHooks, SkillProposal, ProposalGateFinding } from '@harness-engineering/core';
3
+ import { IssueTrackerClient, Issue as Issue$1, TrackerConfig, eventSourcing, CacheMetricsRecorder, ArchiveHooks, SkillProposal, ProposalGateFinding, SkillKind } from '@harness-engineering/core';
4
4
  import { EnrichedSpec, ComplexityScore, SimulationResult, IntelligencePipeline, WeightedRecommendation, AnalysisProvider } from '@harness-engineering/intelligence';
5
5
  import { GraphStore } from '@harness-engineering/graph';
6
6
  import { execFile } from 'node:child_process';
7
7
  import { EventEmitter } from 'node:events';
8
+ import { PoolStateProvider, SchedulerTimerHandle } from '@harness-engineering/local-models';
8
9
  import { z } from 'zod';
9
10
 
10
11
  /**
@@ -1697,6 +1698,45 @@ declare class Orchestrator extends EventEmitter {
1697
1698
  * so this map is the single source of truth post-migration.
1698
1699
  */
1699
1700
  private localResolvers;
1701
+ /** Phase 4 (D5): pool-state port shared by all local/pi resolvers. Null when LMLM disabled. */
1702
+ private poolStateProvider;
1703
+ private poolStateStore;
1704
+ /**
1705
+ * LMLM Phase 6: live model pool + its installer. Constructed only when
1706
+ * `localModels.enabled` and a real `PoolStateStore` exists (not a test
1707
+ * override). Exposed to the server via `getModelPool()`, which retires the
1708
+ * proposals-route 501 stub for `kind: 'model'` approve/reject. Null when LMLM
1709
+ * is disabled. `PoolManager` reads `store.snapshot()` lazily, so constructing
1710
+ * before `store.load()` (in initLocalModelAndPipeline) is safe.
1711
+ */
1712
+ private modelPool;
1713
+ private modelInstaller;
1714
+ /**
1715
+ * S1 drain re-entrancy guard (P7-SUG-DRAIN-REENTRANCY). `drainDeferredEvictions`
1716
+ * is fired fire-and-forget from `emitWorkerExit` (and, since P7-SUG-DRAIN-LIVENESS,
1717
+ * piggybacked on each refresh tick). Two overlapping drains would both read the
1718
+ * same `listPendingEvictions()` snapshot, both re-check `isLocalModelInUse`, and
1719
+ * both `await pool.evict` the SAME name — double-calling the installer and
1720
+ * broadcasting a duplicate `evict_completed` frame. The single-threaded event
1721
+ * loop makes a plain boolean sufficient: a drain that arrives while one is
1722
+ * running returns early rather than double-processing.
1723
+ */
1724
+ private draining;
1725
+ /**
1726
+ * LMLM Phase 6: single per-instance background refresh scheduler. Started in
1727
+ * `initLocalModelAndPipeline` when the pool exists; stopped in `stop()`. Null
1728
+ * when LMLM is disabled. Exposed to the server via `getRefreshScheduler()`.
1729
+ */
1730
+ private refreshScheduler;
1731
+ /**
1732
+ * LMLM Phase 7: the hardware-aware recommender bound at scheduler start. Reused
1733
+ * by `GET /api/v1/local-models/recommendations`. Null when LMLM is disabled (no
1734
+ * pool → scheduler never armed). Ranks the (currently empty) candidate set —
1735
+ * see the Phase 2 candidate-parser gap noted on `startRefreshScheduler`.
1736
+ */
1737
+ private modelRecommender;
1738
+ /** Test seam: injected timer/clock for the scheduler so no real 24h timer runs. */
1739
+ private readonly schedulerTimerOverride;
1700
1740
  /**
1701
1741
  * Spec B Phase 3: skill catalog (name + cognitiveMode) read once at
1702
1742
  * construction from `projectRoot/agents/skills/`. Consulted by
@@ -1765,6 +1805,14 @@ declare class Orchestrator extends EventEmitter {
1765
1805
  tracker?: IssueTrackerClient;
1766
1806
  backend?: AgentBackend;
1767
1807
  execFileFn?: ExecFileFn$1;
1808
+ poolState?: PoolStateProvider;
1809
+ /** LMLM Phase 6 test seam: inject the RefreshScheduler timer/clock. */
1810
+ schedulerTimer?: {
1811
+ setTimer?: (cb: () => void, delayMs: number) => SchedulerTimerHandle;
1812
+ clearTimer?: (handle: SchedulerTimerHandle) => void;
1813
+ now?: () => number;
1814
+ random?: () => number;
1815
+ };
1768
1816
  });
1769
1817
  /**
1770
1818
  * Phase 5: construct the OTLP/HTTP trace exporter and wire telemetry fanout.
@@ -1784,6 +1832,29 @@ declare class Orchestrator extends EventEmitter {
1784
1832
  * with its backendName + endpoint from the config.
1785
1833
  */
1786
1834
  private buildLocalModelStatuses;
1835
+ /**
1836
+ * S1 conservative in-use probe (ADR 0060). Returns `true` when ANY agent run
1837
+ * is live AND `ollamaName` is a currently-resolved (or last-detected) local
1838
+ * model — i.e. an agent could be routing inference to it right now.
1839
+ *
1840
+ * This signal is AGENT-RUN-COARSE, not per-request: `state.running` is keyed
1841
+ * by GitHub issue (spawned agent runs), NOT by inference call, and no
1842
+ * per-model request counter exists today. The probe therefore MAY OVER-DEFER
1843
+ * (a swap waits until the pool is idle). Over-deferral is exactly S1's
1844
+ * intended safe failure — never yank a model mid-request; occasionally wait
1845
+ * longer than strictly necessary. A fine-grained per-request signal is an
1846
+ * explicit deferred gap (ADR 0060).
1847
+ */
1848
+ private isLocalModelInUse;
1849
+ /**
1850
+ * S1 drain (ADR 0060): complete any eviction that was DEFERRED because its
1851
+ * target was in use, now that the probe reports it idle. Best-effort — called
1852
+ * from the run-completion path; it never blocks dispatch and swallows
1853
+ * per-model errors (a failed or still-busy evict stays pending for the next
1854
+ * drain). `pendingEviction` is a transient overlay, so a missed drain simply
1855
+ * leaves the flag set until the next completion re-checks it.
1856
+ */
1857
+ private drainDeferredEvictions;
1787
1858
  private createTracker;
1788
1859
  /**
1789
1860
  * Creates a TaskRunner for the maintenance scheduler.
@@ -1914,6 +1985,31 @@ declare class Orchestrator extends EventEmitter {
1914
1985
  * before constructing the intelligence pipeline. Subscribes the dashboard
1915
1986
  * broadcast stub to status changes. Called exactly once from start().
1916
1987
  */
1988
+ /**
1989
+ * LMLM Phase 6: construct the live `PoolManager` (Ollama installer + the
1990
+ * loaded pool-state store) and stash it for `getModelPool()`. Defensive
1991
+ * config fallbacks: `ollamaEndpoint → http://localhost:11434`. The pool reads
1992
+ * `store.snapshot()` lazily, so this runs safely at construction time before
1993
+ * `store.load()`.
1994
+ */
1995
+ private initModelPool;
1996
+ /**
1997
+ * LMLM Phase 6: arm the single background refresh scheduler over the live
1998
+ * pool. No-op when LMLM is disabled (`modelPool` null). Each tick runs
1999
+ * hardware→recommend→reconcile(D12 drift)→diff→emit→score-writeback.
2000
+ *
2001
+ * NOTE (deferred): the recommender is seeded with an empty candidate set —
2002
+ * Phase 2's live-HF→RankerCandidate parser was never built, so autonomous
2003
+ * swap-proposal discovery is out of scope here (flagged concern). The tick
2004
+ * still performs F10 drift reconciliation, O1 logging, and re-ranks/dedups
2005
+ * whatever candidates are supplied — the wiring is complete and candidate
2006
+ * breadth is the only piece deferred to the Phase 2 recommender.
2007
+ */
2008
+ private startRefreshScheduler;
2009
+ /** Resolve the hardware profile for a refresh tick (operator override wins). */
2010
+ private detectLmlmHardware;
2011
+ /** Map the on-disk model-proposal queue to F7 dedup pairs (open→pending, rejected→rejected). */
2012
+ private lmlmDedupSource;
1917
2013
  private initLocalModelAndPipeline;
1918
2014
  /**
1919
2015
  * Starts the polling loop and the internal HTTP server.
@@ -2337,6 +2433,13 @@ interface LocalModelResolverOptions {
2337
2433
  apiKey?: string;
2338
2434
  /** Normalized candidate list (already turned from string|string[] into string[]). */
2339
2435
  configured: string[];
2436
+ /**
2437
+ * Phase 4 (D5): optional read-only pool-state port. When provided, the
2438
+ * candidate list derives from pool entries (currentScore desc → ollamaName)
2439
+ * instead of `configured`. When absent (default), behavior is byte-identical
2440
+ * to the pre-Phase-4 resolver.
2441
+ */
2442
+ poolState?: PoolStateProvider;
2340
2443
  /** Probe cadence in ms; default 30_000, minimum 1_000. */
2341
2444
  probeIntervalMs?: number;
2342
2445
  /**
@@ -2367,6 +2470,7 @@ declare class LocalModelResolver {
2367
2470
  private readonly endpoint;
2368
2471
  private readonly apiKey?;
2369
2472
  private readonly configured;
2473
+ private readonly poolState?;
2370
2474
  private readonly probeIntervalMs;
2371
2475
  private readonly fetchModels;
2372
2476
  private readonly logger;
@@ -2391,6 +2495,12 @@ declare class LocalModelResolver {
2391
2495
  constructor(opts: LocalModelResolverOptions);
2392
2496
  resolveModel(): string | null;
2393
2497
  getStatus(): LocalModelStatus;
2498
+ /**
2499
+ * Effective candidate list. With a poolState port present the list derives
2500
+ * from pool entries (currentScore desc → ollamaName); otherwise the static
2501
+ * `configured` list is returned unchanged (byte-identical to pre-Phase-4).
2502
+ */
2503
+ private candidates;
2394
2504
  onStatusChange(handler: (status: LocalModelStatus) => void): () => void;
2395
2505
  probe(): Promise<LocalModelStatus>;
2396
2506
  private runProbe;
@@ -3520,7 +3630,7 @@ declare function promote(projectPath: string, proposalId: string, decidedBy: str
3520
3630
  */
3521
3631
  interface ProposalCreatedData {
3522
3632
  id: string;
3523
- kind: SkillProposal['kind'];
3633
+ kind: SkillKind;
3524
3634
  name: string;
3525
3635
  targetSkill?: string;
3526
3636
  proposedBy: string;
@@ -3528,14 +3638,14 @@ interface ProposalCreatedData {
3528
3638
  }
3529
3639
  interface ProposalApprovedData {
3530
3640
  id: string;
3531
- kind: SkillProposal['kind'];
3641
+ kind: SkillKind;
3532
3642
  name: string;
3533
3643
  targetSkill?: string;
3534
3644
  decidedBy: string;
3535
3645
  }
3536
3646
  interface ProposalRejectedData {
3537
3647
  id: string;
3538
- kind: SkillProposal['kind'];
3648
+ kind: SkillKind;
3539
3649
  name: string;
3540
3650
  decidedBy: string;
3541
3651
  reason: string;
package/dist/index.d.ts CHANGED
@@ -1,10 +1,11 @@
1
1
  import * as _harness_engineering_types from '@harness-engineering/types';
2
2
  import { Issue, AgentEvent, WorkflowConfig, TokenUsage, ConcernSignal, ScopeTier, EscalationConfig, IssueRoutingDecision, Result, WorkflowDefinition, BackendDef, RoutingConfig, WorkspaceConfig, HooksConfig, AgentBackend, SessionStartParams, AgentSession, AgentError, TurnParams, TurnResult, CheckScriptDefinition, OutputRetentionConfig, RoutingDecision, RoutingUseCase, ContainerConfig, SecretConfig, AgentConfig, LocalModelStatus, CustomTaskDefinition, MaintenanceConfig, TokenScope, AuthToken, AuthTokenPublic, IndexedFileKind, SessionSearchResult, ReindexStats, SessionSummarizationConfig, SessionSummary, SessionSummaryMeta, SessionsConfig, GatewayEvent, NotificationEnvelope, NotificationDeliveryResult, NotificationSinkConfig, NotificationsConfig } from '@harness-engineering/types';
3
- import { IssueTrackerClient, Issue as Issue$1, TrackerConfig, eventSourcing, CacheMetricsRecorder, ArchiveHooks, SkillProposal, ProposalGateFinding } from '@harness-engineering/core';
3
+ import { IssueTrackerClient, Issue as Issue$1, TrackerConfig, eventSourcing, CacheMetricsRecorder, ArchiveHooks, SkillProposal, ProposalGateFinding, SkillKind } from '@harness-engineering/core';
4
4
  import { EnrichedSpec, ComplexityScore, SimulationResult, IntelligencePipeline, WeightedRecommendation, AnalysisProvider } from '@harness-engineering/intelligence';
5
5
  import { GraphStore } from '@harness-engineering/graph';
6
6
  import { execFile } from 'node:child_process';
7
7
  import { EventEmitter } from 'node:events';
8
+ import { PoolStateProvider, SchedulerTimerHandle } from '@harness-engineering/local-models';
8
9
  import { z } from 'zod';
9
10
 
10
11
  /**
@@ -1697,6 +1698,45 @@ declare class Orchestrator extends EventEmitter {
1697
1698
  * so this map is the single source of truth post-migration.
1698
1699
  */
1699
1700
  private localResolvers;
1701
+ /** Phase 4 (D5): pool-state port shared by all local/pi resolvers. Null when LMLM disabled. */
1702
+ private poolStateProvider;
1703
+ private poolStateStore;
1704
+ /**
1705
+ * LMLM Phase 6: live model pool + its installer. Constructed only when
1706
+ * `localModels.enabled` and a real `PoolStateStore` exists (not a test
1707
+ * override). Exposed to the server via `getModelPool()`, which retires the
1708
+ * proposals-route 501 stub for `kind: 'model'` approve/reject. Null when LMLM
1709
+ * is disabled. `PoolManager` reads `store.snapshot()` lazily, so constructing
1710
+ * before `store.load()` (in initLocalModelAndPipeline) is safe.
1711
+ */
1712
+ private modelPool;
1713
+ private modelInstaller;
1714
+ /**
1715
+ * S1 drain re-entrancy guard (P7-SUG-DRAIN-REENTRANCY). `drainDeferredEvictions`
1716
+ * is fired fire-and-forget from `emitWorkerExit` (and, since P7-SUG-DRAIN-LIVENESS,
1717
+ * piggybacked on each refresh tick). Two overlapping drains would both read the
1718
+ * same `listPendingEvictions()` snapshot, both re-check `isLocalModelInUse`, and
1719
+ * both `await pool.evict` the SAME name — double-calling the installer and
1720
+ * broadcasting a duplicate `evict_completed` frame. The single-threaded event
1721
+ * loop makes a plain boolean sufficient: a drain that arrives while one is
1722
+ * running returns early rather than double-processing.
1723
+ */
1724
+ private draining;
1725
+ /**
1726
+ * LMLM Phase 6: single per-instance background refresh scheduler. Started in
1727
+ * `initLocalModelAndPipeline` when the pool exists; stopped in `stop()`. Null
1728
+ * when LMLM is disabled. Exposed to the server via `getRefreshScheduler()`.
1729
+ */
1730
+ private refreshScheduler;
1731
+ /**
1732
+ * LMLM Phase 7: the hardware-aware recommender bound at scheduler start. Reused
1733
+ * by `GET /api/v1/local-models/recommendations`. Null when LMLM is disabled (no
1734
+ * pool → scheduler never armed). Ranks the (currently empty) candidate set —
1735
+ * see the Phase 2 candidate-parser gap noted on `startRefreshScheduler`.
1736
+ */
1737
+ private modelRecommender;
1738
+ /** Test seam: injected timer/clock for the scheduler so no real 24h timer runs. */
1739
+ private readonly schedulerTimerOverride;
1700
1740
  /**
1701
1741
  * Spec B Phase 3: skill catalog (name + cognitiveMode) read once at
1702
1742
  * construction from `projectRoot/agents/skills/`. Consulted by
@@ -1765,6 +1805,14 @@ declare class Orchestrator extends EventEmitter {
1765
1805
  tracker?: IssueTrackerClient;
1766
1806
  backend?: AgentBackend;
1767
1807
  execFileFn?: ExecFileFn$1;
1808
+ poolState?: PoolStateProvider;
1809
+ /** LMLM Phase 6 test seam: inject the RefreshScheduler timer/clock. */
1810
+ schedulerTimer?: {
1811
+ setTimer?: (cb: () => void, delayMs: number) => SchedulerTimerHandle;
1812
+ clearTimer?: (handle: SchedulerTimerHandle) => void;
1813
+ now?: () => number;
1814
+ random?: () => number;
1815
+ };
1768
1816
  });
1769
1817
  /**
1770
1818
  * Phase 5: construct the OTLP/HTTP trace exporter and wire telemetry fanout.
@@ -1784,6 +1832,29 @@ declare class Orchestrator extends EventEmitter {
1784
1832
  * with its backendName + endpoint from the config.
1785
1833
  */
1786
1834
  private buildLocalModelStatuses;
1835
+ /**
1836
+ * S1 conservative in-use probe (ADR 0060). Returns `true` when ANY agent run
1837
+ * is live AND `ollamaName` is a currently-resolved (or last-detected) local
1838
+ * model — i.e. an agent could be routing inference to it right now.
1839
+ *
1840
+ * This signal is AGENT-RUN-COARSE, not per-request: `state.running` is keyed
1841
+ * by GitHub issue (spawned agent runs), NOT by inference call, and no
1842
+ * per-model request counter exists today. The probe therefore MAY OVER-DEFER
1843
+ * (a swap waits until the pool is idle). Over-deferral is exactly S1's
1844
+ * intended safe failure — never yank a model mid-request; occasionally wait
1845
+ * longer than strictly necessary. A fine-grained per-request signal is an
1846
+ * explicit deferred gap (ADR 0060).
1847
+ */
1848
+ private isLocalModelInUse;
1849
+ /**
1850
+ * S1 drain (ADR 0060): complete any eviction that was DEFERRED because its
1851
+ * target was in use, now that the probe reports it idle. Best-effort — called
1852
+ * from the run-completion path; it never blocks dispatch and swallows
1853
+ * per-model errors (a failed or still-busy evict stays pending for the next
1854
+ * drain). `pendingEviction` is a transient overlay, so a missed drain simply
1855
+ * leaves the flag set until the next completion re-checks it.
1856
+ */
1857
+ private drainDeferredEvictions;
1787
1858
  private createTracker;
1788
1859
  /**
1789
1860
  * Creates a TaskRunner for the maintenance scheduler.
@@ -1914,6 +1985,31 @@ declare class Orchestrator extends EventEmitter {
1914
1985
  * before constructing the intelligence pipeline. Subscribes the dashboard
1915
1986
  * broadcast stub to status changes. Called exactly once from start().
1916
1987
  */
1988
+ /**
1989
+ * LMLM Phase 6: construct the live `PoolManager` (Ollama installer + the
1990
+ * loaded pool-state store) and stash it for `getModelPool()`. Defensive
1991
+ * config fallbacks: `ollamaEndpoint → http://localhost:11434`. The pool reads
1992
+ * `store.snapshot()` lazily, so this runs safely at construction time before
1993
+ * `store.load()`.
1994
+ */
1995
+ private initModelPool;
1996
+ /**
1997
+ * LMLM Phase 6: arm the single background refresh scheduler over the live
1998
+ * pool. No-op when LMLM is disabled (`modelPool` null). Each tick runs
1999
+ * hardware→recommend→reconcile(D12 drift)→diff→emit→score-writeback.
2000
+ *
2001
+ * NOTE (deferred): the recommender is seeded with an empty candidate set —
2002
+ * Phase 2's live-HF→RankerCandidate parser was never built, so autonomous
2003
+ * swap-proposal discovery is out of scope here (flagged concern). The tick
2004
+ * still performs F10 drift reconciliation, O1 logging, and re-ranks/dedups
2005
+ * whatever candidates are supplied — the wiring is complete and candidate
2006
+ * breadth is the only piece deferred to the Phase 2 recommender.
2007
+ */
2008
+ private startRefreshScheduler;
2009
+ /** Resolve the hardware profile for a refresh tick (operator override wins). */
2010
+ private detectLmlmHardware;
2011
+ /** Map the on-disk model-proposal queue to F7 dedup pairs (open→pending, rejected→rejected). */
2012
+ private lmlmDedupSource;
1917
2013
  private initLocalModelAndPipeline;
1918
2014
  /**
1919
2015
  * Starts the polling loop and the internal HTTP server.
@@ -2337,6 +2433,13 @@ interface LocalModelResolverOptions {
2337
2433
  apiKey?: string;
2338
2434
  /** Normalized candidate list (already turned from string|string[] into string[]). */
2339
2435
  configured: string[];
2436
+ /**
2437
+ * Phase 4 (D5): optional read-only pool-state port. When provided, the
2438
+ * candidate list derives from pool entries (currentScore desc → ollamaName)
2439
+ * instead of `configured`. When absent (default), behavior is byte-identical
2440
+ * to the pre-Phase-4 resolver.
2441
+ */
2442
+ poolState?: PoolStateProvider;
2340
2443
  /** Probe cadence in ms; default 30_000, minimum 1_000. */
2341
2444
  probeIntervalMs?: number;
2342
2445
  /**
@@ -2367,6 +2470,7 @@ declare class LocalModelResolver {
2367
2470
  private readonly endpoint;
2368
2471
  private readonly apiKey?;
2369
2472
  private readonly configured;
2473
+ private readonly poolState?;
2370
2474
  private readonly probeIntervalMs;
2371
2475
  private readonly fetchModels;
2372
2476
  private readonly logger;
@@ -2391,6 +2495,12 @@ declare class LocalModelResolver {
2391
2495
  constructor(opts: LocalModelResolverOptions);
2392
2496
  resolveModel(): string | null;
2393
2497
  getStatus(): LocalModelStatus;
2498
+ /**
2499
+ * Effective candidate list. With a poolState port present the list derives
2500
+ * from pool entries (currentScore desc → ollamaName); otherwise the static
2501
+ * `configured` list is returned unchanged (byte-identical to pre-Phase-4).
2502
+ */
2503
+ private candidates;
2394
2504
  onStatusChange(handler: (status: LocalModelStatus) => void): () => void;
2395
2505
  probe(): Promise<LocalModelStatus>;
2396
2506
  private runProbe;
@@ -3520,7 +3630,7 @@ declare function promote(projectPath: string, proposalId: string, decidedBy: str
3520
3630
  */
3521
3631
  interface ProposalCreatedData {
3522
3632
  id: string;
3523
- kind: SkillProposal['kind'];
3633
+ kind: SkillKind;
3524
3634
  name: string;
3525
3635
  targetSkill?: string;
3526
3636
  proposedBy: string;
@@ -3528,14 +3638,14 @@ interface ProposalCreatedData {
3528
3638
  }
3529
3639
  interface ProposalApprovedData {
3530
3640
  id: string;
3531
- kind: SkillProposal['kind'];
3641
+ kind: SkillKind;
3532
3642
  name: string;
3533
3643
  targetSkill?: string;
3534
3644
  decidedBy: string;
3535
3645
  }
3536
3646
  interface ProposalRejectedData {
3537
3647
  id: string;
3538
- kind: SkillProposal['kind'];
3648
+ kind: SkillKind;
3539
3649
  name: string;
3540
3650
  decidedBy: string;
3541
3651
  reason: string;