fix(skill): bound catalog discovery retries

This commit is contained in:
Tianyi Cui
2026-07-29 22:27:49 +08:00
parent c10c72837b
commit db543ffa85
16 changed files with 76 additions and 34 deletions

View File

@@ -15,6 +15,7 @@ import type Schema from 'schemastery'
const SKILL_NAME = /^[a-z0-9]+(?:-[a-z0-9]+)*$/
const DEFAULT_COLLECT_CACHE_ENTRIES = 128
const MAX_COLLECT_ATTEMPTS = 2
const RUNTIME_PROVIDER = 'runtime'
const RUNTIME_RANK = 250
@@ -87,11 +88,11 @@ export interface SkillLookupOptions {
readonly signal?: AbortSignal | undefined
}
/** One catalog observation plus whether every registered provider completed discovery. */
/** One catalog observation plus whether discovery completed within a stable catalog revision. */
export interface SkillCatalogSnapshot {
/** Sorted model-invocable summaries collected in this observation. */
readonly skills: SkillSummary[]
/** Whether every registered provider completed discovery for this observation. */
/** Whether every registered provider completed without a concurrent catalog revision. */
readonly complete: boolean
}
@@ -286,11 +287,11 @@ export class SkillService extends Service {
}
/**
* Observe the current model-invocable catalog and whether all providers completed discovery.
* Observe the current model-invocable catalog and whether discovery completed within a stable revision.
* Incomplete observations are never cached, allowing consumers to retain last-good state and
* retry on their next request boundary.
* @param options - lookup options; `cwd` selects project roots and `signal` cancels discovery.
* @returns sorted summaries plus provider-completeness state.
* @returns sorted summaries plus discovery-completeness state.
*/
async snapshot(options: SkillLookupOptions = {}): Promise<SkillCatalogSnapshot> {
const collected = await this.collect(options)
@@ -333,6 +334,7 @@ export class SkillService extends Service {
private async collect(options: SkillLookupOptions): Promise<CollectResult> {
throwIfAborted(options.signal)
let attempt = 1
while (true) {
const providerRevision = this.providerRevision
const runtimeRevision = this.runtimeRevision
@@ -342,7 +344,13 @@ export class SkillService extends Service {
const result = await this.collectFresh(options)
throwIfAborted(options.signal)
if (providerRevision !== this.providerRevision || runtimeRevision !== this.runtimeRevision) continue
if (providerRevision !== this.providerRevision || runtimeRevision !== this.runtimeRevision) {
if (attempt < MAX_COLLECT_ATTEMPTS) {
attempt += 1
continue
}
return { entries: result.entries, cacheable: false }
}
if (result.cacheable) {
this.collectCache.set(key, result.entries)
if (this.collectCache.size > this.collectCacheMaxEntries) {