atris 3.57.4 → 3.58.6

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 (175) hide show
  1. package/README.md +12 -1
  2. package/atris/skills/clawhub/member-runtime/SKILL.md +9 -9
  3. package/atris/skills/create-member/SKILL.md +16 -13
  4. package/atris/skills/design/SKILL.md +27 -4
  5. package/atris/skills/engines/SKILL.md +55 -12
  6. package/atris/skills/x-search/SKILL.md +8 -6
  7. package/atris/skills/youtube/SKILL.md +66 -21
  8. package/atris/team/brainstormer/MEMBER.md +2 -2
  9. package/atris/team/executor/MEMBER.md +3 -3
  10. package/atris/team/launcher/MEMBER.md +4 -4
  11. package/atris/team/navigator/MEMBER.md +3 -3
  12. package/atris/team/researcher/MEMBER.md +2 -2
  13. package/atris/team/validator/MEMBER.md +1 -1
  14. package/atris.md +14 -0
  15. package/ax +153 -23
  16. package/bin/atris.js +78 -30
  17. package/commands/aeo.js +4 -4
  18. package/commands/agents.js +1 -1
  19. package/commands/align.js +12 -12
  20. package/commands/analytics.js +3 -3
  21. package/commands/app.js +5 -5
  22. package/commands/ask.js +175 -0
  23. package/commands/auth.js +59 -31
  24. package/commands/autoland.js +29 -5
  25. package/commands/autopilot-front.js +6 -6
  26. package/commands/autopilot.js +90 -90
  27. package/commands/avail.js +5 -5
  28. package/commands/bench.js +63 -1
  29. package/commands/browse.js +2 -2
  30. package/commands/business.js +12 -12
  31. package/commands/card.js +2 -2
  32. package/commands/clean.js +13 -13
  33. package/commands/close.js +3 -3
  34. package/commands/compile.js +13 -13
  35. package/commands/computer.js +94 -27
  36. package/commands/console.js +5 -5
  37. package/commands/decide.js +1 -1
  38. package/commands/deck.js +9 -9
  39. package/commands/drive.js +4 -4
  40. package/commands/engine.js +173 -19
  41. package/commands/errors.js +3 -3
  42. package/commands/experiments.js +18 -1
  43. package/commands/feed.js +2 -2
  44. package/commands/feedback.js +4 -4
  45. package/commands/fleet.js +12 -12
  46. package/commands/game.js +1 -1
  47. package/commands/guide.js +49 -0
  48. package/commands/improve.js +19 -17
  49. package/commands/init.js +67 -19
  50. package/commands/interview.js +7 -7
  51. package/commands/land.js +40 -15
  52. package/commands/learn.js +216 -50
  53. package/commands/lesson.js +2 -2
  54. package/commands/log.js +1 -1
  55. package/commands/loops.js +4 -4
  56. package/commands/member.js +75 -50
  57. package/commands/mission.js +90 -82
  58. package/commands/one-lap.js +1 -1
  59. package/commands/pack.js +74 -8
  60. package/commands/plugin.js +6 -6
  61. package/commands/probe.js +9 -9
  62. package/commands/pull.js +35 -35
  63. package/commands/pulse.js +1 -1
  64. package/commands/push.js +21 -21
  65. package/commands/rainmaker.js +1 -1
  66. package/commands/recap.js +7 -7
  67. package/commands/reel.js +2 -2
  68. package/commands/release.js +3 -3
  69. package/commands/report.js +1 -1
  70. package/commands/review.js +5 -5
  71. package/commands/revisions.js +1 -1
  72. package/commands/run.js +1 -1
  73. package/commands/scout.js +3 -3
  74. package/commands/security-review.js +7 -7
  75. package/commands/serve.js +9 -9
  76. package/commands/setup.js +1 -1
  77. package/commands/sign.js +4 -4
  78. package/commands/signup.js +4 -4
  79. package/commands/site.js +8 -3
  80. package/commands/skill.js +6 -6
  81. package/commands/slop.js +18 -18
  82. package/commands/social.js +3 -3
  83. package/commands/soul.js +14 -14
  84. package/commands/spaceship.js +1 -1
  85. package/commands/status.js +2 -2
  86. package/commands/strings.js +13 -13
  87. package/commands/study.js +4 -4
  88. package/commands/sync.js +8 -8
  89. package/commands/task.js +50 -25
  90. package/commands/teach.js +2 -2
  91. package/commands/team.js +1 -1
  92. package/commands/theme.js +2 -2
  93. package/commands/tree.js +34 -0
  94. package/commands/truth.js +7 -7
  95. package/commands/verify.js +12 -12
  96. package/commands/visualize.js +2 -2
  97. package/commands/workflow.js +101 -48
  98. package/commands/worktree.js +1 -1
  99. package/commands/write.js +9 -9
  100. package/commands/x-search.js +93 -22
  101. package/commands/xp.js +6 -3
  102. package/commands/youtube.js +1019 -123
  103. package/lib/accept-verify-gate.js +3 -3
  104. package/lib/activity-stream.js +1 -1
  105. package/lib/auto-accept-certified.js +28 -12
  106. package/lib/autoland.js +6 -6
  107. package/lib/bench/engines.js +59 -8
  108. package/lib/bench/report.js +1 -1
  109. package/lib/bench/runner.js +204 -5
  110. package/lib/bench/tree-render.js +76 -0
  111. package/lib/card.js +1 -1
  112. package/lib/chat-log-scan.js +1 -1
  113. package/lib/claude-boot-block.js +2 -0
  114. package/lib/deck-compose.js +1 -1
  115. package/lib/deck-history.js +2 -2
  116. package/lib/deck-layout.js +1 -1
  117. package/lib/deck-review.js +5 -5
  118. package/lib/deck-schema.js +1 -1
  119. package/lib/default-verifier.js +1 -1
  120. package/lib/developer-api.js +3 -0
  121. package/lib/engine-ask.js +45 -7
  122. package/lib/engine-registry.js +15 -2
  123. package/lib/file-ops.js +1 -1
  124. package/lib/fleet.js +71 -35
  125. package/lib/html-render.js +1 -1
  126. package/lib/intents.js +172 -0
  127. package/lib/journal.js +1 -1
  128. package/lib/known-commands.js +2 -2
  129. package/lib/land-green.js +364 -0
  130. package/lib/lesson-contradiction.js +2 -2
  131. package/lib/lesson-preflight.js +1 -1
  132. package/lib/manifest.js +5 -5
  133. package/lib/member-alive.js +9 -5
  134. package/lib/memory-view.js +3 -3
  135. package/lib/mission-protected-lane.js +1 -1
  136. package/lib/mission-root.js +2 -2
  137. package/lib/orb-context.js +10 -10
  138. package/lib/pack-capabilities.js +861 -3
  139. package/lib/permission-grants.js +2 -2
  140. package/lib/policy-lessons.js +6 -6
  141. package/lib/pulse.js +8 -8
  142. package/lib/receipt-evidence.js +1 -1
  143. package/lib/reel.js +1 -1
  144. package/lib/review-integrity.js +1 -1
  145. package/lib/runner-command.js +35 -5
  146. package/lib/scorecard.js +4 -4
  147. package/lib/security-scan.js +5 -5
  148. package/lib/self-drive.js +1 -1
  149. package/lib/site-publish.js +372 -0
  150. package/lib/site.js +1 -1
  151. package/lib/slides-deck.js +2 -2
  152. package/lib/state-detection.js +2 -2
  153. package/lib/sync-telemetry.js +2 -2
  154. package/lib/task-db.js +17 -10
  155. package/lib/task-explanation.js +1 -1
  156. package/lib/task-proof.js +2 -2
  157. package/lib/task-receipt.js +3 -3
  158. package/lib/theme.js +12 -12
  159. package/lib/todo-fallback.js +3 -3
  160. package/lib/todo-sections.js +1 -1
  161. package/lib/tree-hash.js +79 -0
  162. package/lib/usage.js +1 -1
  163. package/lib/wish-delegate.js +1 -1
  164. package/lib/workspace-safety.js +4 -4
  165. package/lib/workspace-scaffold.js +3 -3
  166. package/package.json +1 -1
  167. package/scripts/det/concierge-bakeoff.sh +26 -0
  168. package/scripts/det/ytnotes +82 -5
  169. package/scripts/det/ytquote-repair.js +0 -1
  170. package/scripts/det/ytrail-race.js +0 -5
  171. package/scripts/member-operate.mjs +52 -14
  172. package/scripts/outbound-artifact-gate.js +0 -3
  173. package/utils/auth.js +47 -9
  174. package/utils/config.js +0 -1
  175. package/scripts/det/checklist-score.js +0 -191
