akm-cli 0.9.0-beta.56 → 0.9.0-beta.58

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.
Files changed (53) hide show
  1. package/dist/assets/prompts/extract-session.md +5 -1
  2. package/dist/cli/config-migrate.js +7 -1
  3. package/dist/commands/config-cli.js +8 -11
  4. package/dist/commands/health/stash-exposure.js +46 -0
  5. package/dist/commands/health/windows.js +6 -7
  6. package/dist/commands/health.js +31 -10
  7. package/dist/commands/improve/collapse-detector.js +2 -1
  8. package/dist/commands/improve/consolidate.js +207 -159
  9. package/dist/commands/improve/distill/promote-memory.js +4 -3
  10. package/dist/commands/improve/distill/quality-gate.js +7 -4
  11. package/dist/commands/improve/distill-promotion-policy.js +826 -167
  12. package/dist/commands/improve/distill.js +26 -12
  13. package/dist/commands/improve/extract-prompt.js +16 -2
  14. package/dist/commands/improve/extract.js +16 -8
  15. package/dist/commands/improve/improve-auto-accept.js +22 -1
  16. package/dist/commands/improve/loop-stages.js +7 -2
  17. package/dist/commands/improve/memory/memory-belief.js +14 -15
  18. package/dist/commands/improve/memory/memory-contradiction-detect.js +60 -32
  19. package/dist/commands/improve/memory/memory-improve.js +27 -27
  20. package/dist/commands/improve/preparation.js +4 -0
  21. package/dist/commands/improve/procedural.js +1 -0
  22. package/dist/commands/improve/recombine.js +1 -0
  23. package/dist/commands/improve/reflect-noise.js +1 -1
  24. package/dist/commands/improve/reflect.js +4 -3
  25. package/dist/commands/improve/shared.js +9 -6
  26. package/dist/commands/proposal/drain-policies.js +4 -2
  27. package/dist/commands/read/remember-cli.js +1 -1
  28. package/dist/commands/read/show.js +15 -0
  29. package/dist/commands/remember.js +11 -12
  30. package/dist/commands/sources/init.js +5 -1
  31. package/dist/commands/sources/stash-skeleton.js +34 -0
  32. package/dist/core/asset/frontmatter.js +22 -0
  33. package/dist/core/common.js +1 -15
  34. package/dist/core/config/config-io.js +10 -1
  35. package/dist/core/config/config-migration.js +2 -15
  36. package/dist/core/config/config-schema.js +15 -3
  37. package/dist/core/config/config.js +22 -14
  38. package/dist/core/paths.js +4 -4
  39. package/dist/core/time.js +53 -0
  40. package/dist/indexer/db/db.js +51 -46
  41. package/dist/indexer/indexer.js +77 -65
  42. package/dist/indexer/search/db-search.js +41 -6
  43. package/dist/indexer/search/ranking-contributors.js +14 -8
  44. package/dist/indexer/search/search-source.js +15 -3
  45. package/dist/integrations/agent/profiles.js +7 -1
  46. package/dist/llm/feature-gate.js +4 -8
  47. package/dist/output/renderers.js +4 -0
  48. package/dist/scripts/migrate-storage.js +84 -60
  49. package/dist/scripts/migrations/import-fs-improve-runs-to-db.js +6 -0
  50. package/dist/storage/repositories/registry-cache.js +2 -1
  51. package/dist/storage/repositories/registry-index-cache-repository.js +46 -0
  52. package/dist/workflows/runtime/runs.js +6 -1
  53. package/package.json +1 -1
@@ -331,7 +331,6 @@ async function akmIndexReal(options) {
331
331
  walkWarnings: [],
332
332
  dirsNeedingLlm: [],
333
333
  embeddingResult: null,
334
- graphExtractionResult: null,
335
334
  };
336
335
  onProgress({
337
336
  phase: "summary",
@@ -399,14 +398,21 @@ async function akmIndexReal(options) {
399
398
  }
400
399
  });
401
400
  }
