akm-cli 0.9.0-beta.4 → 0.9.0-beta.41

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 (132) hide show
  1. package/CHANGELOG.md +646 -0
  2. package/dist/assets/prompts/consolidate-system.md +23 -0
  3. package/dist/assets/prompts/contradiction-judge.md +33 -0
  4. package/dist/assets/prompts/distill-knowledge-system.md +22 -0
  5. package/dist/assets/prompts/distill-lesson-system.md +36 -0
  6. package/dist/assets/prompts/extract-session.md +6 -2
  7. package/dist/assets/prompts/graph-extract-system.md +1 -0
  8. package/dist/assets/prompts/memory-infer-system.md +1 -0
  9. package/dist/assets/prompts/memory-infer-user.md +5 -0
  10. package/dist/assets/prompts/metadata-enhance-system.md +1 -0
  11. package/dist/assets/prompts/procedural-system.md +44 -0
  12. package/dist/assets/prompts/recombine-system.md +40 -0
  13. package/dist/assets/prompts/staleness-detect-system.md +6 -0
  14. package/dist/assets/prompts/validate-summary-judge.md +1 -0
  15. package/dist/assets/stash-skeleton/facts/conventions/assets/agent.md +22 -0
  16. package/dist/assets/stash-skeleton/facts/conventions/assets/command.md +22 -0
  17. package/dist/assets/stash-skeleton/facts/conventions/assets/fact.md +24 -0
  18. package/dist/assets/stash-skeleton/facts/conventions/assets/knowledge.md +22 -0
  19. package/dist/assets/stash-skeleton/facts/conventions/assets/lesson.md +25 -0
  20. package/dist/assets/stash-skeleton/facts/conventions/assets/memory.md +21 -0
  21. package/dist/assets/stash-skeleton/facts/conventions/assets/script.md +21 -0
  22. package/dist/assets/stash-skeleton/facts/conventions/assets/skill.md +23 -0
  23. package/dist/assets/stash-skeleton/facts/conventions/assets/workflow.md +22 -0
  24. package/dist/assets/templates/html/health.html +281 -111
  25. package/dist/cli.js +14 -3
  26. package/dist/commands/agent/contribute-cli.js +16 -3
  27. package/dist/commands/feedback-cli.js +15 -6
  28. package/dist/commands/graph/graph.js +75 -71
  29. package/dist/commands/health/checks.js +48 -0
  30. package/dist/commands/health/html-report.js +422 -80
  31. package/dist/commands/health.js +381 -9
  32. package/dist/commands/improve/calibration.js +161 -0
  33. package/dist/commands/improve/consolidate.js +631 -111
  34. package/dist/commands/improve/dedup.js +482 -0
  35. package/dist/commands/improve/distill.js +163 -69
  36. package/dist/commands/improve/encoding-salience.js +205 -0
  37. package/dist/commands/improve/extract-cli.js +115 -1
  38. package/dist/commands/improve/extract-prompt.js +39 -2
  39. package/dist/commands/improve/extract-watch.js +140 -0
  40. package/dist/commands/improve/extract.js +403 -40
  41. package/dist/commands/improve/feedback-valence.js +54 -0
  42. package/dist/commands/improve/homeostatic.js +467 -0
  43. package/dist/commands/improve/improve-auto-accept.js +113 -6
  44. package/dist/commands/improve/improve-profiles.js +12 -0
  45. package/dist/commands/improve/improve.js +2042 -612
  46. package/dist/commands/improve/memory/memory-contradiction-detect.js +23 -28
  47. package/dist/commands/improve/outcome-loop.js +256 -0
  48. package/dist/commands/improve/proactive-maintenance.js +115 -0
  49. package/dist/commands/improve/procedural.js +418 -0
  50. package/dist/commands/improve/recombine.js +602 -0
  51. package/dist/commands/improve/reflect-noise.js +0 -0
  52. package/dist/commands/improve/reflect.js +46 -4
  53. package/dist/commands/improve/related-sessions.js +120 -0
  54. package/dist/commands/improve/salience.js +438 -0
  55. package/dist/commands/improve/triage.js +93 -0
  56. package/dist/commands/lint/agent-linter.js +19 -24
  57. package/dist/commands/lint/base-linter.js +173 -60
  58. package/dist/commands/lint/command-linter.js +19 -24
  59. package/dist/commands/lint/env-key-rules.js +34 -1
  60. package/dist/commands/lint/fact-linter.js +39 -0
  61. package/dist/commands/lint/index.js +31 -13
  62. package/dist/commands/lint/memory-linter.js +1 -1
  63. package/dist/commands/lint/registry.js +7 -2
  64. package/dist/commands/lint/task-linter.js +3 -3
  65. package/dist/commands/lint/workflow-linter.js +26 -1
  66. package/dist/commands/proposal/drain-policies.js +5 -0
  67. package/dist/commands/proposal/drain.js +17 -1
  68. package/dist/commands/proposal/proposal.js +5 -0
  69. package/dist/commands/proposal/propose.js +5 -0
  70. package/dist/commands/proposal/validators/proposal-quality-validators.js +9 -8
  71. package/dist/commands/proposal/validators/proposals.js +187 -57
  72. package/dist/commands/read/curate.js +344 -80
  73. package/dist/commands/read/search-cli.js +7 -0
  74. package/dist/commands/read/search.js +1 -0
  75. package/dist/commands/read/show.js +67 -2
  76. package/dist/commands/sources/init.js +36 -9
  77. package/dist/commands/sources/installed-stashes.js +5 -1
  78. package/dist/commands/sources/schema-repair.js +13 -1
  79. package/dist/commands/sources/stash-cli.js +19 -3
  80. package/dist/commands/sources/stash-skeleton.js +23 -8
  81. package/dist/core/asset/asset-registry.js +2 -0
  82. package/dist/core/asset/asset-spec.js +14 -0
  83. package/dist/core/asset/frontmatter.js +166 -167
  84. package/dist/core/asset/markdown.js +8 -0
  85. package/dist/core/authoring-rules.js +83 -0
  86. package/dist/core/config/config-schema.js +274 -2
  87. package/dist/core/config/config.js +2 -2
  88. package/dist/core/logs-db.js +4 -3
  89. package/dist/core/paths.js +3 -0
  90. package/dist/core/standards/resolve-standards-context.js +87 -0
  91. package/dist/core/standards/resolve-stash-standards.js +99 -0
  92. package/dist/core/standards/resolve-type-conventions.js +66 -0
  93. package/dist/core/state-db.js +691 -30
  94. package/dist/indexer/db/db.js +364 -38
  95. package/dist/indexer/db/graph-db.js +129 -86
  96. package/dist/indexer/ensure-index.js +152 -17
  97. package/dist/indexer/graph/graph-boost.js +51 -41
  98. package/dist/indexer/graph/graph-extraction.js +203 -3
  99. package/dist/indexer/index-writer-lock.js +99 -0
  100. package/dist/indexer/indexer.js +114 -111
  101. package/dist/indexer/passes/memory-inference.js +10 -3
  102. package/dist/indexer/passes/staleness-detect.js +2 -5
  103. package/dist/indexer/search/db-search.js +15 -4
  104. package/dist/indexer/search/ranking-contributors.js +22 -0
  105. package/dist/indexer/search/ranking.js +4 -0
  106. package/dist/indexer/walk/matchers.js +9 -0
  107. package/dist/integrations/agent/prompts.js +33 -0
  108. package/dist/integrations/harnesses/claude/session-log.js +11 -1
  109. package/dist/integrations/harnesses/opencode/session-log.js +173 -3
  110. package/dist/integrations/session-logs/index.js +16 -0
  111. package/dist/llm/client.js +23 -4
  112. package/dist/llm/embedder.js +27 -3
  113. package/dist/llm/embedders/local.js +66 -2
  114. package/dist/llm/feature-gate.js +8 -4
  115. package/dist/llm/graph-extract.js +2 -1
  116. package/dist/llm/memory-infer.js +4 -8
  117. package/dist/llm/metadata-enhance.js +9 -1
  118. package/dist/output/renderers.js +73 -1
  119. package/dist/output/shapes/curate.js +14 -2
  120. package/dist/output/text/helpers.js +16 -1
  121. package/dist/runtime.js +25 -1
  122. package/dist/scripts/migrate-storage.js +1378 -599
  123. package/dist/scripts/migrations/import-fs-improve-runs-to-db.js +479 -270
  124. package/dist/setup/setup.js +3 -3
  125. package/dist/sources/providers/git.js +71 -61
  126. package/dist/sources/providers/tar-utils.js +16 -8
  127. package/dist/storage/sqlite-pragmas.js +146 -0
  128. package/dist/wiki/wiki.js +37 -0
  129. package/dist/workflows/db.js +3 -4
  130. package/dist/workflows/validate-summary.js +2 -7
  131. package/docs/data-and-telemetry.md +1 -0
  132. package/package.json +8 -6