package/commands/learn.js CHANGED
@@ -12,6 +12,17 @@ const {
12
12
  getStats,
13
13
  exportMarkdown,
14
14
  } = require('../lib/learnings');
15
+ const applyGate = require('../lib/apply-gate');
16
+ const {
17
+ fileTeachExperiment,
18
+ extractTeachNumbers,
19
+ extractTeachMechanisms,
20
+ isThinTeachLesson,
21
+ printLearnerCheckGate,
22
+ proveSavedLearnerBaseline,
23
+ } = require('./youtube');
24
+
25
+ const KEEP_RULE = 'keep only if measure.py moves 0→1. scores 1 only when the fixture contains the check tokens.';
15
26
 
16
27
  function showRecent(limit = 20) {
17
28
  const learnings = loadLearnings()
@@ -36,7 +47,7 @@ function showRecent(limit = 20) {
36
47
 
37
48
  console.log('');
38
49
  console.log('┌─────────────────────────────────────────────────────────────┐');
39
- console.log(`│ Learnings ${learnings.length} active${' '.repeat(Math.max(0, 44 - String(learnings.length).length))}│`);
50
+ console.log(`│ Learnings: ${learnings.length} active${' '.repeat(Math.max(0, 44 - String(learnings.length).length))}│`);
40
51
  console.log('└─────────────────────────────────────────────────────────────┘');
41
52
  console.log('');
42
53
 
@@ -46,7 +57,7 @@ function showRecent(limit = 20) {
46
57
  const conf = e._effectiveConfidence;
47
58
  const bar = conf >= 7 ? '●' : conf >= 4 ? '◐' : '○';
48
59
  const date = (e.ts || '').split('T')[0];
49
- console.log(` ${bar} [${conf}/10] ${e.key} ${e.insight}`);
60
+ console.log(` ${bar} [${conf}/10] ${e.key}: ${e.insight}`);
50
61
  if (e.files && e.files.length > 0) {
51
62
  console.log(` files: ${e.files.join(', ')}`);
52
63
  }
@@ -68,12 +79,12 @@ function showSearch(query) {
68
79
  }
69
80
 
70
81
  console.log('');
71
- console.log(` Search: "${query}" ${results.length} ${results.length === 1 ? 'result' : 'results'}`);
82
+ console.log(` Search: "${query}", ${results.length} ${results.length === 1 ? 'result' : 'results'}`);
72
83
  console.log('');
73
84
  for (const e of results) {
74
85
  const conf = e._effectiveConfidence;
75
86
  const bar = conf >= 7 ? '●' : conf >= 4 ? '◐' : '○';
76
- console.log(` ${bar} [${conf}/10] ${e.type}/${e.key} ${e.insight}`);
87
+ console.log(` ${bar} [${conf}/10] ${e.type}/${e.key}: ${e.insight}`);
77
88
  }
78
89
  console.log('');
79
90
  }
@@ -114,7 +125,7 @@ function showExport() {
114
125
  const md = exportMarkdown();
115
126
  console.log('');
116
127
  console.log(md);
117
- console.log(' Copy the above into CLAUDE.md or save to a file.');
128
+ console.log(' Copy the above into CLAUDE.md or save to a file.');
118
129
  console.log('');
119
130
  }
120
131
 
@@ -130,15 +141,15 @@ function showPrune() {
130
141
 
131
142
  console.log('');
132
143
  if (stale.length > 0) {
133
- console.log(` STALE (${stale.length} referenced files deleted):`);
144
+ console.log(` STALE (${stale.length}, referenced files deleted):`);
134
145
  for (const { entry, missingFiles } of stale) {
135
- console.log(` ⚠ ${entry.key} missing: ${missingFiles.join(', ')}`);
146
+ console.log(` ⚠ ${entry.key}, missing: ${missingFiles.join(', ')}`);
136
147
  }
137
148
  console.log('');
138
149
  }
139
150
 
140
151
  if (contradictions.length > 0) {
141
- console.log(` CONFLICTS (${contradictions.length} same key, different insight):`);
152
+ console.log(` CONFLICTS (${contradictions.length}, same key, different insight):`);
142
153
  for (const { a, b } of contradictions) {
143
154
  console.log(` ⚠ ${a.key}: "${a.insight}" vs "${b.insight}"`);
144
155
  }
@@ -149,7 +160,52 @@ function showPrune() {
149
160
  console.log('');
150
161
  }
151
162
 
152
- function interactiveAdd() {
163
+ function commitAddedLearning({ type, key, insight, confidence, source, files } = {}, deps = {}) {
164
+ const print = typeof deps.output === 'function' ? deps.output : (line = '') => console.log(line);
165
+ const cwd = deps.cwd || process.cwd();
166
+ const entry = addLearning({ type, key, insight, confidence, source, files });
167
+ print('');
168
+ print(` ✓ Saved: [${entry.confidence}/10] ${entry.type}/${entry.key}`);
169
+ print(` "${entry.insight}"`);
170
+ print('');
171
+ const baseline = mintRichLearn({
172
+ cwd,
173
+ key: entry.key,
174
+ insight: entry.insight,
175
+ now: deps.now,
176
+ output: print,
177
+ });
178
+ return { entry, baseline };
179
+ }
180
+
181
+ function reviewLearningType(insight) {
182
+ return /^(don't|never|avoid|watch out|careful)/i.test(String(insight || '')) ? 'pitfall' : 'pattern';
183
+ }
184
+
185
+ function reviewLearningKey(insight) {
186
+ return String(insight || '').toLowerCase().replace(/[^a-z0-9\s]/g, '').split(/\s+/).slice(0, 4).join('-');
187
+ }
188
+
189
+ function commitReviewLearning(insight, deps = {}) {
190
+ const print = typeof deps.output === 'function' ? deps.output : (line = '') => console.log(line);
191
+ const cwd = deps.cwd || process.cwd();
192
+ const text = String(insight || '').trim();
193
+ if (!text) return { entry: null, baseline: 0 };
194
+ const type = reviewLearningType(text);
195
+ const key = reviewLearningKey(text);
196
+ const entry = addLearning({ type, key, insight: text, confidence: 7, source: 'review', files: [] });
197
+ print(`✓ Saved to learnings: [7/10] ${entry.type}/${entry.key}`);
198
+ const baseline = mintRichLearn({
199
+ cwd,
200
+ key: entry.key,
201
+ insight: entry.insight,
202
+ now: deps.now,
203
+ output: print,
204
+ });
205
+ return { entry, baseline };
206
+ }
207
+
208
+ function interactiveAdd(deps = {}) {
153
209
  const rl = readline.createInterface({
154
210
  input: process.stdin,
155
211
  output: process.stdout,
@@ -209,16 +265,16 @@ function interactiveAdd() {
209
265
  // Quality gate
210
266
  const worth = (await ask('\n Would this save time in a future session? (y/n): ')).trim().toLowerCase();
211
267
  if (worth !== 'y' && worth !== 'yes') {
212
- console.log(' Skipped only save learnings that compound.');
268
+ console.log(' Skipped, only save learnings that compound.');
213
269
  rl.close();
214
270
  return;
215
271
  }
216
272
 
217
- const entry = addLearning({ type, key, insight, confidence, source, files });
218
- console.log('');
219
- console.log(` ✓ Saved: [${entry.confidence}/10] ${entry.type}/${entry.key}`);
220
- console.log(` "${entry.insight}"`);
221
- console.log('');
273
+ commitAddedLearning({ type, key, insight, confidence, source, files }, {
274
+ cwd: deps.cwd || process.cwd(),
275
+ now: deps.now,
276
+ output: deps.output,
277
+ });
222
278
  rl.close();
223
279
  } catch (err) {
224
280
  console.log(` ✗ Error: ${err.message}`);
@@ -239,24 +295,104 @@ function printLearnLogSchema(stream = console.error) {
239
295
  for (const line of learnLogSchemaLines()) stream(` ${line}`);
240
296
  }
241
297
 
298
+ function learnExperimentSlug(key) {
299
+ return `learn-${applyGate.applySlug(key)}`;
300
+ }
301
+
302
+ function learnExperimentRel(key) {
303
+ return `atris/experiments/${learnExperimentSlug(key)}`;
304
+ }
305
+
306
+ function learnApplyRel(key) {
307
+ return applyGate.applySidecarRel('learn', applyGate.applySlug(key));
308
+ }
309
+
310
+ function learnLessonFromText(text) {
311
+ const body = String(text || '');
312
+ return {
313
+ numbers: extractTeachNumbers(body),
314
+ mechanisms: extractTeachMechanisms(body),
315
+ };
316
+ }
317
+
318
+ function saveRichLearn({ cwd, key, insight } = {}) {
319
+ const lesson = learnLessonFromText(insight);
320
+ if (isThinTeachLesson(lesson)) {
321
+ return { thin: true, packRel: null, lesson };
322
+ }
323
+ if (cwd) fs.mkdirSync(path.join(cwd, 'atris', 'wiki'), { recursive: true });
324
+ const packRel = fileTeachExperiment({
325
+ cwd,
326
+ lesson,
327
+ slug: key ? learnExperimentSlug(key) : null,
328
+ applyRel: key ? learnApplyRel(key) : null,
329
+ });
330
+ return { thin: false, packRel, lesson };
331
+ }
332
+
333
+ function ensureLearnApply({ cwd, key, packRel, now, output } = {}) {
334
+ const pack = packRel || (key ? learnExperimentRel(key) : null);
335
+ const slug = pack ? path.basename(pack) : null;
336
+ return applyGate.ensureApply({
337
+ cwd,
338
+ source: key ? `learn:${key}` : 'learn',
339
+ rel: key ? learnApplyRel(key) : null,
340
+ now,
341
+ output,
342
+ incompleteMessage: slug
343
+ ? `next: atris experiments keep ${slug}`
344
+ : applyGate.ephemeralApplyMessage('learning'),
345
+ required: false,
346
+ change: pack ? `apply ${pack}` : undefined,
347
+ receipt: pack ? KEEP_RULE : undefined,
348
+ journalLine: pack ? `- [claimable] apply: ${pack}. ${KEEP_RULE}` : undefined,
349
+ });
350
+ }
351
+
352
+ function mintRichLearn({ cwd, key, insight, now, output } = {}) {
353
+ const print = typeof output === 'function' ? output : (line = '') => console.log(line);
354
+ const saved = saveRichLearn({ cwd, key, insight });
355
+ if (saved.thin) {
356
+ printLearnerCheckGate(print, saved.lesson, { includeCheck: true });
357
+ return 0;
358
+ }
359
+ ensureLearnApply({
360
+ cwd,
361
+ key,
362
+ packRel: saved.packRel,
363
+ now,
364
+ output: print,
365
+ });
366
+ return proveSavedLearnerBaseline({
367
+ cwd,
368
+ applyRel: key ? learnApplyRel(key) : null,
369
+ lesson: saved.lesson,
370
+ output: print,
371
+ });
372
+ }
373
+
242
374
  /**
243
375
  * Non-interactive log: `atris learn log '{"type":"pattern","key":"...","insight":"...","confidence":8,"source":"observed"}'`
244
- * For agents and scripts no prompts, no quality gate.
376
+ * For agents and scripts, no prompts, no quality gate.
245
377
  * Aliases: title→key, detail→insight.
246
378
  */
247
- function logDirect(jsonStr) {
379
+ function logDirect(jsonStr, deps = {}) {
380
+ const error = typeof deps.error === 'function' ? deps.error : (line = '') => console.error(line);
381
+ const print = typeof deps.output === 'function' ? deps.output : (line = '') => console.log(line);
382
+ const exit = typeof deps.exit === 'function' ? deps.exit : (code) => process.exit(code);
383
+ const cwd = deps.cwd || process.cwd();
248
384
  if (!jsonStr) {
249
- console.error(' ✗ Usage: atris learn log \'<json>\'');
250
- printLearnLogSchema();
251
- process.exit(1);
385
+ error(' ✗ Usage: atris learn log \'<json>\'');
386
+ printLearnLogSchema(error);
387
+ return exit(1);
252
388
  }
253
389
  let data;
254
390
  try {
255
391
  data = JSON.parse(jsonStr);
256
392
  } catch (err) {
257
- console.error(` ✗ Invalid JSON: ${err.message}`);
258
- printLearnLogSchema();
259
- process.exit(1);
393
+ error(` ✗ Invalid JSON: ${err.message}`);
394
+ printLearnLogSchema(error);
395
+ return exit(1);
260
396
  }
261
397
  try {
262
398
  const entry = addLearning({
@@ -267,11 +403,20 @@ function logDirect(jsonStr) {
267
403
  source: data.source || 'observed',
268
404
  files: data.files || [],
269
405
  });
270
- console.log(` ✓ [${entry.confidence}/10] ${entry.type}/${entry.key}`);
406
+ print(` ✓ [${entry.confidence}/10] ${entry.type}/${entry.key}`);
407
+ const baseline = mintRichLearn({
408
+ cwd,
409
+ key: entry.key,
410
+ insight: entry.insight,
411
+ now: deps.now,
412
+ output: print,
413
+ });
414
+ if (baseline !== 0) return exit(baseline);
415
+ return 0;
271
416
  } catch (err) {
272
- console.error(` ✗ ${err.message}`);
273
- printLearnLogSchema();
274
- process.exit(1);
417
+ error(` ✗ ${err.message}`);
418
+ printLearnLogSchema(error);
419
+ return exit(1);
275
420
  }
276
421
  }
277
422
 
@@ -279,12 +424,14 @@ function logDirect(jsonStr) {
279
424
  * Harvest learnings from journal Notes sections.
280
425
  * Scans recent journals for lines that look like insights.
281
426
  */
282
- function harvestFromJournals() {
283
- const atrisDir = path.join(process.cwd(), 'atris');
427
+ function harvestFromJournals(deps = {}) {
428
+ const print = typeof deps.output === 'function' ? deps.output : (line = '') => console.log(line);
429
+ const cwd = deps.cwd || process.cwd();
430
+ const atrisDir = path.join(cwd, 'atris');
284
431
  const logsDir = path.join(atrisDir, 'logs');
285
432
 
286
433
  if (!fs.existsSync(logsDir)) {
287
- console.log(' No journals found.');
434
+ print(' No journals found.');
288
435
  return;
289
436
  }
290
437
 
@@ -309,7 +456,7 @@ function harvestFromJournals() {
309
456
  const lines = notesMatch[1].trim().split('\n').filter(l => l.startsWith('- '));
310
457
  for (const line of lines) {
311
458
  // Strip bullet and optional timestamp prefix
312
- const insight = line.replace(/^- (\d{2}:\d{2} )?/, '').trim();
459
+ const insight = line.replace(/^- (\d{2}:\d{2} \u2014 )?/, '').trim();
313
460
  if (insight.length > 10) {
314
461
  candidates.push({ insight, source: path.basename(logPath) });
315
462
  }
@@ -318,10 +465,10 @@ function harvestFromJournals() {
318
465
  }
319
466
 
320
467
  if (candidates.length === 0) {
321
- console.log('');
322
- console.log(' No harvestable notes found in recent journals.');
323
- console.log(' Add notes during "atris review" or write to ## Notes in your journal.');
324
- console.log('');
468
+ print('');
469
+ print(' No harvestable notes found in recent journals.');
470
+ print(' Add notes during "atris review" or write to ## Notes in your journal.');
471
+ print('');
325
472
  return;
326
473
  }
327
474
 
@@ -331,31 +478,38 @@ function harvestFromJournals() {
331
478
  const fresh = candidates.filter(c => !existingInsights.has(c.insight.toLowerCase()));
332
479
 
333
480
  if (fresh.length === 0) {
334
- console.log('');
335
- console.log(` Scanned ${candidates.length} journal notes all already captured.`);
336
- console.log('');
481
+ print('');
482
+ print(` Scanned ${candidates.length} journal notes, all already captured.`);
483
+ print('');
337
484
  return;
338
485
  }
339
486
 
340
- console.log('');
341
- console.log(` Found ${fresh.length} new ${fresh.length === 1 ? 'note' : 'notes'} to harvest:`);
342
- console.log('');
487
+ print('');
488
+ print(` Found ${fresh.length} new ${fresh.length === 1 ? 'note' : 'notes'} to harvest:`);
489
+ print('');
343
490
  for (let i = 0; i < fresh.length; i++) {
344
491
  const c = fresh[i];
345
492
  const isPitfall = /^(don't|never|avoid|watch out|careful)/i.test(c.insight);
346
493
  const type = isPitfall ? 'pitfall' : 'pattern';
347
494
  const key = c.insight.toLowerCase().replace(/[^a-z0-9\s]/g, '').split(/\s+/).slice(0, 4).join('-');
348
- console.log(` ${i + 1}. [${type}] ${c.insight}`);
349
- console.log(` from: ${c.source}`);
495
+ print(` ${i + 1}. [${type}] ${c.insight}`);
496
+ print(` from: ${c.source}`);
350
497
 
351
498
  try {
352
- addLearning({ type, key, insight: c.insight, confidence: 6, source: 'review', files: [] });
353
- console.log(` ✓ saved [6/10]`);
499
+ const entry = addLearning({ type, key, insight: c.insight, confidence: 6, source: 'review', files: [] });
500
+ print(` ✓ saved [6/10]`);
501
+ mintRichLearn({
502
+ cwd,
503
+ key: entry.key,
504
+ insight: entry.insight,
505
+ now: deps.now,
506
+ output: print,
507
+ });
354
508
  } catch (err) {
355
- console.log(` ✗ ${err.message}`);
509
+ print(` ✗ ${err.message}`);
356
510
  }
357
511
  }
358
- console.log('');
512
+ print('');
359
513
  }
360
514
 
361
515
  /**
@@ -375,10 +529,10 @@ function showLearnHelp() {
375
529
  console.log('');
376
530
  console.log(' Commands:');
377
531
  console.log(' (none) Show recent learnings');
378
- console.log(' add Add a learning interactively');
379
- console.log(' log <json> Add programmatically (for agents)');
532
+ console.log(' add Add a learning interactively. A rich insight mints one apply plus a failing measure.py. A thin insight prints check: fill this.');
533
+ console.log(' log <json> Add programmatically (for agents). A rich insight (number or named mechanism) mints one apply plus a failing measure.py. A thin insight prints check: fill this.');
380
534
  console.log(' search <q> Search learnings by keyword');
381
- console.log(' harvest Extract learnings from journal Notes');
535
+ console.log(' harvest Extract learnings from journal Notes. A rich insight mints one apply plus a failing measure.py. A thin insight prints check: fill this.');
382
536
  console.log(' prune Check for stale/contradictory entries');
383
537
  console.log(' stats Show learning statistics');
384
538
  console.log(' export Export as markdown');
@@ -437,5 +591,17 @@ function learnAtris(subcommand, ...args) {
437
591
  }
438
592
 
439
593
  learnAtris.getLearningCount = getLearningCount;
594
+ learnAtris.learnExperimentSlug = learnExperimentSlug;
595
+ learnAtris.learnExperimentRel = learnExperimentRel;
596
+ learnAtris.learnApplyRel = learnApplyRel;
597
+ learnAtris.learnLessonFromText = learnLessonFromText;
598
+ learnAtris.saveRichLearn = saveRichLearn;
599
+ learnAtris.ensureLearnApply = ensureLearnApply;
600
+ learnAtris.mintRichLearn = mintRichLearn;
601
+ learnAtris.commitAddedLearning = commitAddedLearning;
602
+ learnAtris.commitReviewLearning = commitReviewLearning;
603
+ learnAtris.reviewLearningKey = reviewLearningKey;
604
+ learnAtris.harvestFromJournals = harvestFromJournals;
605
+ learnAtris.logDirect = logDirect;
440
606
 
441
607
  module.exports = learnAtris;
@@ -26,7 +26,7 @@ function tagLessonResolvedInMd(cwd, slug) {
26
26
  if (!m || m[1] !== slug) continue;
27
27
  if (/\[resolved\]/i.test(lines[i])) continue; // already tagged
28
28
  lines[i] = lines[i].replace(
29
- /(\*\*\[\d{4}-\d{2}-\d{2}\]\s+[\w-]+\*\*\s*[—-]\s*(?:pass|fail)?\s*[—-]?\s*)/,
29
+ /(\*\*\[\d{4}-\d{2}-\d{2}\]\s+[\w-]+\*\*\s*[\u2014-]\s*(?:pass|fail)?\s*[\u2014-]?\s*)/,
30
30
  '$1[resolved] '
31
31
  );
32
32
  changed = true;
@@ -46,7 +46,7 @@ function tagLessonResolvedInMd(cwd, slug) {
46
46
  * and trustworthy.
47
47
  *
48
48
  * Only detector-backed `fail` lessons self-retire. Prose-only lessons (no
49
- * detector) and `observed` process rules are never auto-resolved they have
49
+ * detector) and `observed` process rules are never auto-resolved, they have
50
50
  * no falsifiable pass state.
51
51
  *
52
52
  * @param {string} cwd
package/commands/log.js CHANGED
@@ -119,7 +119,7 @@ function logAtris() {
119
119
  }
120
120
 
121
121
  console.log(`┌─────────────────────────────────────────────────────────┐`);
122
- console.log(`│ Daily Log ${dateFormatted} [type "exit" to quit] │`);
122
+ console.log(`│ Daily Log, ${dateFormatted} [type "exit" to quit] │`);
123
123
  console.log(`└─────────────────────────────────────────────────────────┘`);
124
124
  console.log('');
125
125
 
package/commands/loops.js CHANGED
@@ -308,7 +308,7 @@ function auditLoops(cwd = process.cwd()) {
308
308
  console.log('No atris/loops directory found.');
309
309
  console.log('Run: atris loops init');
310
310
  console.log('');
311
- console.log('SELF-IMPROVING: NOT YET 1 open');
311
+ console.log('SELF-IMPROVING: NOT YET, 1 open');
312
312
  return 1;
313
313
  }
314
314
 
@@ -320,7 +320,7 @@ function auditLoops(cwd = process.cwd()) {
320
320
  console.log('No loop files found in atris/loops.');
321
321
  console.log('Run: atris loops init');
322
322
  console.log('');
323
- console.log('SELF-IMPROVING: NOT YET 1 open');
323
+ console.log('SELF-IMPROVING: NOT YET, 1 open');
324
324
  return 1;
325
325
  }
326
326
 
@@ -352,7 +352,7 @@ function auditLoops(cwd = process.cwd()) {
352
352
  console.log('SELF-IMPROVING: YES');
353
353
  return 0;
354
354
  }
355
- console.log(`SELF-IMPROVING: NOT YET ${open} open`);
355
+ console.log(`SELF-IMPROVING: NOT YET, ${open} open`);
356
356
  return 1;
357
357
  }
358
358
 
@@ -382,7 +382,7 @@ function showLoopsHelp() {
382
382
  }
383
383
 
384
384
  function loopsCommand(subcommand, ...args) {
385
- // `loops <sub> --help` is a help request, not a run tick/start/stop/init
385
+ // `loops <sub> --help` is a help request, not a run, tick/start/stop/init
386
386
  // mutate loop state, so never execute a subcommand just to show usage.
387
387
  if (subcommand === '--help' || subcommand === '-h' || subcommand === 'help'
388
388
  || args.includes('--help') || args.includes('-h')) {