402
- // ── Extracted helpers for indexing ────────────────────────────────────────────
403
- async function indexEntries(db, allSourceEntries, isIncremental, builtAtMs, hadRemovedSources, doFullDelete = false, onProgress) {
401
+ /**
402
+ * Phase 1 (async): walk every source directory and pre-generate all metadata
403
+ * outside any transaction, producing the per-directory scan records that
404
+ * {@link persistDirRecords} later writes.
405
+ *
406
+ * generateMetadataFlat is async (uses dynamic import for the matcher/renderer
407
+ * registry), so it cannot run inside a db.transaction() callback — hence the
408
+ * split into a pure-ish scan pass and a synchronous persist pass.
409
+ */
410
+ async function scanSourceDirs(db, allSourceEntries, isIncremental, builtAtMs, hadRemovedSources, onProgress) {
404
411
  let scannedDirs = 0;
405
412
  let skippedDirs = 0;
406
413
  let generatedCount = 0;
407
414
  const warnings = [];
408
415
  const seenPaths = new Set();
409
- const dirsNeedingLlm = [];
410
416
  const dirRecords = [];
411
417
  let processedDirs = 0;
412
418
  let priorDirsChanged = hadRemovedSources;
@@ -426,6 +432,53 @@ async function indexEntries(db, allSourceEntries, isIncremental, builtAtMs, hadR
426
432
  reportScanProgress(`${kind === "scan" ? "Rescanning" : "Skipping"} ${path.relative(currentStashDir, dirPath) || "."} ` +
427
433
  `from ${currentStashDir}: ${reason.kind}${detail}${rowInfo}`);
428
434
  };
435
+ // Duplicate-directory guard shared by the wiki-root and normal branches. A
436
+ // dir may surface via multiple stash roots; only the first occurrence is
437
+ // indexed, and the later ones are recorded as skips. Returns true when the
438
+ // dir was already seen (caller should skip further processing).
439
+ const markSeenOrSkipDuplicate = (dirPath, currentStashDir, files) => {
440
+ const resolved = path.resolve(dirPath);
441
+ if (seenPaths.has(resolved)) {
442
+ const reason = { kind: "duplicate-dir" };
443
+ dirRecords.push({ dirPath, currentStashDir, files, stash: null, skip: true, reason });
444
+ reportDirDecision("skip", dirPath, currentStashDir, reason);
445
+ return true;
446
+ }
447
+ seenPaths.add(resolved);
448
+ return false;
449
+ };
450
+ // Incremental freshness gate shared by both branches: consult the persisted
451
+ // dir state and record either a skip (unchanged + eligible for incremental
452
+ // skip) or a scan record carrying the candidate stash.
453
+ const recordFreshnessDecision = (dirPath, currentStashDir, stateFiles, stash) => {
454
+ const previousState = getDirIndexState(db, dirPath, stateFiles, builtAtMs);
455
+ if (isIncremental && !previousState.stale && canUseIncrementalSkip(previousState, priorDirsChanged)) {
456
+ skippedDirs++;
457
+ dirRecords.push({
458
+ dirPath,
459
+ currentStashDir,
460
+ files: stateFiles,
461
+ stash: null,
462
+ skip: true,
463
+ reason: previousState.reason,
464
+ });
465
+ reportDirDecision("skip", dirPath, currentStashDir, previousState.reason, previousState.persistedRowCount);
466
+ return;
467
+ }
468
+ scannedDirs++;
469
+ priorDirsChanged = true;
470
+ const reason = isIncremental ? previousState.reason : { kind: "full-rebuild" };
471
+ dirRecords.push({
472
+ dirPath,
473
+ currentStashDir,
474
+ files: stateFiles,
475
+ stash,
476
+ skip: false,
477
+ reason,
478
+ persistedRowCount: previousState.persistedRowCount,
479
+ });
480
+ reportDirDecision("scan", dirPath, currentStashDir, reason, previousState.persistedRowCount);
481
+ };
429
482
  for (const sourceAdded of allSourceEntries) {
430
483
  const currentStashDir = sourceAdded.path;
431
484
  const fileContexts = walkStashFlat(currentStashDir);
@@ -463,33 +516,9 @@ async function indexEntries(db, allSourceEntries, isIncremental, builtAtMs, hadR
463
516
  }
464
517
  }
465
518
  for (const [dirPath, { files, entries }] of wikiDirGroups) {
466
- if (seenPaths.has(path.resolve(dirPath))) {
467
- const reason = { kind: "duplicate-dir" };
468
- dirRecords.push({ dirPath, currentStashDir, files, stash: null, skip: true, reason });
469
- reportDirDecision("skip", dirPath, currentStashDir, reason);
470
- continue;
471
- }
472
- seenPaths.add(path.resolve(dirPath));
473
- const previousState = getDirIndexState(db, dirPath, files, builtAtMs);
474
- if (isIncremental && !previousState.stale && canUseIncrementalSkip(previousState, priorDirsChanged)) {
475
- skippedDirs++;
476
- dirRecords.push({ dirPath, currentStashDir, files, stash: null, skip: true, reason: previousState.reason });
477
- reportDirDecision("skip", dirPath, currentStashDir, previousState.reason, previousState.persistedRowCount);
519
+ if (markSeenOrSkipDuplicate(dirPath, currentStashDir, files))
478
520
  continue;
479
- }
480
- scannedDirs++;
481
- priorDirsChanged = true;
482
- const reason = isIncremental ? previousState.reason : { kind: "full-rebuild" };
483
- dirRecords.push({
484
- dirPath,
485
- currentStashDir,
486
- files,
487
- stash: { entries },
488
- skip: false,
489
- reason,
490
- persistedRowCount: previousState.persistedRowCount,
491
- });
492
- reportDirDecision("scan", dirPath, currentStashDir, reason, previousState.persistedRowCount);
521
+ recordFreshnessDecision(dirPath, currentStashDir, files, { entries });
493
522
  }
494
523
  continue;
495
524
  }
@@ -504,13 +533,8 @@ async function indexEntries(db, allSourceEntries, isIncremental, builtAtMs, hadR
504
533
  }
505
534
  for (const [dirPath, files] of dirGroups) {
506
535
  const indexableFiles = files.filter((file) => shouldIndexStashFile(currentStashDir, file));
507
- if (seenPaths.has(path.resolve(dirPath))) {
508
- const reason = { kind: "duplicate-dir" };
509
- dirRecords.push({ dirPath, currentStashDir, files: indexableFiles, stash: null, skip: true, reason });
510
- reportDirDecision("skip", dirPath, currentStashDir, reason);
536
+ if (markSeenOrSkipDuplicate(dirPath, currentStashDir, indexableFiles))
511
537
  continue;
512
- }
513
- seenPaths.add(path.resolve(dirPath));
514
538
  if (indexableFiles.length === 0) {
515
539
  skippedDirs++;
516
540
  const reason = { kind: "no-indexable-files" };
@@ -540,37 +564,17 @@ async function indexEntries(db, allSourceEntries, isIncremental, builtAtMs, hadR
540
564
  if (generated.entries.length > 0) {
541
565
  generatedCount += generated.entries.length;
542
566
  }
543
- const previousState = getDirIndexState(db, dirPath, staleFiles, builtAtMs);
544
- if (isIncremental && !previousState.stale && canUseIncrementalSkip(previousState, priorDirsChanged)) {
545
- skippedDirs++;
546
- dirRecords.push({
547
- dirPath,
548
- currentStashDir,
549
- files: staleFiles,
550
- stash: null,
551
- skip: true,
552
- reason: previousState.reason,
553
- });
554
- reportDirDecision("skip", dirPath, currentStashDir, previousState.reason, previousState.persistedRowCount);
555
- continue;
556
- }
557
- scannedDirs++;
558
- priorDirsChanged = true;
559
- const reason = isIncremental ? previousState.reason : { kind: "full-rebuild" };
560
- dirRecords.push({
561
- dirPath,
562
- currentStashDir,
563
- files: staleFiles,
564
- stash,
565
- skip: false,
566
- reason,
567
- persistedRowCount: previousState.persistedRowCount,
568
- });
569
- reportDirDecision("scan", dirPath, currentStashDir, reason, previousState.persistedRowCount);
567
+ recordFreshnessDecision(dirPath, currentStashDir, staleFiles, stash);
570
568
  }
571
569
  }
572
- // Phase 2 (sync): write all pre-generated metadata inside a single transaction.
573
- //
570
+ return { dirRecords, scannedDirs, skippedDirs, generatedCount, warnings };
571
+ }
572
+ /**
573
+ * Phase 2 (sync): write all pre-generated scan records inside a single
574
+ * transaction, returning the directories that still need LLM enrichment.
575
+ */
576
+ function persistDirRecords(db, dirRecords, doFullDelete, warnings) {
577
+ const dirsNeedingLlm = [];
574
578
  // Cross-stash dedup: track indexed assets by content identity
575
579
  // (type + filename + description) so the same asset from a lower-priority
576
580
  // stash root is skipped when a higher-priority root already covers it.
@@ -692,6 +696,14 @@ async function indexEntries(db, allSourceEntries, isIncremental, builtAtMs, hadR
692
696
  }
693
697
  });