@@ -3,8 +3,26 @@
3
3
  // file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
4
  import { loadConfig } from "../../core/config/config.js";
5
5
  import { appendEvent } from "../../core/events.js";
6
+ import { getPhaseThreshold, openStateDatabase } from "../../core/state-db.js";
6
7
  import { info, warn } from "../../core/warn.js";
7
8
  import { promoteProposal, recordGateDecision } from "../proposal/validators/proposals.js";
9
+ /**
10
+ * Derive a stable, low-cardinality reason bucket from an auto-accept promotion
11
+ * error. `promoteProposal` throws a `validateProposal` report formatted as
12
+ * `[kind] message` lines; we extract the first finding kind. Non-validation
13
+ * throws collapse to `promote-error`.
14
+ */
15
+ function classifyPromoteFailure(err) {
16
+ const message = err instanceof Error ? err.message : String(err);
17
+ const finding = /\[([a-z][a-z0-9-]*)\]/i.exec(message);
18
+ if (finding)
19
+ return `validation:${finding[1]}`;
20
+ if (/not pending/i.test(message))
21
+ return "not-pending";
22
+ if (/unknown asset type/i.test(message))
23
+ return "unknown-type";
24
+ return "promote-error";
25
+ }
8
26
  // ---------------------------------------------------------------------------
