Skip to content

Commit f81c2d8

Browse files
committed
fix: allow package skill bundle fallback
1 parent 0131bf4 commit f81c2d8

2 files changed

Lines changed: 32 additions & 2 deletions

File tree

mcp/profile-contract.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -667,11 +667,13 @@ function buildBundleDecisionV2(portfolio, input = {}) {
667667
tokenCount: maxTokens === null || !Number.isInteger(tokenCounts[id]) ? null : tokenCounts[id]
668668
}
669669
}
670-
// reserved / missing resolution: keep package identity but mark mandatory missing as blocker later
670+
// W/G resolution is an overlay. The package portfolio remains the fallback
671+
// identity unless a caller explicitly requires a resolved external Skill.
672+
const requireResolvedSkills = input.requireResolvedSkills === true
671673
for (const id of closureIds) {
672674
if (excluded.has(id) || !mandatoryClosure.has(id)) continue
673675
const trace = resolutionById.get(id)
674-
if (trace && trace.selectedLayer === 'missing') {
676+
if (requireResolvedSkills && trace && trace.selectedLayer === 'missing') {
675677
blockers.push({
676678
code: 'skill-resolution-missing',
677679
id,

scripts/test-skill-portfolio.js

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -387,6 +387,34 @@ assert.strictEqual(v2Decision.budget.selected.bytes,
387387
assert.strictEqual(v2Decision.budget.tokens.status, 'N/A')
388388
assert.deepStrictEqual(v2Decision.writes, [])
389389

390+
const missingResolutionRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'devcodex-portfolio-resolution-'))
391+
try {
392+
const missingResolutionEnv = {
393+
HOME: path.join(missingResolutionRoot, 'home'),
394+
USERPROFILE: path.join(missingResolutionRoot, 'home'),
395+
DEVCODEX_GLOBAL_SKILLS_ROOT: path.join(missingResolutionRoot, 'empty-global-skills')
396+
}
397+
const packageFallbackDecision = buildBundleDecisionV2(first, {
398+
candidateIds: ['dev-testing'],
399+
maxBytes: 1024 * 1024,
400+
cwd: missingResolutionRoot,
401+
env: missingResolutionEnv
402+
})
403+
assert.strictEqual(packageFallbackDecision.completion, 'complete')
404+
assert(packageFallbackDecision.selected.every(item => item.selectedLayer === 'package'))
405+
const requiredResolutionDecision = buildBundleDecisionV2(first, {
406+
candidateIds: ['dev-testing'],
407+
maxBytes: 1024 * 1024,
408+
cwd: missingResolutionRoot,
409+
env: missingResolutionEnv,
410+
requireResolvedSkills: true
411+
})
412+
assert.strictEqual(requiredResolutionDecision.completion, 'blocked')
413+
assert(requiredResolutionDecision.blockers.some(item => item.code === 'skill-resolution-missing'))
414+
} finally {
415+
fs.rmSync(missingResolutionRoot, { recursive: true, force: true })
416+
}
417+
390418
const overBudgetMandatory = buildBundleDecisionV2(first, {
391419
candidateIds: ['dev-testing'],
392420
maxSkills: 1,

0 commit comments

Comments
 (0)