694
698
  insertTransaction();
699
+ return { dirsNeedingLlm };
700
+ }
701
+ async function indexEntries(db, allSourceEntries, isIncremental, builtAtMs, hadRemovedSources, doFullDelete = false, onProgress) {
702
+ // Phase 1 (async): walk directories and pre-generate all metadata outside the
703
+ // transaction.
704
+ const { dirRecords, scannedDirs, skippedDirs, generatedCount, warnings } = await scanSourceDirs(db, allSourceEntries, isIncremental, builtAtMs, hadRemovedSources, onProgress);
705
+ // Phase 2 (sync): write all pre-generated metadata inside a single transaction.
706
+ const { dirsNeedingLlm } = persistDirRecords(db, dirRecords, doFullDelete, warnings);
695
707
  return { scannedDirs, skippedDirs, generatedCount, warnings, dirsNeedingLlm };
696
708
  }
697
709
  async function enhanceDirsWithLlm(db, config, dirsNeedingLlm, onProgress, signal, reEnrich = false) {
@@ -20,7 +20,7 @@ import { defaultRendererRegistry } from "../../core/asset/asset-registry.js";
20
20
  import { getDbPath } from "../../core/paths.js";
21
21
  import { warn } from "../../core/warn.js";
22
22
  import { getCurrentWorkflowScopeKey } from "../../workflows/authoring/scope-key.js";
23
- import { closeDatabase, getAllEntries, getEntryById, getEntryCount, getMeta, getPositiveFeedbackCountsByIds, openExistingDatabase, sanitizeFtsQuery, searchFts, searchVec, } from "../db/db.js";
23
+ import { closeDatabase, getAllEntries, getBaseBeliefStatesForDerivedTwins, getEntryById, getEntryCount, getMeta, getPositiveFeedbackCountsByIds, openExistingDatabase, sanitizeFtsQuery, searchFts, searchVec, } from "../db/db.js";
24
24
  import { ensureIndex } from "../ensure-index.js";
25
25
  import { collectGraphRelatedHit, computeGraphBoost, loadGraphBoostContext, } from "../graph/graph-boost.js";
26
26
  import { isProposedQuality } from "../passes/metadata.js";
@@ -201,7 +201,11 @@ async function searchDatabase(db, query, searchType, limit, stashDir, allSourceD
201
201
  const qualityFiltered = includeProposed
202
202
  ? scopeFiltered
203
203
  : scopeFiltered.filter((ie) => !isProposedQuality(ie.entry.quality));
204
- const beliefFiltered = qualityFiltered.filter((ie) => matchBeliefFilter(ie.entry.type, ie.entry.beliefState, beliefFilter));
204
+ // 03-R3: derived twins inherit their base's demoting belief state here too,
205
+ // so the belief FILTER (and the reported hit state) stays consistent on the
206
+ // enumerate/browse path — not only on the FTS-scored path below.
207
+ inheritDerivedTwinBeliefStates(db, qualityFiltered);
208
+ const beliefFiltered = qualityFiltered.filter((ie) => matchBeliefFilter(ie.entry.beliefState, beliefFilter));
205
209
  const selected = beliefFiltered.slice(0, limit);
206
210
  const hits = await Promise.all(selected.map((ie) => buildDbHit({
207
211
  entry: ie.entry,
@@ -302,6 +306,9 @@ async function searchDatabase(db, query, searchType, limit, stashDir, allSourceD
302
306
  catch {
303
307
  // Non-fatal — ranking proceeds without scoped utility on any error.
304
308
  }
309
+ // 03-R3: derived twins inherit their base's demoting belief state before
310
+ // ranking, so the (03) belief-state ranker demotes a stale flag-free twin.
311
+ inheritDerivedTwinBeliefStates(db, scored);
305
312
  applyRankingRules({
306
313
  db,
307
314
  query,
@@ -358,7 +365,7 @@ async function searchDatabase(db, query, searchType, limit, stashDir, allSourceD
358
365
  const qualityFiltered = includeProposed
359
366
  ? scopeFiltered
360
367
  : scopeFiltered.filter((item) => !isProposedQuality(item.entry.quality));
361
- const beliefFiltered = qualityFiltered.filter((item) => matchBeliefFilter(item.entry.type, item.entry.beliefState, beliefFilter));
368
+ const beliefFiltered = qualityFiltered.filter((item) => matchBeliefFilter(item.entry.beliefState, beliefFilter));
362
369
  const rankMs = Date.now() - tRank0;
363
370
  const selected = beliefFiltered.slice(0, limit);
364
371
  const hits = await Promise.all(selected.map(({ entry, filePath, score, rankingMode, utilityBoosted }) => {
@@ -386,11 +393,39 @@ async function searchDatabase(db, query, searchType, limit, stashDir, allSourceD
386
393
  }));
387
394
  return { embedMs, rankMs, hits };
388
395
  }
389
- function matchBeliefFilter(type, beliefState, filter) {
396
+ /**
397
+ * 03-R3: let each `.derived` twin inherit its base memory's demoting belief
398
+ * state for this ranking pass, so a stale flag-free twin is demoted like its
399
+ * corrected base. The base carries the flag (a contradicted base takes a real
400
+ * ranking penalty); its near-duplicate `.derived` twin carries none and would
401
+ * otherwise outrank the corrected copy. Done in-memory at search time — NOT by
402
+ * writing the twin's frontmatter — because the SCC belief resolver refreshes any
403
+ * non-frozen state written to a derived memory back to `active` on the next
404
+ * improve run, erasing it. Only twins with no state of their own inherit; an
405
+ * explicit twin state always wins. Reuses the (03) belief-state ranker + filter.
406
+ */
407
+ function inheritDerivedTwinBeliefStates(db, items) {
408
+ const DEMOTING = new Set(["contradicted", "superseded", "deprecated", "archived"]);
409
+ const twins = items.filter((it) => it.entry.type === "memory" &&
410
+ it.entry.beliefState === undefined &&
411
+ it.entry.name.toLowerCase().endsWith(".derived"));
412
+ if (twins.length === 0)
413
+ return;
414
+ const baseBeliefByTwinId = getBaseBeliefStatesForDerivedTwins(db, twins.map((t) => t.id));
415
+ for (const t of twins) {
416
+ const baseBelief = baseBeliefByTwinId.get(t.id);
417
+ // Only inherit DEMOTIONS — never let a base's active/asserted state lift a twin.
418
+ if (baseBelief && DEMOTING.has(baseBelief)) {
419
+ t.entry.beliefState = baseBelief;
420
+ }
421
+ }
422
+ }
423
+ function matchBeliefFilter(beliefState, filter) {
390
424
  if (filter === "all")
391
425
  return true;
392
- if (type !== "memory")
393
- return true;
426
+ // 03: the belief filter applies to ANY flagged entry, not just memories, so
427
+ // `current`/`historical` filters catch contradicted/superseded KNOWLEDGE too.
428
+ // Unflagged entries (beliefState === undefined) still pass the `current` filter.
394
429
  if (filter === "current") {
395
430
  // Phase 1A: `asserted` is a "current" state (stronger authority than `active`);
396
431
  // `deprecated` is excluded from current results.
@@ -40,8 +40,10 @@ const DEFAULT_RECENCY_HALF_LIFE_DAYS = 30;
40
40
  const FEEDBACK_HALF_LIFE_CAP_MULTIPLIER = 4;
41
41
  function beliefStateBoost(item) {
42
42
  const entry = item.entry;
43
- if (entry.type !== "memory")
44
- return 0;
43
+ // 03: belief-state penalties/boosts apply to ANY flagged entry (memory OR
44
+ // knowledge), so contradicted/superseded KNOWLEDGE is demoted from results
45
+ // just like flagged memories. Entries without a belief state fall through to
46
+ // the `return 0` below (default-safe — no effect on unflagged assets).
45
47
  // Phase 1A: `asserted` and `deprecated` are first-class states.
46
48
  // `asserted` carries stronger user-explicit authority than `active`.
47
49
  // `deprecated` is a frozen historical state — penalized but milder than `superseded`.
@@ -87,14 +89,18 @@ const typeRankingContributor = {
87
89
  return TYPE_BOOST[item.entry.type] ?? 0;
88
90
  },
89
91
  };
90
- const memoryRankingContributor = {
91
- name: "memory-ranking",
92
+ const beliefStateRankingContributor = {
93
+ name: "belief-state-ranking",
92
94
  appliesTo(item) {
93
- return item.entry.type === "memory";
95
+ // Fire for any entry that carries a belief state, regardless of type so
96
+ // contradicted/superseded knowledge is demoted, not just memories. The
97
+ // `.derived`-twin `derivedBoost` (±0.12/−0.08) is deleted (03-R3): it made
98
+ // stale flag-free twins outrank their corrected base memory; belief-state
99
+ // demotion is the principled signal, not the twin-name heuristic.
100
+ return item.entry.beliefState !== undefined;
94
101
  },
95
102
  adjust(item) {
96
- const derivedBoost = item.entry.name.toLowerCase().endsWith(".derived") ? 0.12 : -0.08;
97
- return derivedBoost + beliefStateBoost(item);
103
+ return beliefStateBoost(item);
98
104
  },
99
105
  };
100
106
  const tagRankingContributor = {
@@ -330,7 +336,7 @@ const projectContextRankingContributor = {
330
336
  export const defaultRankingContributors = [
331
337
  exactNameRankingContributor,
332
338
  typeRankingContributor,
333
- memoryRankingContributor,
339
+ beliefStateRankingContributor,
334
340
  tagRankingContributor,
335
341
  searchHintRankingContributor,
336
342
  aliasRankingContributor,
@@ -167,14 +167,26 @@ export function getWritableStashDirs(overrideStashDir, existingConfig) {
167
167
  }
168
168
  /**
169
169
  * Find which source a file path belongs to.
170
+ *
171
+ * Longest-matching-prefix wins: a source nested inside another (e.g. `akm add
172
+ * ./sub` where `./sub` lives under the primary stash — which is always
173
+ * `sources[0]`) is the more specific owner and must win over the enclosing
174
+ * source regardless of array order. A first-match-in-order scan would
175
+ * misattribute every asset under the nested source to the primary stash,
176
+ * corrupting origin / editability / provenance decisions for the affected files.
170
177
  */
171
178
  export function findSourceForPath(filePath, sources) {
172
179
  const resolved = path.resolve(filePath);
180
+ let best;
181
+ let bestLen = -1;
173
182
  for (const source of sources) {
174
- if (resolved.startsWith(path.resolve(source.path) + path.sep))
175
- return source;
183
+ const base = path.resolve(source.path);
184
+ if (resolved.startsWith(base + path.sep) && base.length > bestLen) {
185
+ best = source;
186
+ bestLen = base.length;
187
+ }
176
188
  }
177
- return undefined;
189
+ return best;
178
190
  }
179
191
  /**
180
192
  * Return the primary stash source (first entry in the list).
@@ -1,7 +1,13 @@
1
1
  // This Source Code Form is subject to the terms of the Mozilla Public
2
2
  // License, v. 2.0. If a copy of the MPL was not distributed with this
3
3
  // file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
- const COMMON_PASSTHROUGH = ["HOME", "PATH", "USER", "LANG", "LC_ALL", "TERM", "TMPDIR"];
4
+ // AKM_EVENT_SOURCE carries usage-event provenance (improve/task) so that akm
5
+ // invocations a spawned agent makes are recorded as machine traffic, not user
6
+ // demand (DRIFT-6). Without it in the passthrough whitelist, buildChildEnv drops
7
+ // the stamp at the agent boundary — e.g. `akm wiki ingest` spawns an agent whose
8
+ // `akm curate/show/search` tool-calls then log source='user', silently inflating
9
+ // every lane's read-back (GRR). It is a provenance tag, never a secret.
10
+ const COMMON_PASSTHROUGH = ["HOME", "PATH", "USER", "LANG", "LC_ALL", "TERM", "TMPDIR", "AKM_EVENT_SOURCE"];
5
11
  /**
6
12
  * Built-in profiles for the five agent CLIs the v1 spec calls out
7
13
  * explicitly. The fields here are conservative defaults — every value is
@@ -22,12 +22,11 @@ const FEATURE_LOCATION = {
22
22
  graph_extraction: (cfg) => cfg.profiles?.improve?.default?.processes?.graphExtraction?.enabled ?? true,
23
23
  // Legacy default: false
24
24
  metadata_enhance: (cfg) => cfg.index?.metadataEnhance?.enabled ?? false,
25
- // Legacy default: false
26
- curate_rerank: (cfg) => cfg.search?.curateRerank?.enabled ?? false,
27
25
  // Default ON since R3 (docs/design/improve-self-learning-analysis.md G5):
28
- // distill is a primary acquisition path and the judge fails open (no LLM /
29
- // timeout / parse failure all pass through), so the gate only ever filters
30
- // when a judge verdict actually exists. Opt out via
26
+ // distill is a primary acquisition path, so the gate guards minted content by
27
+ // default. The judge fails CLOSED (07 P0-2): no LLM / timeout / parse failure
28
+ // reject the proposal rather than passing it through — an unjudgeable proposal
29
+ // must not slip into the stash. Opt out via
31
30
  // profiles.improve.default.processes.distill.qualityGate.enabled: false.
32
31
  lesson_quality_gate: (cfg) => cfg.profiles?.improve?.default?.processes?.distill?.qualityGate?.enabled ?? true,
33
32
  // Legacy default: false
@@ -112,9 +111,6 @@ export function isProcessEnabled(section, processName, config) {
112
111
  return isLlmFeatureEnabled(config, "graph_extraction");
113
112
  }
114
113
  }
115
- if (section === "search" && (processName === "curate_rerank" || processName === "curateRerank")) {
116
- return config.search?.curateRerank?.enabled ?? false;
117
- }
118
114
  if (section === "improve") {
119
115
  const processes = config.profiles?.improve?.default?.processes;
120
116
  const entry = processes?.[processName];
@@ -210,6 +210,10 @@ const agentMdRenderer = {
210
210
  action: "Dispatch using the prompt below verbatim. Use modelHint and toolPolicy if present.",
211
211
  description: asNonEmptyString(parsedMd.data.description),
212
212
  prompt: parsedMd.content,
213
+ // `tools` is self-declared frontmatter. The provenance CEILING that decides
214
+ // whether this self-declared policy is honoured is applied at the show
215
+ // layer (`akmShowUnified`), which knows whether the source is the operator's
216
+ // own writable stash vs a read-only third-party source (07 P1-D).
213
217
  toolPolicy: parsedMd.data.tools,
214
218
  modelHint: typeof parsedMd.data.model === "string" ? parsedMd.data.model : undefined,
215
219
  };