@event4u/agent-config 14.21.0 → 14.22.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/.augment-plugin/marketplace.json +3 -3
- package/.augment-plugin/plugin.json +1 -1
- package/.claude-plugin/marketplace.json +1 -1
- package/CHANGELOG.md +198 -0
- package/CREDITS.md +37 -0
- package/dist/agent-src/commands/analyze/inbox.md +380 -5
- package/dist/agent-src/commands/analyze/repo.md +4 -2
- package/dist/agent-src/contexts/communication/rules-auto/token-efficiency-mechanics.md +69 -0
- package/dist/agent-src/contexts/execution/auto-dispatch-classification.md +5 -5
- package/dist/agent-src/rules/external-code-graph-interop.md +19 -14
- package/dist/agent-src/rules/token-efficiency.md +26 -3
- package/dist/agent-src/scripts/_cli_entry.ts +51 -0
- package/dist/agent-src/scripts/archive_completed_roadmaps.ts +14 -23
- package/dist/agent-src/scripts/roadmap_gates.ts +4 -20
- package/dist/agent-src/scripts/roadmap_set_step.ts +5 -2
- package/dist/agent-src/scripts/stubs_due.ts +4 -18
- package/dist/agent-src/scripts/update_roadmap_progress.ts +7 -24
- package/dist/agent-src/skills/code-intelligence/SKILL.md +25 -0
- package/dist/agent-src/skills/roadmap-writing/SKILL.md +17 -0
- package/dist/agent-src/templates/agent-settings.md +0 -1
- package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
- package/dist/cli-delegate/_cli_entry.js +8 -0
- package/dist/cli-delegate/_lib/pin_resolver.js +170 -0
- package/dist/cli-delegate/archival_sweep.js +8 -0
- package/dist/cli-delegate/archive_completed_roadmaps.js +494 -0
- package/dist/cli-delegate/check_update_banner.js +923 -0
- package/dist/cli-delegate/chunk-5EK5WQN6.js +28 -0
- package/dist/cli-delegate/chunk-7XGD2OEU.js +96 -0
- package/dist/cli-delegate/{chunk-643Z7FIC.js → chunk-BIXEDQPZ.js} +6 -0
- package/dist/cli-delegate/chunk-DBH4AHDK.js +155 -0
- package/dist/cli-delegate/chunk-JO25G5H7.js +20 -0
- package/dist/cli-delegate/chunk-LTYVIY76.js +7961 -0
- package/dist/cli-delegate/chunk-QAJS5YWY.js +999 -0
- package/dist/cli-delegate/{chunk-ZVBOHSVF.js → chunk-TGIPZBCW.js} +1 -1
- package/dist/cli-delegate/{chunk-YS6XTLEK.js → chunk-TYSY33MD.js} +56 -6
- package/dist/cli-delegate/chunk-ULET4BYS.js +328 -0
- package/dist/cli-delegate/chunk-YDQSHTD7.js +187 -0
- package/dist/cli-delegate/chunk-YWVXLR44.js +48 -0
- package/dist/cli-delegate/chunk-ZZQBUHFK.js +273 -0
- package/dist/cli-delegate/cmd_conformance.js +2 -2
- package/dist/cli-delegate/cmd_export.js +1 -1
- package/dist/cli-delegate/cmd_handoff.js +26 -2
- package/dist/cli-delegate/cmd_prune.js +1 -1
- package/dist/cli-delegate/cmd_session_recycle.js +2 -2
- package/dist/cli-delegate/cmd_settings_migrate.js +1 -1
- package/dist/cli-delegate/cmd_sync.js +1 -1
- package/dist/cli-delegate/cmd_uninstall.js +1 -1
- package/dist/cli-delegate/cmd_validate.js +1 -1
- package/dist/cli-delegate/dashboard_mode.js +16 -0
- package/dist/cli-delegate/gate_budget.js +24 -0
- package/dist/cli-delegate/gate_execute.js +25 -0
- package/dist/cli-delegate/guarded_baseline.js +18 -0
- package/dist/cli-delegate/resume_probe.js +22 -0
- package/dist/cli-delegate/roadmap_gates.js +638 -0
- package/dist/cli-delegate/roadmap_set_step.js +173 -0
- package/dist/cli-delegate/stub_queue.js +47 -0
- package/dist/cli-delegate/stubs_due.js +38 -0
- package/dist/cli-delegate/ticket_bundles.js +59 -0
- package/dist/cli-delegate/update_roadmap_progress.js +60 -0
- package/dist/discovery/deprecation-report.md +1 -1
- package/dist/discovery/discovery-manifest.json +8 -8
- package/dist/discovery/discovery-manifest.json.sha256 +1 -1
- package/dist/discovery/discovery-manifest.summary.md +1 -1
- package/dist/discovery/dormancy-report.md +1 -1
- package/dist/discovery/orphan-report.md +1 -1
- package/dist/discovery/packs.json +2 -2
- package/dist/discovery/trust-report.md +1 -1
- package/dist/discovery/workspaces.json +2 -2
- package/dist/hooks/dispatch.js +1193 -917
- package/dist/install/claudeLayerCarriage.js +237 -0
- package/dist/install/claudePathsPlan.js +79 -0
- package/dist/install/hostLayerFingerprint.js +1 -1
- package/dist/install/install.mjs +6 -0
- package/dist/install/partitionEligibility.js +231 -117
- package/dist/install/ruleLayerPartition.js +10 -7
- package/dist/mcp/registry-manifest.json +2 -2
- package/dist/mcp/server.json +2 -2
- package/dist/mcp/server.mjs +39219 -38995
- package/dist/scripts/condense.js +32 -25
- package/docs/MIGRATION.md +49 -1
- package/docs/contracts/settings-classes.md +1 -1
- package/docs/guidelines/agent-infra/minimal-safe-diff-mechanics.md +73 -0
- package/package.json +7 -4
- package/src/agent-src/scripts/_cli_entry.ts +51 -0
- package/src/agent-src/scripts/archive_completed_roadmaps.ts +14 -23
- package/src/agent-src/scripts/roadmap_gates.ts +4 -20
- package/src/agent-src/scripts/roadmap_set_step.ts +5 -2
- package/src/agent-src/scripts/stubs_due.ts +4 -18
- package/src/agent-src/scripts/update_roadmap_progress.ts +7 -24
- package/src/config/continuity-surface.json +509 -0
- package/src/config/estate-count-budget.json +1 -1
- package/src/config/gate-coverage.yml +29 -8
- package/src/config/gate-violation-baselines.json +8 -8
- package/src/config/gitignore-block.txt +19 -0
- package/src/config/host-injection-effect.json +73 -0
- package/src/config/packed-binary-manifest.json +33 -0
- package/src/config/preamble-payload-budget.json +112 -105
- package/src/config/publish-surface.json +4 -2
- package/src/install/claudeLayerCarriage.ts +273 -0
- package/src/install/claudePathsPlan.ts +76 -0
- package/src/install/hostLayerFingerprint.ts +1 -1
- package/src/install/partitionEligibility.ts +261 -127
- package/src/install/ruleLayerPartition.ts +19 -23
- package/src/scripts/_dispatch.bash +77 -27
- package/src/scripts/_lib/auto_dispatch.ts +2 -2
- package/src/scripts/_lib/catalogue_layer_parity.ts +146 -0
- package/src/scripts/_lib/continuity_surface.ts +296 -0
- package/src/scripts/_lib/corpus_manifest.ts +26 -9
- package/src/scripts/_lib/hook_settings.ts +1 -1
- package/src/scripts/_lib/injection_effect.ts +151 -0
- package/src/scripts/_lib/layer_overlap_notice.ts +50 -6
- package/src/scripts/_lib/packed_binary_predicate.ts +225 -0
- package/src/scripts/_lib/pin_resolver.ts +13 -0
- package/src/scripts/_lib/regression_neighbourhood.ts +1 -1
- package/src/scripts/_lib/retired_status.ts +74 -0
- package/src/scripts/_lib/skill_catalogue.ts +156 -21
- package/src/scripts/_lib/subagent_capsule.ts +117 -8
- package/src/scripts/ai_council/api_transport.ts +5 -0
- package/src/scripts/ai_council/deliberation_metrics.ts +353 -0
- package/src/scripts/ai_council/prompts.ts +9 -0
- package/src/scripts/ai_council/synthesis_disagreement.ts +206 -0
- package/src/scripts/capture_skill_catalogue.ts +24 -11
- package/src/scripts/check_candidate_lines.ts +499 -0
- package/src/scripts/check_continuity_surface.ts +475 -0
- package/src/scripts/check_dependency_floors.ts +13 -6
- package/src/scripts/check_estate_count.ts +40 -79
- package/src/scripts/check_pack_size.ts +262 -23
- package/src/scripts/check_roadmap_trackable.ts +19 -16
- package/src/scripts/check_rule_layer_partition.ts +54 -34
- package/src/scripts/check_single_delivery.ts +97 -31
- package/src/scripts/check_update_banner.ts +13 -0
- package/src/scripts/code_graph/build.ts +138 -15
- package/src/scripts/code_graph/cli.ts +76 -8
- package/src/scripts/code_graph/extract.ts +11 -0
- package/src/scripts/code_graph/grammar_probe.ts +43 -0
- package/src/scripts/code_graph/loader.ts +88 -14
- package/src/scripts/code_graph/query.ts +121 -11
- package/src/scripts/code_graph/resolution_tiers.ts +238 -0
- package/src/scripts/code_graph/sqlite_store.ts +247 -6
- package/src/scripts/code_graph/types.ts +66 -1
- package/src/scripts/code_graph/validate.ts +23 -0
- package/src/scripts/condense.ts +34 -22
- package/src/scripts/corpus_manifest.ts +5 -5
- package/src/scripts/council_cli.ts +11 -12
- package/src/scripts/count_intent_disagreement.ts +426 -0
- package/src/scripts/handoff_context_hook.ts +79 -0
- package/src/scripts/hook_effect_doctor.ts +82 -1
- package/src/scripts/hook_manifest.json +1 -1
- package/src/scripts/hook_manifest.yaml +64 -12
- package/src/scripts/hooks/chain_nudge_hook.ts +282 -0
- package/src/scripts/hooks/code_graph_context_hook.ts +212 -0
- package/src/scripts/hooks/concern_registry.ts +4 -2
- package/src/scripts/hooks/host_semantics.ts +1 -1
- package/src/scripts/hooks/orchestration_record_hook.ts +2 -2
- package/src/scripts/hooks/rule_inject_hook.ts +1 -1
- package/src/scripts/hooks/skill_route_hook.ts +11 -4
- package/src/scripts/inbox_source_census.ts +322 -0
- package/src/scripts/install-hooks.sh +82 -0
- package/src/scripts/{lint_carrier_integrity.ts → lint_deferral_integrity.ts} +60 -214
- package/src/scripts/lint_plan_risk_register.ts +13 -17
- package/src/scripts/mcp_server/suggest_skill.ts +72 -0
- package/src/scripts/mcp_server/tools.ts +2 -56
- package/src/scripts/media/lib/adapter-contract.md +7 -2
- package/src/scripts/media/lib/fixtures/flux/asset-0001.png +0 -0
- package/src/scripts/media/lib/fixtures/gemini-image/asset-0001.png +0 -0
- package/src/scripts/media/lib/fixtures/ideogram/asset-0001.png +0 -0
- package/src/scripts/rdp_candidates_delta.ts +276 -0
- package/src/scripts/rdp_quality_eval.ts +217 -19
- package/src/scripts/release_mix_taxonomy.json +3 -2
- package/src/scripts/report_host_injection_effect.ts +189 -0
- package/src/scripts/report_layer_overlap.ts +41 -0
- package/src/scripts/report_skill_menu_census.ts +341 -0
- package/src/scripts/skill_tools/score_skill_relevance.ts +70 -6
- package/src/scripts/skill_tools/suggest_skill_for_task.ts +37 -3
- package/src/vendor/grammars/README.md +61 -0
- package/src/vendor/grammars/tree-sitter-javascript.wasm +0 -0
- package/src/vendor/grammars/tree-sitter-php.wasm +0 -0
- package/src/vendor/grammars/tree-sitter-typescript.wasm +0 -0
- package/src/scripts/_lib/carrier_status.ts +0 -118
- package/src/scripts/_lib/estate_carrier_cases.ts +0 -72
- package/src/scripts/hooks/code_graph_nudge_hook.ts +0 -191
|
@@ -37,16 +37,16 @@
|
|
|
37
37
|
* three are red here, deliberately, until a disposition vocabulary exists to
|
|
38
38
|
* express them.
|
|
39
39
|
*
|
|
40
|
-
* A SECOND ENUMERATION
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
*
|
|
40
|
+
* A SECOND ENUMERATION used to run beside this one, over the live roadmaps that
|
|
41
|
+
* declared `status: carrier`, reddening any that no archived parent named. It
|
|
42
|
+
* existed only to police the three exemptions that status bought — from the
|
|
43
|
+
* active roadmap count, from `check_roadmap_trackable` and from the plan risk
|
|
44
|
+
* register — so that a file nobody carried anything to could not wear them for
|
|
45
|
+
* free. ADR-262 deleted the status and all three exemptions, leaving nothing to
|
|
46
|
+
* police: a file that would once have been an unjustified carrier is now
|
|
47
|
+
* ordinary estate, caught by those gates for present-tense reasons rather than
|
|
48
|
+
* for its ancestry. The reciprocal direction — every carry names a destination
|
|
49
|
+
* that exists and back-links — is this enumeration, and it is untouched.
|
|
50
50
|
*
|
|
51
51
|
* Exit codes: 0 = every carry resolves, 1 = at least one does not.
|
|
52
52
|
*/
|
|
@@ -102,12 +102,12 @@ const EXCLUDE_NAMES: ReadonlySet<string> = new Set([
|
|
|
102
102
|
* is an obligation that had a recorded home and lost it. Deleting a live
|
|
103
103
|
* receiver produces this class, not the other one.
|
|
104
104
|
*/
|
|
105
|
-
export type
|
|
105
|
+
export type DeferralProblemClass = 'unannotated' | 'broken-destination';
|
|
106
106
|
|
|
107
|
-
export interface
|
|
107
|
+
export interface DeferralProblem {
|
|
108
108
|
/** Repo-relative path of the archived roadmap whose carry is broken. */
|
|
109
109
|
source: string;
|
|
110
|
-
cls:
|
|
110
|
+
cls: DeferralProblemClass;
|
|
111
111
|
detail: string;
|
|
112
112
|
}
|
|
113
113
|
|
|
@@ -197,7 +197,6 @@ function _locate(root: string, slug: string): { file: string; dir: 'active' | 'l
|
|
|
197
197
|
const OPEN_STEP_RE = /^[ \t]*[-*][ \t]*\[[ ][ \t]*\][ \t]/;
|
|
198
198
|
/** A fenced block's delimiter, in either of the two fence styles in use here. */
|
|
199
199
|
const FENCE_RE = /^[ \t]*(?:```|~~~)/;
|
|
200
|
-
const CARRIER_STATUS_RE = /^status:[ \t]*carrier[ \t]*$/m;
|
|
201
200
|
|
|
202
201
|
/**
|
|
203
202
|
* True when an OPEN step appears OUTSIDE every fenced block.
|
|
@@ -222,11 +221,6 @@ export function hasOpenStep(text: string): boolean {
|
|
|
222
221
|
return false;
|
|
223
222
|
}
|
|
224
223
|
|
|
225
|
-
/** True when the frontmatter — not the body — declares `status: carrier`. */
|
|
226
|
-
export function declaresCarrier(text: string): boolean {
|
|
227
|
-
return CARRIER_STATUS_RE.test(frontmatter(text));
|
|
228
|
-
}
|
|
229
|
-
|
|
230
224
|
/**
|
|
231
225
|
* The standing carry policy for one archived roadmap. See the module header for
|
|
232
226
|
* why this is not `deferralProblems`.
|
|
@@ -235,23 +229,22 @@ export function carryProblems(
|
|
|
235
229
|
root: string,
|
|
236
230
|
sourceSlug: string,
|
|
237
231
|
text: string,
|
|
238
|
-
): Array<{ cls:
|
|
232
|
+
): Array<{ cls: DeferralProblemClass; detail: string }> {
|
|
239
233
|
return carryProblemsFor(root, sourceSlug, parseDeferredItems(text));
|
|
240
234
|
}
|
|
241
235
|
|
|
242
236
|
/**
|
|
243
237
|
* The same policy over an ALREADY-PARSED item list.
|
|
244
238
|
*
|
|
245
|
-
* Split out so the walk parses each candidate once and
|
|
246
|
-
*
|
|
247
|
-
* needs, rather than parsing the same file twice per run.
|
|
239
|
+
* Split out so the walk parses each candidate once and hands the item list to
|
|
240
|
+
* this check, rather than parsing the same file twice per run.
|
|
248
241
|
*/
|
|
249
242
|
export function carryProblemsFor(
|
|
250
243
|
root: string,
|
|
251
244
|
sourceSlug: string,
|
|
252
245
|
items: readonly DeferredItem[],
|
|
253
|
-
): Array<{ cls:
|
|
254
|
-
const problems: Array<{ cls:
|
|
246
|
+
): Array<{ cls: DeferralProblemClass; detail: string }> {
|
|
247
|
+
const problems: Array<{ cls: DeferralProblemClass; detail: string }> = [];
|
|
255
248
|
for (const item of items) {
|
|
256
249
|
const label = item.text === '' ? '(unnamed step)' : item.text;
|
|
257
250
|
if (item.kind === null || item.destination === null) {
|
|
@@ -289,20 +282,6 @@ export function carryProblemsFor(
|
|
|
289
282
|
continue;
|
|
290
283
|
}
|
|
291
284
|
const destText = fs.readFileSync(found.file, 'utf-8');
|
|
292
|
-
// A carrier exists to stay live. Archiving one is the terminal-archival
|
|
293
|
-
// transition that has no vocabulary yet, and it strands every item the
|
|
294
|
-
// carrier itself holds — none of which is an OPEN step, so the check
|
|
295
|
-
// below would not see it.
|
|
296
|
-
if (found.dir === 'archive' && declaresCarrier(destText)) {
|
|
297
|
-
problems.push({
|
|
298
|
-
cls: 'broken-destination',
|
|
299
|
-
detail:
|
|
300
|
-
`destination \`${item.destination}\` is a \`status: carrier\` roadmap that has been ` +
|
|
301
|
-
'archived — a carrier holds obligations whose triggers are unmet, so archiving it ' +
|
|
302
|
-
'strands them. Terminal archival of a live carrier is not expressible yet and fails closed.',
|
|
303
|
-
});
|
|
304
|
-
continue;
|
|
305
|
-
}
|
|
306
285
|
if (found.dir === 'archive' && hasOpenStep(destText)) {
|
|
307
286
|
problems.push({
|
|
308
287
|
cls: 'broken-destination',
|
|
@@ -322,67 +301,20 @@ export function carryProblemsFor(
|
|
|
322
301
|
return problems;
|
|
323
302
|
}
|
|
324
303
|
|
|
325
|
-
/** The live locations a carrier may occupy — both are live per `_locate`. */
|
|
326
|
-
const LIVE_SUBDIRS: readonly string[] = ['', 'later'];
|
|
327
|
-
|
|
328
304
|
/**
|
|
329
|
-
*
|
|
330
|
-
*
|
|
331
|
-
* The enumeration the walk-from-the-archive direction cannot produce. Sorted,
|
|
332
|
-
* repo-relative, and `later/` is included because `_locate` treats a parked
|
|
333
|
-
* receiver as live — a carrier parked there is equally excluded from the active
|
|
334
|
-
* count, so it needs the same justification.
|
|
335
|
-
*/
|
|
336
|
-
export function liveCarriers(root: string): string[] {
|
|
337
|
-
const out: string[] = [];
|
|
338
|
-
for (const sub of LIVE_SUBDIRS) {
|
|
339
|
-
const abs = path.join(root, 'agents', 'roadmaps', sub);
|
|
340
|
-
let entries: string[];
|
|
341
|
-
try {
|
|
342
|
-
entries = fs.readdirSync(abs);
|
|
343
|
-
} catch {
|
|
344
|
-
continue;
|
|
345
|
-
}
|
|
346
|
-
for (const name of entries.sort()) {
|
|
347
|
-
if (!name.endsWith('.md') || EXCLUDE_NAMES.has(name)) {
|
|
348
|
-
continue;
|
|
349
|
-
}
|
|
350
|
-
const full = path.join(abs, name);
|
|
351
|
-
let text: string;
|
|
352
|
-
try {
|
|
353
|
-
if (!fs.statSync(full).isFile()) {
|
|
354
|
-
continue;
|
|
355
|
-
}
|
|
356
|
-
text = fs.readFileSync(full, 'utf-8');
|
|
357
|
-
} catch {
|
|
358
|
-
continue;
|
|
359
|
-
}
|
|
360
|
-
if (declaresCarrier(text)) {
|
|
361
|
-
out.push(path.posix.join('agents/roadmaps', sub, name));
|
|
362
|
-
}
|
|
363
|
-
}
|
|
364
|
-
}
|
|
365
|
-
return out;
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
/**
|
|
369
|
-
* Validate every carry annotation in the dead directories, then every live
|
|
370
|
-
* carrier against the destinations those annotations actually name.
|
|
305
|
+
* Validate every carry annotation in the dead directories.
|
|
371
306
|
*
|
|
372
307
|
* @param walked the dead-roadmap list, when the caller already has it. The walk
|
|
373
308
|
* `statSync`s every file in the archive, so taking it as a parameter is what
|
|
374
309
|
* stops the run doing that twice — once for the ledger's plan and once here.
|
|
375
|
-
* @returns the problems found
|
|
376
|
-
* live carriers were checked.
|
|
310
|
+
* @returns the problems found and how many dead roadmaps were read.
|
|
377
311
|
*/
|
|
378
312
|
export function auditCarries(
|
|
379
313
|
root: string,
|
|
380
314
|
walked: readonly string[] = deadRoadmaps(root),
|
|
381
|
-
): { problems:
|
|
382
|
-
const problems:
|
|
315
|
+
): { problems: DeferralProblem[]; scanned: number } {
|
|
316
|
+
const problems: DeferralProblem[] = [];
|
|
383
317
|
let scanned = 0;
|
|
384
|
-
/** Every slug some dead roadmap named with `carried-to=`. */
|
|
385
|
-
const carriedTo = new Set<string>();
|
|
386
318
|
for (const rel of walked) {
|
|
387
319
|
let text: string;
|
|
388
320
|
try {
|
|
@@ -395,39 +327,15 @@ export function auditCarries(
|
|
|
395
327
|
continue;
|
|
396
328
|
}
|
|
397
329
|
const items = parseDeferredItems(text);
|
|
398
|
-
for (const item of items) {
|
|
399
|
-
if (item.kind === 'carried-to' && item.destination !== null) {
|
|
400
|
-
carriedTo.add(item.destination);
|
|
401
|
-
}
|
|
402
|
-
}
|
|
403
330
|
const sourceSlug = path.basename(rel).replace(/\.md$/, '');
|
|
404
331
|
for (const found of carryProblemsFor(root, sourceSlug, items)) {
|
|
405
332
|
problems.push({ source: rel, ...found });
|
|
406
333
|
}
|
|
407
334
|
}
|
|
408
|
-
|
|
409
|
-
for (const rel of carriers) {
|
|
410
|
-
const slug = path.basename(rel).replace(/\.md$/, '');
|
|
411
|
-
if (carriedTo.has(slug)) {
|
|
412
|
-
continue;
|
|
413
|
-
}
|
|
414
|
-
problems.push({
|
|
415
|
-
source: rel,
|
|
416
|
-
cls: 'broken-destination',
|
|
417
|
-
detail:
|
|
418
|
-
'declares `status: carrier`, but no roadmap under `agents/roadmaps/archive/` or ' +
|
|
419
|
-
'`agents/roadmaps/skipped/` names it with a `<!-- deferred-resolution: ' +
|
|
420
|
-
`carried-to=${slug} -->\` annotation. That status is only legitimate for the ` +
|
|
421
|
-
'RECEIVER of a real carry: it buys exclusion from the active roadmap count, from ' +
|
|
422
|
-
'`check_roadmap_trackable` and from the plan risk register, so a file no archived ' +
|
|
423
|
-
'parent carried anything to would take all three exclusions for nothing. Either ' +
|
|
424
|
-
'record the carry in the parent that deferred the work, or drop `status: carrier`.',
|
|
425
|
-
});
|
|
426
|
-
}
|
|
427
|
-
return { problems, scanned, carriers };
|
|
335
|
+
return { problems, scanned };
|
|
428
336
|
}
|
|
429
337
|
|
|
430
|
-
const USAGE = `usage:
|
|
338
|
+
const USAGE = `usage: lint_deferral_integrity [--root <dir>] [--self-test] [--quiet]
|
|
431
339
|
|
|
432
340
|
--root <dir> audit another checkout (default: this repository)
|
|
433
341
|
--self-test run the built-in discrimination suite and exit
|
|
@@ -454,19 +362,15 @@ Body.
|
|
|
454
362
|
}
|
|
455
363
|
|
|
456
364
|
/**
|
|
457
|
-
* A body that DOCUMENTS roadmap syntax inside a fence
|
|
458
|
-
* used to match
|
|
365
|
+
* A body that DOCUMENTS roadmap syntax inside a fence rather than declaring it.
|
|
366
|
+
* `hasOpenStep` used to match the `- [ ]` line inside it and red an archived
|
|
367
|
+
* receiver that had completed its work.
|
|
459
368
|
*/
|
|
460
|
-
const FENCED_SYNTAX_DOC = 'Syntax this file documents rather than declares:\n\n```markdown\n---\nstatus:
|
|
369
|
+
const FENCED_SYNTAX_DOC = 'Syntax this file documents rather than declares:\n\n```markdown\n---\nstatus: draft\n---\n- [ ] **1.1 An example open step.**\n```\n';
|
|
461
370
|
|
|
462
371
|
/** A back-link that exists only in the body — a mention, never a declaration. */
|
|
463
372
|
const FENCED_BODY_BACKLINK = 'What a back-link looks like, quoted:\n\n```yaml\nparent_roadmap: road-to-parent\nrelates:\n - slug: road-to-parent\n relation: extends\n```\n';
|
|
464
373
|
|
|
465
|
-
/** A receiver that declares itself a carrier — used only where that is the point. */
|
|
466
|
-
function _carrierReceiver(backlink: string): string {
|
|
467
|
-
return _receiver(backlink).replace('---\n# Receiver', 'status: carrier\n---\n# Receiver');
|
|
468
|
-
}
|
|
469
|
-
|
|
470
374
|
/**
|
|
471
375
|
* Each case builds a throwaway tree, runs the real CLI against it with
|
|
472
376
|
* `--root`, and asserts the exit code. Shelling out rather than calling
|
|
@@ -488,7 +392,7 @@ export function selfTestCases(root: string): SelfTestCase[] {
|
|
|
488
392
|
plant(dir);
|
|
489
393
|
return runGateCli(
|
|
490
394
|
root,
|
|
491
|
-
'src/scripts/
|
|
395
|
+
'src/scripts/lint_deferral_integrity.ts',
|
|
492
396
|
['--root', dir, '--quiet'],
|
|
493
397
|
root,
|
|
494
398
|
);
|
|
@@ -503,7 +407,7 @@ export function selfTestCases(root: string): SelfTestCase[] {
|
|
|
503
407
|
{
|
|
504
408
|
name: 'a live receiver with a back-link is accepted',
|
|
505
409
|
expect: 'accept',
|
|
506
|
-
run: build('
|
|
410
|
+
run: build('deferral-ok-', (dir) => {
|
|
507
411
|
parent(dir);
|
|
508
412
|
_writeFixture(
|
|
509
413
|
dir,
|
|
@@ -515,7 +419,7 @@ export function selfTestCases(root: string): SelfTestCase[] {
|
|
|
515
419
|
{
|
|
516
420
|
name: 'a receiver parked under later/ is equally live and is accepted',
|
|
517
421
|
expect: 'accept',
|
|
518
|
-
run: build('
|
|
422
|
+
run: build('deferral-later-', (dir) => {
|
|
519
423
|
parent(dir);
|
|
520
424
|
_writeFixture(
|
|
521
425
|
dir,
|
|
@@ -527,7 +431,7 @@ export function selfTestCases(root: string): SelfTestCase[] {
|
|
|
527
431
|
{
|
|
528
432
|
name: 'a receiver that completed its work and archived is accepted — fulfilment, not loss',
|
|
529
433
|
expect: 'accept',
|
|
530
|
-
run: build('
|
|
434
|
+
run: build('deferral-done-', (dir) => {
|
|
531
435
|
parent(dir);
|
|
532
436
|
_writeFixture(
|
|
533
437
|
dir,
|
|
@@ -539,7 +443,7 @@ export function selfTestCases(root: string): SelfTestCase[] {
|
|
|
539
443
|
{
|
|
540
444
|
name: 'a back-link supplied as a relates: row instead of parent_roadmap is accepted',
|
|
541
445
|
expect: 'accept',
|
|
542
|
-
run: build('
|
|
446
|
+
run: build('deferral-relates-', (dir) => {
|
|
543
447
|
parent(dir);
|
|
544
448
|
_writeFixture(
|
|
545
449
|
dir,
|
|
@@ -548,22 +452,10 @@ export function selfTestCases(root: string): SelfTestCase[] {
|
|
|
548
452
|
);
|
|
549
453
|
}),
|
|
550
454
|
},
|
|
551
|
-
{
|
|
552
|
-
name: 'a live carrier an archived parent actually names is accepted',
|
|
553
|
-
expect: 'accept',
|
|
554
|
-
run: build('carrier-named-', (dir) => {
|
|
555
|
-
parent(dir);
|
|
556
|
-
_writeFixture(
|
|
557
|
-
dir,
|
|
558
|
-
'agents/roadmaps/road-to-receiver.md',
|
|
559
|
-
_carrierReceiver('parent_roadmap: road-to-parent'),
|
|
560
|
-
);
|
|
561
|
-
}),
|
|
562
|
-
},
|
|
563
455
|
{
|
|
564
456
|
name: 'an archived receiver that only documents roadmap syntax in a fence is accepted',
|
|
565
457
|
expect: 'accept',
|
|
566
|
-
run: build('
|
|
458
|
+
run: build('deferral-fenced-', (dir) => {
|
|
567
459
|
parent(dir);
|
|
568
460
|
_writeFixture(
|
|
569
461
|
dir,
|
|
@@ -572,44 +464,10 @@ export function selfTestCases(root: string): SelfTestCase[] {
|
|
|
572
464
|
);
|
|
573
465
|
}),
|
|
574
466
|
},
|
|
575
|
-
{
|
|
576
|
-
name: 'a live carrier no archived parent names is rejected — the status is not free',
|
|
577
|
-
expect: 'reject',
|
|
578
|
-
run: build('carrier-orphan-', (dir) => {
|
|
579
|
-
parent(dir);
|
|
580
|
-
_writeFixture(
|
|
581
|
-
dir,
|
|
582
|
-
'agents/roadmaps/road-to-receiver.md',
|
|
583
|
-
_receiver('parent_roadmap: road-to-parent'),
|
|
584
|
-
);
|
|
585
|
-
_writeFixture(
|
|
586
|
-
dir,
|
|
587
|
-
'agents/roadmaps/road-to-self-declared.md',
|
|
588
|
-
_carrierReceiver('owner: maintainer'),
|
|
589
|
-
);
|
|
590
|
-
}),
|
|
591
|
-
},
|
|
592
|
-
{
|
|
593
|
-
name: 'an orphan carrier parked under later/ is rejected too',
|
|
594
|
-
expect: 'reject',
|
|
595
|
-
run: build('carrier-orphan-later-', (dir) => {
|
|
596
|
-
parent(dir);
|
|
597
|
-
_writeFixture(
|
|
598
|
-
dir,
|
|
599
|
-
'agents/roadmaps/road-to-receiver.md',
|
|
600
|
-
_receiver('parent_roadmap: road-to-parent'),
|
|
601
|
-
);
|
|
602
|
-
_writeFixture(
|
|
603
|
-
dir,
|
|
604
|
-
'agents/roadmaps/later/road-to-self-declared.md',
|
|
605
|
-
_carrierReceiver('owner: maintainer'),
|
|
606
|
-
);
|
|
607
|
-
}),
|
|
608
|
-
},
|
|
609
467
|
{
|
|
610
468
|
name: 'an archived receiver with an asterisk-bulleted OPEN step is rejected',
|
|
611
469
|
expect: 'reject',
|
|
612
|
-
run: build('
|
|
470
|
+
run: build('deferral-asterisk-', (dir) => {
|
|
613
471
|
parent(dir);
|
|
614
472
|
_writeFixture(
|
|
615
473
|
dir,
|
|
@@ -621,7 +479,7 @@ export function selfTestCases(root: string): SelfTestCase[] {
|
|
|
621
479
|
{
|
|
622
480
|
name: 'a back-link that only appears in the body, not the frontmatter, is rejected',
|
|
623
481
|
expect: 'reject',
|
|
624
|
-
run: build('
|
|
482
|
+
run: build('deferral-bodylink-', (dir) => {
|
|
625
483
|
parent(dir);
|
|
626
484
|
_writeFixture(
|
|
627
485
|
dir,
|
|
@@ -630,27 +488,15 @@ export function selfTestCases(root: string): SelfTestCase[] {
|
|
|
630
488
|
);
|
|
631
489
|
}),
|
|
632
490
|
},
|
|
633
|
-
{
|
|
634
|
-
name: 'a receiver that is itself a carrier and has been archived is rejected',
|
|
635
|
-
expect: 'reject',
|
|
636
|
-
run: build('carrier-archived-carrier-', (dir) => {
|
|
637
|
-
parent(dir);
|
|
638
|
-
_writeFixture(
|
|
639
|
-
dir,
|
|
640
|
-
'agents/roadmaps/archive/road-to-receiver.md',
|
|
641
|
-
_carrierReceiver('parent_roadmap: road-to-parent'),
|
|
642
|
-
);
|
|
643
|
-
}),
|
|
644
|
-
},
|
|
645
491
|
{
|
|
646
492
|
name: 'the receiver deleted entirely is rejected',
|
|
647
493
|
expect: 'reject',
|
|
648
|
-
run: build('
|
|
494
|
+
run: build('deferral-deleted-', parent),
|
|
649
495
|
},
|
|
650
496
|
{
|
|
651
497
|
name: 'a receiver archived while still carrying OPEN steps is rejected — stranded, not completed',
|
|
652
498
|
expect: 'reject',
|
|
653
|
-
run: build('
|
|
499
|
+
run: build('deferral-stranded-', (dir) => {
|
|
654
500
|
parent(dir);
|
|
655
501
|
_writeFixture(
|
|
656
502
|
dir,
|
|
@@ -662,7 +508,7 @@ export function selfTestCases(root: string): SelfTestCase[] {
|
|
|
662
508
|
{
|
|
663
509
|
name: 'a receiver under skipped/ is rejected — skipping is not fulfilling',
|
|
664
510
|
expect: 'reject',
|
|
665
|
-
run: build('
|
|
511
|
+
run: build('deferral-skipped-', (dir) => {
|
|
666
512
|
parent(dir);
|
|
667
513
|
_writeFixture(
|
|
668
514
|
dir,
|
|
@@ -674,7 +520,7 @@ export function selfTestCases(root: string): SelfTestCase[] {
|
|
|
674
520
|
{
|
|
675
521
|
name: 'the back-link removed is rejected — the link must verify from both ends',
|
|
676
522
|
expect: 'reject',
|
|
677
|
-
run: build('
|
|
523
|
+
run: build('deferral-nolink-', (dir) => {
|
|
678
524
|
parent(dir);
|
|
679
525
|
_writeFixture(
|
|
680
526
|
dir,
|
|
@@ -686,7 +532,7 @@ export function selfTestCases(root: string): SelfTestCase[] {
|
|
|
686
532
|
{
|
|
687
533
|
name: 'a back-link naming a different parent is rejected',
|
|
688
534
|
expect: 'reject',
|
|
689
|
-
run: build('
|
|
535
|
+
run: build('deferral-reparent-', (dir) => {
|
|
690
536
|
parent(dir);
|
|
691
537
|
_writeFixture(
|
|
692
538
|
dir,
|
|
@@ -711,34 +557,36 @@ export function main(argv: string[]): number {
|
|
|
711
557
|
} else if (arg === '--root') {
|
|
712
558
|
const next = argv[i + 1];
|
|
713
559
|
if (next === undefined) {
|
|
714
|
-
process.stderr.write('
|
|
560
|
+
process.stderr.write('lint_deferral_integrity: --root needs a directory\n');
|
|
715
561
|
return 2;
|
|
716
562
|
}
|
|
717
563
|
root = path.resolve(next);
|
|
718
564
|
i += 1;
|
|
719
565
|
} else if (arg === '--self-test') {
|
|
720
566
|
return runSelfTest({
|
|
721
|
-
gate: '
|
|
567
|
+
gate: 'lint_deferral_integrity',
|
|
722
568
|
cases: selfTestCases(_DEFAULT_ROOT),
|
|
723
|
-
|
|
724
|
-
|
|
569
|
+
// 16/10 before ADR-262 deleted `status: carrier`, which took one
|
|
570
|
+
// accept case (a named live carrier) and three reject cases (two
|
|
571
|
+
// orphan carriers, one archived carrier destination) with it.
|
|
572
|
+
minCases: 12,
|
|
573
|
+
minRejectCases: 7,
|
|
725
574
|
});
|
|
726
575
|
} else {
|
|
727
|
-
process.stderr.write(`
|
|
576
|
+
process.stderr.write(`lint_deferral_integrity: unknown argument ${JSON.stringify(arg)}\n`);
|
|
728
577
|
process.stderr.write(USAGE);
|
|
729
578
|
return 2;
|
|
730
579
|
}
|
|
731
580
|
}
|
|
732
581
|
|
|
733
|
-
const ledger = new GateLedger('
|
|
582
|
+
const ledger = new GateLedger('lint_deferral_integrity');
|
|
734
583
|
const walked = deadRoadmaps(root);
|
|
735
|
-
// Walked ONCE and handed to the audit
|
|
736
|
-
//
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
ledger.plan([...walked, ...carriers]);
|
|
584
|
+
// Walked ONCE and handed to the audit, so the run does not readdir and
|
|
585
|
+
// `statSync` every archived file twice for one reading.
|
|
586
|
+
const { problems, scanned } = auditCarries(root, walked);
|
|
587
|
+
ledger.plan([...walked]);
|
|
740
588
|
const failed = new Set(problems.map((p) => p.source));
|
|
741
|
-
for (const rel of
|
|
589
|
+
for (const rel of walked) {
|
|
742
590
|
if (failed.has(rel)) {
|
|
743
591
|
ledger.fail(rel, problems.find((p) => p.source === rel)?.detail ?? 'broken carry');
|
|
744
592
|
} else {
|
|
@@ -747,7 +595,7 @@ export function main(argv: string[]): number {
|
|
|
747
595
|
}
|
|
748
596
|
try {
|
|
749
597
|
reportScanned({
|
|
750
|
-
gate: '
|
|
598
|
+
gate: 'lint_deferral_integrity',
|
|
751
599
|
scanned,
|
|
752
600
|
units: 'archived roadmap(s)',
|
|
753
601
|
roots: DEAD_DIRS.map((d) => `agents/roadmaps/${d}`),
|
|
@@ -779,16 +627,14 @@ export function main(argv: string[]): number {
|
|
|
779
627
|
process.stderr.write(
|
|
780
628
|
'\n Restore the receiver, restore its `parent_roadmap:` back-link, or move the\n' +
|
|
781
629
|
' carried items back to open in the archived parent. A rename or a re-parent is\n' +
|
|
782
|
-
' not expressible yet and fails closed on purpose.\n'
|
|
783
|
-
' A file listed for declaring `status: carrier` needs neither: record the carry\n' +
|
|
784
|
-
' in the archived parent, or drop the status and let the file be ordinary estate.\n',
|
|
630
|
+
' not expressible yet and fails closed on purpose.\n',
|
|
785
631
|
);
|
|
786
632
|
ledger.report();
|
|
787
633
|
return 1;
|
|
788
634
|
}
|
|
789
635
|
|
|
790
636
|
const verdict = checkRatchet({
|
|
791
|
-
gate: '
|
|
637
|
+
gate: 'lint_deferral_integrity',
|
|
792
638
|
actual: unannotated.length,
|
|
793
639
|
repoRoot: _DEFAULT_ROOT,
|
|
794
640
|
});
|
|
@@ -807,8 +653,8 @@ export function main(argv: string[]): number {
|
|
|
807
653
|
if (!quiet) {
|
|
808
654
|
process.stdout.write(`✅ ${verdict.message}\n`);
|
|
809
655
|
process.stdout.write(
|
|
810
|
-
`✅
|
|
811
|
-
|
|
656
|
+
`✅ lint_deferral_integrity: ${String(scanned)} dead roadmap(s) scanned, ` +
|
|
657
|
+
'every annotated carry resolves.\n',
|
|
812
658
|
);
|
|
813
659
|
}
|
|
814
660
|
ledger.report();
|
|
@@ -9,10 +9,10 @@
|
|
|
9
9
|
*
|
|
10
10
|
* Corpus: ready (non-draft) roadmap files directly under `agents/roadmaps/` code-comment-allow provenance-comment -- the path is this script's operand, not where the code came from
|
|
11
11
|
* (top level only — `archive/`, `skipped/`, `later/`, `stubs/` excluded, as
|
|
12
|
-
* are `template.md` and `dashboard*`). `status: draft`
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
12
|
+
* are `template.md` and `dashboard*`). `status: draft` frontmatter exempts a
|
|
13
|
+
* file, reported as `draft-exempt` and still counted as scanned. A second
|
|
14
|
+
* exempting status, `carrier`, was deleted by ADR-262: a receiver of deferred
|
|
15
|
+
* work is an ordinary plan and carries a real risk register like any other.
|
|
16
16
|
*
|
|
17
17
|
* Checks per ready file:
|
|
18
18
|
* 1. `## Risk Register` section exists — missing = fail unless the
|
|
@@ -329,20 +329,16 @@ export function validateTable(
|
|
|
329
329
|
}
|
|
330
330
|
|
|
331
331
|
/** A frontmatter status that exempts the file, or `null` when none does. */
|
|
332
|
-
export type ExemptingStatus = 'draft'
|
|
332
|
+
export type ExemptingStatus = 'draft';
|
|
333
333
|
|
|
334
334
|
/**
|
|
335
335
|
* Which frontmatter status exempts this file, per contract § 1.
|
|
336
336
|
*
|
|
337
|
-
*
|
|
338
|
-
*
|
|
339
|
-
*
|
|
340
|
-
*
|
|
341
|
-
*
|
|
342
|
-
* A carrier is not a plan. It holds obligations deferred out of an archived
|
|
343
|
-
* parent, each with an unmet resumption trigger, so a plan risk register for it
|
|
344
|
-
* would be manufactured rather than reported. Same category reason that exempts
|
|
345
|
-
* it from `check_roadmap_trackable`'s `## Phase` requirement.
|
|
337
|
+
* The status is returned rather than a bare boolean so the gate's own output
|
|
338
|
+
* names the state it acted on. That mattered while `carrier` was a second
|
|
339
|
+
* exempting value and the predicate printed `draft-exempt` for it; ADR-262
|
|
340
|
+
* deleted that value, and the shape is kept so a future exemption is one enum
|
|
341
|
+
* member rather than a re-argued boolean.
|
|
346
342
|
*/
|
|
347
343
|
export function exemptingStatus(text: string): ExemptingStatus | null {
|
|
348
344
|
const lines = _splitLines(text);
|
|
@@ -350,7 +346,7 @@ export function exemptingStatus(text: string): ExemptingStatus | null {
|
|
|
350
346
|
for (let i = 1; i < lines.length; i++) {
|
|
351
347
|
const line = lines[i] as string;
|
|
352
348
|
if (line.trim() === '---') return null;
|
|
353
|
-
const m = /^status:\s*(draft
|
|
349
|
+
const m = /^status:\s*(draft)\s*$/.exec(line);
|
|
354
350
|
if (m !== null) return m[1] as ExemptingStatus;
|
|
355
351
|
}
|
|
356
352
|
return null;
|
|
@@ -592,7 +588,7 @@ function _blobAt(absPath: string, sha: string): string | null {
|
|
|
592
588
|
return _git(root, ['show', `${sha}:${rel}`]);
|
|
593
589
|
}
|
|
594
590
|
|
|
595
|
-
export type FileStatus = 'ok' | 'draft-exempt' | '
|
|
591
|
+
export type FileStatus = 'ok' | 'draft-exempt' | 'grandfathered' | 'fail';
|
|
596
592
|
|
|
597
593
|
export interface FileResult {
|
|
598
594
|
file: string;
|
|
@@ -879,7 +875,7 @@ export function main(argv?: readonly string[]): number {
|
|
|
879
875
|
for (const p of targets) {
|
|
880
876
|
const result = checkFile(p);
|
|
881
877
|
scanned += 1;
|
|
882
|
-
if (result.status === 'draft-exempt'
|
|
878
|
+
if (result.status === 'draft-exempt') {
|
|
883
879
|
statusLines.push(` ${result.status}: ${p}`);
|
|
884
880
|
} else if (result.status === 'grandfathered') {
|
|
885
881
|
statusLines.push(` grandfathered: ${p}`);
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `suggest_skill_for_task` MCP handler, extracted from `tools.ts`.
|
|
3
|
+
*
|
|
4
|
+
* `tools.ts` sits ~580 lines past the 1,500-line ceiling `check_source_size_budget`
|
|
5
|
+
* enforces as a shrink-only ratchet, so every line there costs one unit of excess
|
|
6
|
+
* and the gate names re-pinning the baseline a defect rather than a fix. Adding a
|
|
7
|
+
* `skills_roots` field to this handler's answer cost four; extracting the handler
|
|
8
|
+
* pays that back many times over and puts it in a file where its prose is free.
|
|
9
|
+
*
|
|
10
|
+
* Cohesive on its own terms too: this is the one handler in the allowlist that
|
|
11
|
+
* owns a ranking policy rather than reading a file, and it is the only consumer
|
|
12
|
+
* of the skill-catalogue resolver on the server side.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* `suggest_skill_for_task` — the recovery path for a truncated catalogue.
|
|
17
|
+
*
|
|
18
|
+
* Promoted from a discovery stub because the defect it answers is measured, not
|
|
19
|
+
* hypothetical: on the one host that publishes its own truncation, a default
|
|
20
|
+
* install had 402 entries dropped from the model-visible skills list — the
|
|
21
|
+
* host's own count against its own denominator, not a subtraction over ours.
|
|
22
|
+
* An agent
|
|
23
|
+
* that cannot see a skill also cannot ask for it by name, so the only reachable
|
|
24
|
+
* question is "what fits this task" — which is what the deterministic ranker
|
|
25
|
+
* answers, over the TREE rather than over the host's catalogue.
|
|
26
|
+
*
|
|
27
|
+
* Read-only and shell-free by construction: it reads SKILL.md frontmatter and
|
|
28
|
+
* returns names, scores and declared personas. No skill BODY is returned — the
|
|
29
|
+
* caller opens what it picked, so a wrong rank costs one read rather than a
|
|
30
|
+
* context payload.
|
|
31
|
+
*/
|
|
32
|
+
export async function suggestSkillForTask(
|
|
33
|
+
args: Record<string, unknown>,
|
|
34
|
+
consumerRoot: string,
|
|
35
|
+
): Promise<Record<string, unknown>> {
|
|
36
|
+
const task = typeof args['task'] === 'string' ? args['task'] : '';
|
|
37
|
+
if (task.trim() === '') {
|
|
38
|
+
return { status: 'error', error: 'task must be a non-empty string', suggestions: [] };
|
|
39
|
+
}
|
|
40
|
+
// `limit`, not `top` — the stub published `limit` and a consumer may have
|
|
41
|
+
// read that contract already. Renaming it on promotion would break a
|
|
42
|
+
// caller for cosmetic reasons.
|
|
43
|
+
const limitRaw = args['limit'];
|
|
44
|
+
const limit =
|
|
45
|
+
typeof limitRaw === 'number' && Number.isFinite(limitRaw) ? Math.floor(limitRaw) : 5;
|
|
46
|
+
|
|
47
|
+
const catalogue = await import('../_lib/skill_catalogue.js');
|
|
48
|
+
// EVERY readable root — see `resolveSkillCatalogueRoots` on why not the first.
|
|
49
|
+
const roots = catalogue.resolveSkillCatalogueRoots(consumerRoot);
|
|
50
|
+
if (roots.length === 0) {
|
|
51
|
+
// A missing catalogue is UNKNOWN, never "no skill fits this task". The
|
|
52
|
+
// second reads as a ranked answer and would be a confident empty list —
|
|
53
|
+
// the same zero-from-silence failure `capture_skill_catalogue` refuses.
|
|
54
|
+
return {
|
|
55
|
+
status: 'no_catalogue',
|
|
56
|
+
searched: [...catalogue.DEFAULT_CATALOGUE_ROOTS],
|
|
57
|
+
consumer_root: consumerRoot,
|
|
58
|
+
suggestions: [],
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const { rank } = await import('../skill_tools/score_skill_relevance.js');
|
|
63
|
+
const rows = rank(task, roots).slice(0, Math.max(1, limit));
|
|
64
|
+
return {
|
|
65
|
+
status: 'ok',
|
|
66
|
+
// `skills_root` kept verbatim (a consumer may read it); `skills_roots` is
|
|
67
|
+
// the additive truth about what was actually ranked.
|
|
68
|
+
skills_root: roots[0] as string,
|
|
69
|
+
skills_roots: roots,
|
|
70
|
+
suggestions: rows.map(([name, score, personas]) => ({ skill: name, score, personas })),
|
|
71
|
+
};
|
|
72
|
+
}
|