9
27
  // Gate implementation
10
28
  // ---------------------------------------------------------------------------
@@ -18,13 +36,25 @@ import { promoteProposal, recordGateDecision } from "../proposal/validators/prop
18
36
  * @param promoteFn Injectable override for `promoteProposal` (test seam).
19
37
  */
20
38
  export async function runAutoAcceptGate(candidates, cfg, promoteFn = promoteProposal) {
21
- const result = { promoted: [], skipped: [], failed: [] };
39
+ const result = { promoted: [], skipped: [], failed: [], failedByReason: {} };
22
40
  // --- Guard: gate is disabled or context is incomplete ---
23
41
  if (cfg.dryRun || cfg.globalThreshold === undefined || !cfg.stashDir) {
24
42
  result.skipped = candidates.map((c) => c.proposalId);
25
43
  return result;
26
44
  }
27
- const effectiveThreshold = Math.max(cfg.globalThreshold, cfg.minimumThreshold ?? 0) / 100;
45
+ // WS-4: per-phase threshold from state.db overrides the global threshold.
46
+ // The per-phase value is populated by makeGateConfig when a stateDbPath is
47
+ // available; callers that don't pass it get the global threshold unchanged.
48
+ const resolvedThreshold = cfg.phaseThreshold ?? cfg.globalThreshold;
49
+ const effectiveThreshold = Math.max(resolvedThreshold, cfg.minimumThreshold ?? 0) / 100;
50
+ // WS-4: Exploration budget — promote at most N candidates regardless of
51
+ // confidence to prevent the gate converging to pure exploitation.
52
+ // Exploration candidates are chosen from the LOWEST-confidence eligible set
53
+ // (i.e. those that would be deferred) so the budget truly samples the low-
54
+ // confidence tail and is meaningfully distinct from normal auto-accept.
55
+ // Promoted exploration proposals are logged with eligibilitySource="exploration".
56
+ const explorationBudget = cfg.explorationBudgetCount ?? 0;
57
+ let explorationRemaining = explorationBudget;
28
58
  const resolvedConfig = typeof cfg.config === "function" ? cfg.config() : cfg.config;
29
59
  const gateLabel = `improve:${cfg.phase}`;
30
60
  // #577: stamp the gate's verdict onto each proposal so `akm proposal show`
@@ -40,7 +70,12 @@ export async function runAutoAcceptGate(candidates, cfg, promoteFn = promoteProp
40
70
  };
41
71
  for (const candidate of candidates) {
42
72
  const { proposalId, confidence } = candidate;
43
- if (confidence === undefined || confidence < effectiveThreshold) {
73
+ // Determine if this candidate is exploration-eligible: below-threshold
74
+ // (would normally be deferred) but with a valid confidence score and budget
75
+ // remaining. No-confidence candidates are never exploration-promoted.
76
+ const belowThreshold = confidence === undefined || confidence < effectiveThreshold;
77
+ const isExploration = belowThreshold && confidence !== undefined && explorationRemaining > 0;
78
+ if (belowThreshold && !isExploration) {
44
79
  stamp(proposalId, {
45
80
  outcome: "deferred",
46
81
  reason: confidence === undefined ? "no-confidence" : "below-threshold",
@@ -51,15 +86,25 @@ export async function runAutoAcceptGate(candidates, cfg, promoteFn = promoteProp
51
86
  result.skipped.push(proposalId);
52
87
  continue;
53
88
  }
89
+ // Either above-threshold (normal auto-accept) or exploration-budget promoted.
90
+ if (isExploration)
91
+ explorationRemaining -= 1;
92
+ const promoteReason = isExploration ? "exploration-budget" : "above-threshold";
54
93
  try {
55
94
  const promotion = await promoteFn(cfg.stashDir, resolvedConfig, proposalId, {}, undefined);
56
95
  stamp(promotion.proposal.id, {
57
96
  outcome: "auto-accepted",
58
- reason: "above-threshold",
97
+ reason: promoteReason,
59
98
  confidence,
60
99
  thresholds: { autoAccept: effectiveThreshold },
61
100
  gate: gateLabel,
62
101
  });
102
+ // Resolve the eligibilitySource: exploration-promoted proposals get
103
+ // eligibilitySource="exploration" (WS-4); normal auto-accepts carry
104
+ // whatever the proposal was tagged with at selection time.
105
+ const resolvedEligibilitySource = isExploration
106
+ ? "exploration"
107
+ : promotion.proposal.eligibilitySource;
63
108
  appendEvent({
64
109
  eventType: "promoted",
65
110
  ref: promotion.ref,
@@ -72,14 +117,41 @@ export async function runAutoAcceptGate(candidates, cfg, promoteFn = promoteProp
72
117
  confidence,
73
118
  threshold: effectiveThreshold,
74
119
  phase: cfg.phase,
120
+ // Attribution tagging: carry the eligibility lane from the proposal
121
+ // record onto the auto-accept promoted event so the lane survives to
122
+ // accept time even when promotion happens in a later run.
123
+ ...(resolvedEligibilitySource !== undefined ? { eligibilitySource: resolvedEligibilitySource } : {}),
124
+ // WS-4: mark exploration promotions so health/telemetry can
125
+ // distinguish them from calibration-signal promotions.
126
+ ...(isExploration ? { explorationBudget: true } : {}),
75
127
  },
76
128
  }, cfg.eventsCtx ?? {});
77
- info(`[improve] auto-accepted ${promotion.ref} (${cfg.phase}; confidence=${confidence.toFixed(2)} >= threshold=${effectiveThreshold.toFixed(2)})`);
129
+ if (isExploration) {
130
+ info(`[improve] exploration-accepted ${promotion.ref} (${cfg.phase}; confidence=${confidence.toFixed(2)}; budgetRemaining=${explorationRemaining})`);
131
+ }
132
+ else {
133
+ info(`[improve] auto-accepted ${promotion.ref} (${cfg.phase}; confidence=${confidence.toFixed(2)} >= threshold=${effectiveThreshold.toFixed(2)})`);
134
+ }
78
135
  result.promoted.push(proposalId);
79
136
  }
80
137
  catch (err) {
81
- warn(`[improve] ${cfg.phase} auto-accept failed for ${proposalId}: ${err instanceof Error ? err.message : String(err)}`);
138
+ const reason = classifyPromoteFailure(err);
139
+ warn(`[improve] ${cfg.phase} auto-accept failed for ${proposalId} (${reason}): ${err instanceof Error ? err.message : String(err)}`);
82
140
  result.failed.push(proposalId);
141
+ result.failedByReason[reason] = (result.failedByReason[reason] ?? 0) + 1;
142
+ // Record WHY on the proposal so `akm proposal show` explains the rejection
143
+ // and the leak is no longer blind. Best-effort.
144
+ stamp(proposalId, {
145
+ outcome: "auto-rejected",
146
+ reason,
147
+ confidence,
148
+ thresholds: { autoAccept: effectiveThreshold },
149
+ gate: gateLabel,
150
+ });
151
+ // If exploration budget was consumed but promotion failed, restore the slot
152
+ // so the budget isn't exhausted on errors.
153
+ if (isExploration)
154
+ explorationRemaining += 1;
83
155
  }
84
156
  }
85
157
  return result;
@@ -108,15 +180,50 @@ export function resolveExtractConfidence(proposal) {
108
180
  /**
109
181
  * Build a gate config for a phase, inheriting global settings from the
110
182
  * improve options. Callers supply only the phase-specific overrides.
183
+ *
184
+ * WS-4 additions:
185
+ * - When `shared.stateDbPath` is provided, reads the persisted per-phase
186
+ * threshold from `improve_gate_thresholds` (Migration 012). The phase
187
+ * value overrides `globalThreshold` but is still floored by
188
+ * `minimumThreshold`. Falls back to `globalThreshold` when no row exists.
189
+ * - Computes `explorationBudgetCount` from
190
+ * `config.improve.exploration.budgetFraction × candidateCount` when the
191
+ * exploration budget is enabled. Defaults to 0 (no exploration).
111
192
  */
112
193
  export function makeGateConfig(phase, shared, overrides = {}) {
194
+ // WS-4: read per-phase threshold from state.db when available.
195
+ let phaseThreshold;
196
+ if (shared.stateDbPath && shared.globalThreshold !== undefined) {
197
+ try {
198
+ const db = openStateDatabase(shared.stateDbPath);
199
+ try {
200
+ phaseThreshold = getPhaseThreshold(db, phase) ?? undefined;
201
+ }
202
+ finally {
203
+ db.close();
204
+ }
205
+ }
206
+ catch {
207
+ // DB unavailable — fall back to globalThreshold silently.
208
+ }
209
+ }
210
+ // WS-4: compute exploration budget count from config fraction × candidateCount.
211
+ let explorationBudgetCount;
212
+ const resolvedConfig = typeof shared.config === "function" ? shared.config() : shared.config;
213
+ const explorationCfg = resolvedConfig.improve?.exploration;
214
+ if (explorationCfg?.enabled && shared.candidateCount !== undefined && shared.candidateCount > 0) {
215
+ const fraction = Math.min(1, Math.max(0, explorationCfg.budgetFraction ?? 0.05));
216
+ explorationBudgetCount = Math.max(0, Math.floor(fraction * shared.candidateCount));
217
+ }
113
218
  return {
114
219
  phase,
115
220
  globalThreshold: shared.globalThreshold,
221
+ ...(phaseThreshold !== undefined ? { phaseThreshold } : {}),
116
222
  dryRun: shared.dryRun,
117
223
  stashDir: shared.stashDir,
118
224
  config: shared.config,
119
225
  eventsCtx: shared.eventsCtx,
226
+ ...(explorationBudgetCount !== undefined && explorationBudgetCount > 0 ? { explorationBudgetCount } : {}),
120
227
  ...overrides,
121
228
  };
122
229
  }
@@ -54,6 +54,18 @@ const IMPROVE_PROCESS_DEFAULTS = {
54
54
  // proposal-queue triage drains the standing backlog. Opt-in (default off),
55
55
  // like `validation` — needs an explicit `enabled: true`.
56
56
  triage: false,
57
+ // Layer 2 proactive-maintenance selector. Opt-in (default off) — surfaces
58
+ // stale high-value assets on a schedule. Enable per-profile with an explicit
59
+ // `processes.proactiveMaintenance.enabled: true`.
60
+ proactiveMaintenance: false,
61
+ // #609 recombine / synthesize pass — whole-corpus cross-episodic
62
+ // generalization. Opt-in (default off); enable per-profile with an explicit
63
+ // `processes.recombine.enabled: true`.
64
+ recombine: false,
65
+ // #615 procedural-compilation pass — detects recurring successful ordered
66
+ // action sequences and compiles them into workflow proposals. Opt-in (default
67
+ // off); enable per-profile with an explicit `processes.procedural.enabled: true`.
68
+ procedural: false,
57
69
  };
58
70
  /**
59
71
  * Compute the effective enabled-state for a named improve process.