atris 3.34.0 → 3.36.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.
Files changed (163) hide show
  1. package/AGENTS.md +35 -0
  2. package/FOR_AGENTS.md +5 -3
  3. package/README.md +5 -3
  4. package/atris/GETTING_STARTED.md +1 -1
  5. package/atris/atris.md +3 -0
  6. package/atris/policies/day-loop-voice.md +102 -0
  7. package/atris/policies/outbound-artifact-gate.md +2 -0
  8. package/atris/skills/design/SKILL.md +56 -32
  9. package/atris/skills/endgame/SKILL.md +12 -6
  10. package/atris/skills/engines/SKILL.md +35 -8
  11. package/atris/skills/fable-method/SKILL.md +66 -0
  12. package/atris/skills/improve/SKILL.md +65 -45
  13. package/atris/skills/render-cli/SKILL.md +88 -0
  14. package/atris/skills/youtube/SKILL.md +10 -1
  15. package/atris.md +6 -2
  16. package/ax +617 -31
  17. package/bin/atris.js +719 -266
  18. package/commands/activate.js +194 -88
  19. package/commands/aeo.js +52 -0
  20. package/commands/agents.js +166 -0
  21. package/commands/autoland.js +718 -72
  22. package/commands/autopilot-front.js +20 -2
  23. package/commands/autopilot.js +118 -2
  24. package/commands/avail.js +407 -0
  25. package/commands/bench.js +188 -0
  26. package/commands/brain.js +3 -0
  27. package/commands/brief.js +651 -0
  28. package/commands/business-sync.js +192 -6
  29. package/commands/business.js +91 -8
  30. package/commands/clean.js +50 -24
  31. package/commands/close.js +1083 -0
  32. package/commands/cloud.js +245 -0
  33. package/commands/codex-goal.js +26 -2
  34. package/commands/compile.js +292 -1
  35. package/commands/computer.js +150 -3
  36. package/commands/dream.js +365 -0
  37. package/commands/drill.js +371 -0
  38. package/commands/drive.js +187 -0
  39. package/commands/engine.js +1061 -29
  40. package/commands/experiments.js +28 -0
  41. package/commands/feed.js +202 -0
  42. package/commands/feedback.js +34 -12
  43. package/commands/fleet-report.js +206 -0
  44. package/commands/github.js +38 -0
  45. package/commands/gm.js +285 -3
  46. package/commands/goal.js +247 -0
  47. package/commands/improve.js +642 -26
  48. package/commands/init.js +83 -43
  49. package/commands/integrations.js +39 -11
  50. package/commands/interview.js +209 -0
  51. package/commands/land.js +253 -52
  52. package/commands/lesson.js +112 -1
  53. package/commands/lifecycle.js +39 -3
  54. package/commands/linear.js +38 -0
  55. package/commands/log.js +84 -1
  56. package/commands/loops.js +220 -16
  57. package/commands/meet.js +220 -0
  58. package/commands/member.js +899 -66
  59. package/commands/mission.js +3512 -332
  60. package/commands/next.js +137 -0
  61. package/commands/now.js +240 -21
  62. package/commands/one-lap.js +776 -0
  63. package/commands/orb.js +314 -0
  64. package/commands/pack-craft.js +179 -0
  65. package/commands/pack.js +823 -0
  66. package/commands/play.js +3 -2
  67. package/commands/probe.js +30 -3
  68. package/commands/pulse.js +241 -46
  69. package/commands/push.js +260 -82
  70. package/commands/radar.js +259 -14
  71. package/commands/rainmaker.js +49 -0
  72. package/commands/report.js +415 -0
  73. package/commands/scout.js +147 -0
  74. package/commands/search.js +363 -0
  75. package/commands/serve.js +54 -0
  76. package/commands/skill.js +47 -3
  77. package/commands/slop.js +50 -2
  78. package/commands/soul.js +1 -1
  79. package/commands/status.js +50 -5
  80. package/commands/stream.js +861 -0
  81. package/commands/stripe.js +38 -0
  82. package/commands/study.js +693 -0
  83. package/commands/supabase.js +39 -0
  84. package/commands/sync.js +67 -54
  85. package/commands/task.js +2275 -182
  86. package/commands/team.js +73 -0
  87. package/commands/truth.js +29 -3
  88. package/commands/unknowns.js +627 -0
  89. package/commands/update.js +44 -0
  90. package/commands/vercel.js +38 -0
  91. package/commands/verify.js +96 -0
  92. package/commands/watch.js +303 -0
  93. package/commands/wish.js +500 -0
  94. package/commands/workflow.js +11 -5
  95. package/commands/worktree.js +299 -20
  96. package/commands/write.js +399 -0
  97. package/commands/xp.js +29 -11
  98. package/lib/auto-accept-certified.js +391 -43
  99. package/lib/autoland.js +353 -52
  100. package/lib/ax-auto-lane.js +79 -0
  101. package/lib/bench/context.js +147 -0
  102. package/lib/bench/engines.js +141 -0
  103. package/lib/bench/report.js +140 -0
  104. package/lib/bench/runner.js +512 -0
  105. package/lib/brief-ledger.js +350 -0
  106. package/lib/cloud-mission.js +259 -0
  107. package/lib/codex-flight.js +154 -0
  108. package/lib/default-runner.js +45 -0
  109. package/lib/default-verifier.js +70 -0
  110. package/lib/engine-registry.js +232 -0
  111. package/lib/experiments/daily.js +640 -0
  112. package/lib/fleet.js +2431 -38
  113. package/lib/improve-vitals-html.js +171 -0
  114. package/lib/known-commands.js +58 -0
  115. package/lib/loop-doctor.js +416 -0
  116. package/lib/member-switches.js +144 -0
  117. package/lib/memory-view.js +14 -5
  118. package/lib/mission-room.js +1 -0
  119. package/lib/mission-root.js +52 -0
  120. package/lib/mission-runtime-loop.js +7 -0
  121. package/lib/next-moves.js +327 -10
  122. package/lib/official-cli-integration.js +174 -0
  123. package/lib/one-lap-validator.js +60 -0
  124. package/lib/orb-context.js +477 -0
  125. package/lib/orb-scorecard.js +224 -0
  126. package/lib/outbound-send-gate.js +165 -0
  127. package/lib/permission-grants.js +293 -0
  128. package/lib/policy-lessons.js +52 -1
  129. package/lib/pulse.js +277 -3
  130. package/lib/receipt-block.js +168 -0
  131. package/lib/receipt-evidence.js +65 -4
  132. package/lib/review-integrity.js +147 -0
  133. package/lib/router-brain.js +352 -0
  134. package/lib/runner-command.js +33 -0
  135. package/lib/self-drive.js +258 -0
  136. package/lib/short-name.js +103 -0
  137. package/lib/spawn-env.js +18 -0
  138. package/lib/state-detection.js +56 -1
  139. package/lib/sync-status.js +59 -0
  140. package/lib/task-db.js +319 -27
  141. package/lib/task-proof.js +43 -1
  142. package/lib/task-receipt.js +93 -0
  143. package/lib/team-presence.js +260 -0
  144. package/lib/tool-result-encode.js +7 -0
  145. package/lib/trust-tiers.js +90 -0
  146. package/lib/usage.js +107 -0
  147. package/lib/voice-gate.js +163 -0
  148. package/lib/wish-audit.js +1368 -0
  149. package/lib/wish-delegate.js +1840 -0
  150. package/lib/wish-design.js +110 -0
  151. package/lib/wish-stats.js +183 -0
  152. package/lib/wish-store.js +354 -0
  153. package/lib/zip.js +221 -0
  154. package/package.json +3 -1
  155. package/templates/loops/atris/loops/LOOPS.md +55 -0
  156. package/templates/loops/atris/loops/TICK.md +24 -0
  157. package/templates/loops/atris/loops/feedback.md +22 -0
  158. package/templates/loops/atris/loops/quality.md +22 -0
  159. package/templates/loops/atris/wiki/systems/loops.md +41 -0
  160. package/utils/api.js +5 -1
  161. package/utils/auth.js +57 -21
  162. package/utils/update-check.js +27 -6
  163. package/atris/learnings.jsonl +0 -1
@@ -0,0 +1,137 @@
1
+ 'use strict';
2
+
3
+ const {
4
+ claimRoadmapItem,
5
+ nextCards,
6
+ markDreamCardConsumed,
7
+ parkNextCard,
8
+ recordDecision,
9
+ seedInboxFromMove,
10
+ } = require('../lib/next-moves');
11
+
12
+ function showHelp() {
13
+ console.log('');
14
+ console.log('Usage: atris next [yes|no|skip]');
15
+ console.log('');
16
+ console.log('Shows one next move card.');
17
+ console.log('');
18
+ return 0;
19
+ }
20
+
21
+ function cardLabel(card) {
22
+ return String(card?.label || card?.title || '').trim();
23
+ }
24
+
25
+ function cardWhy(card) {
26
+ const why = String(card?.why || '').trim();
27
+ if (why) return why;
28
+ const status = String(card?.status || '').trim();
29
+ if (status) return status;
30
+ if (card?.source === 'task' || card?.source === 'mission') return 'working';
31
+ return 'new';
32
+ }
33
+
34
+ function renderCard(card) {
35
+ if (!card) return 'Nothing to do. Rest or wish.';
36
+ return [
37
+ cardLabel(card),
38
+ `Why now: ${cardWhy(card)}`,
39
+ 'Do it? yes / no / skip',
40
+ ].join('\n');
41
+ }
42
+
43
+ function printCard(card) {
44
+ console.log(renderCard(card));
45
+ }
46
+
47
+ function wishPromptTitle(label) {
48
+ return String(label || 'this').replace(/^(#\d+)\s+/, '$1: ');
49
+ }
50
+
51
+ function printWishAnswerPrompt(card) {
52
+ const action = card.next_action || {};
53
+ console.log(`Got it, wish ${wishPromptTitle(action.label || cardLabel(card))}.`);
54
+ console.log(String(action.question || 'What should be different when this wish comes true?'));
55
+ console.log('Answer with: atris wish answer "your words"');
56
+ return 0;
57
+ }
58
+
59
+ function approveMove(card, root) {
60
+ recordDecision(root, card, 'approve', new Date().toISOString());
61
+ if (['roadmap', 'inbox', 'endgame'].includes(card.source)) {
62
+ seedInboxFromMove(root, card);
63
+ if (card.source === 'roadmap') claimRoadmapItem(root, card.title);
64
+ console.log(`${cardLabel(card)} is working.`);
65
+ return 0;
66
+ }
67
+ const prompt = card.next_action && card.next_action.prompt;
68
+ console.log(prompt || `${cardLabel(card)} is working.`);
69
+ return 0;
70
+ }
71
+
72
+ function completeMissionFromCard(card) {
73
+ const action = card.next_action || {};
74
+ if (!action.mission_id || !action.proof_path) {
75
+ console.log('Review the proof, then complete this mission.');
76
+ return 0;
77
+ }
78
+ const { completeMission } = require('./mission');
79
+ completeMission([action.mission_id, '--proof', action.proof_path]);
80
+ return 0;
81
+ }
82
+
83
+ function executeCard(card, root) {
84
+ const action = card && card.next_action;
85
+ if (!card) {
86
+ printCard(null);
87
+ return 0;
88
+ }
89
+ if (action?.type === 'wish_answer_prompt') return printWishAnswerPrompt(card);
90
+ if (action?.type === 'mission_complete') return completeMissionFromCard(card);
91
+ if (action?.type === 'wish_review_prompt' || action?.type === 'mission_review_prompt') {
92
+ console.log(action.prompt || 'Review the proof, then choose done or stuck.');
93
+ return 0;
94
+ }
95
+ return approveMove(card, root);
96
+ }
97
+
98
+ function nextCommand(args = [], root = process.cwd()) {
99
+ const action = String(args[0] || '').trim().toLowerCase();
100
+ if (action === 'help' || action === '--help' || action === '-h') return showHelp();
101
+
102
+ const current = nextCards(root, 1)[0] || null;
103
+ if (!action) {
104
+ printCard(current);
105
+ if (current?.source === 'dream') markDreamCardConsumed(root, current, new Date().toISOString(), 'dealt');
106
+ return 0;
107
+ }
108
+
109
+ if (action === 'skip') {
110
+ const following = current ? nextCards(root, 1, { skipIds: [current.id] })[0] : null;
111
+ if (current?.source === 'dream') markDreamCardConsumed(root, current, new Date().toISOString(), 'skipped');
112
+ printCard(following);
113
+ if (following?.source === 'dream') markDreamCardConsumed(root, following, new Date().toISOString(), 'dealt');
114
+ return 0;
115
+ }
116
+
117
+ if (action === 'no') {
118
+ if (!current) {
119
+ printCard(null);
120
+ return 0;
121
+ }
122
+ parkNextCard(root, current);
123
+ console.log(`Parked ${cardLabel(current)}.`);
124
+ return 0;
125
+ }
126
+
127
+ if (action === 'yes') return executeCard(current, root);
128
+
129
+ console.log('Say yes, no, or skip.');
130
+ return 2;
131
+ }
132
+
133
+ module.exports = {
134
+ cardLabel,
135
+ nextCommand,
136
+ renderCard,
137
+ };
package/commands/now.js CHANGED
@@ -1,10 +1,13 @@
1
1
  const fs = require('fs');
2
2
  const path = require('path');
3
3
  const { hasRenderedSections, isOpenSection } = require('../lib/todo-sections');
4
+ const { renderMorningCardRow } = require('../lib/receipt-block');
5
+ const { historicalLandingText } = require('../lib/autoland');
4
6
 
5
7
  const NOW_PATH = path.join('atris', 'now.md');
6
8
  const TASK_EPISODES_PATH = path.join('.atris', 'state', 'task_episodes.jsonl');
7
9
  const CAREER_XP_RECEIPTS_PATH = path.join('.atris', 'state', 'career_xp_receipts.jsonl');
10
+ const MISSION_RUNS_PATH = path.join('atris', 'runs');
8
11
  const EXECUTABLE_TASK_STATUSES = new Set(['open', 'claimed']);
9
12
  const TASK_RECEIPT_EVENTS = new Set(['proof_ready', 'reviewed', 'completed']);
10
13
 
@@ -15,6 +18,12 @@ function formatLocalDate(date = new Date()) {
15
18
  return `${year}-${month}-${day}`;
16
19
  }
17
20
 
21
+ function formatLocalTimestamp(date = new Date()) {
22
+ const hours = String(date.getHours()).padStart(2, '0');
23
+ const minutes = String(date.getMinutes()).padStart(2, '0');
24
+ return `${formatLocalDate(date)} at ${hours}:${minutes} local time`;
25
+ }
26
+
18
27
  function todayIso() {
19
28
  return formatLocalDate(new Date());
20
29
  }
@@ -167,6 +176,20 @@ function taskReceiptProof(row) {
167
176
  ).trim();
168
177
  }
169
178
 
179
+ function taskReceiptResult(row) {
180
+ return compactLine(
181
+ row?.state?.metadata?.result
182
+ || row?.review_landing?.happened
183
+ || row?.state?.metadata?.landing_happened
184
+ || '',
185
+ );
186
+ }
187
+
188
+ function taskReceiptCertified(row) {
189
+ return row?.state?.metadata?.agent_certified === true
190
+ || /second-actor check/i.test(taskReceiptProof(row));
191
+ }
192
+
170
193
  function taskReceiptKey(row, fallback) {
171
194
  const episodeId = row?.episode_id || row?.source_episode_id;
172
195
  if (episodeId) return `episode:${episodeId}`;
@@ -175,30 +198,203 @@ function taskReceiptKey(row, fallback) {
175
198
  return `row:${fallback}`;
176
199
  }
177
200
 
178
- function countTaskReceiptsToday(root = process.cwd(), date = new Date()) {
201
+ function storeTaskReceipt(byKey, row) {
202
+ const key = taskReceiptKey(row, byKey.size);
203
+ const previous = byKey.get(key);
204
+ const candidate = {
205
+ title: taskReceiptTitle(row),
206
+ result: taskReceiptResult(row),
207
+ certified: taskReceiptCertified(row),
208
+ };
209
+ byKey.set(key, {
210
+ title: candidate.title || previous?.title,
211
+ result: candidate.result || previous?.result || '',
212
+ certified: Boolean(candidate.certified || previous?.certified),
213
+ });
214
+ }
215
+
216
+ // One pass over today's receipt sources, deduped by key. Both the
217
+ // "Completed receipts today" count and the readable "While You Were Away"
218
+ // lines derive from this, so the number and the list can never disagree.
219
+ function collectTaskReceiptsToday(root = process.cwd(), date = new Date()) {
179
220
  const targetDay = formatLocalDate(date);
180
221
  const stateDir = path.join(root, '.atris', 'state');
181
- const seen = new Set();
222
+ const byKey = new Map();
182
223
 
183
224
  for (const row of readJsonlRows(path.join(stateDir, 'task_episodes.jsonl'))) {
184
225
  if (localDateKey(row?.created_at) !== targetDay) continue;
185
226
  if (!rowMatchesWorkspace(row?.workspace_root, root)) continue;
186
- if (!taskReceiptProof(row)) continue;
227
+ const proof = taskReceiptProof(row);
228
+ if (!proof) continue;
187
229
  const eventType = String(row?.action?.event_type || '').toLowerCase();
188
230
  if (eventType && !TASK_RECEIPT_EVENTS.has(eventType)) continue;
189
- seen.add(taskReceiptKey(row, seen.size));
231
+ storeTaskReceipt(byKey, row);
190
232
  }
191
233
 
192
234
  for (const row of readJsonlRows(path.join(stateDir, 'career_xp_receipts.jsonl'))) {
193
235
  if (localDateKey(row?.accepted_at || row?.created_at || row?.ts) !== targetDay) continue;
194
236
  if (!rowMatchesWorkspace(row?.workspace_root, root)) continue;
195
- if (!taskReceiptProof(row)) continue;
237
+ const proof = taskReceiptProof(row);
238
+ if (!proof) continue;
196
239
  const source = String(row?.source_type || row?.receipt_id || row?.source || '').toLowerCase();
197
240
  if (!source.includes('task')) continue;
198
- seen.add(taskReceiptKey(row, seen.size));
241
+ storeTaskReceipt(byKey, row);
242
+ }
243
+
244
+ return Array.from(byKey.values());
245
+ }
246
+
247
+ function countTaskReceiptsToday(root = process.cwd(), date = new Date()) {
248
+ return collectTaskReceiptsToday(root, date).length;
249
+ }
250
+
251
+ function compactLine(value) {
252
+ return String(value || '').replace(/\s+/g, ' ').trim();
253
+ }
254
+
255
+ function truncateLine(value, max) {
256
+ const s = compactLine(value);
257
+ if (s.length <= max) return s;
258
+ return `${s.slice(0, max - 1).trimEnd()}…`;
259
+ }
260
+
261
+ function compactCommitSubject(value, max = 90) {
262
+ return historicalLandingText(compactLine(value), max);
263
+ }
264
+
265
+ function taskReceiptTitle(row) {
266
+ return compactLine(row?.state?.title || row?.title || row?.task_id || 'untitled work');
267
+ }
268
+
269
+ // The receipts behind "Completed receipts today", as lines a human can read:
270
+ // one per receipt, landed result + human proof state. Shares the collector
271
+ // with the count so the number and the visible list always match.
272
+ function todayTaskReceiptLines(root = process.cwd(), date = new Date(), limit = 6) {
273
+ return collectTaskReceiptsToday(root, date)
274
+ .slice(-limit)
275
+ .reverse()
276
+ .map((r) => {
277
+ const result = historicalLandingText(r.result || r.title, 180);
278
+ const proofState = r.certified ? 'independently checked' : 'proof on file';
279
+ return `- ✓ ${result} - ${proofState}`;
280
+ });
281
+ }
282
+
283
+ // What actually landed on this branch in the last day - the fleet's overnight
284
+ // merges are the strongest "the computer worked while you were away" evidence.
285
+ function landedCommitLines(root = process.cwd(), limit = 5) {
286
+ try {
287
+ const { execFileSync } = require('node:child_process');
288
+ const out = execFileSync(
289
+ 'git',
290
+ ['log', '--since=24 hours ago', '--no-merges', '--format=%s', '-n', '40'],
291
+ { cwd: root, encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] },
292
+ );
293
+ const seen = new Set();
294
+ const lines = [];
295
+ for (const subject of out.split('\n')) {
296
+ const s = compactLine(subject);
297
+ if (!s || seen.has(s)) continue;
298
+ seen.add(s);
299
+ lines.push(`- ↑ ${compactCommitSubject(s, 90)}`);
300
+ if (lines.length >= limit) break;
301
+ }
302
+ return lines;
303
+ } catch {
304
+ return [];
305
+ }
306
+ }
307
+
308
+ // The one thing only the owner can answer. Prefers an explicit owner action;
309
+ // falls back to a blocked loop's open question (needs_human). Loops ask here -
310
+ // they never invent the answer themselves.
311
+ function nextOwnerActionLine(root = process.cwd()) {
312
+ const statusPath = path.join(root, 'atris', 'status', 'master-loop.md');
313
+ if (!fs.existsSync(statusPath)) return null;
314
+ const lines = fs.readFileSync(statusPath, 'utf8').split(/\r?\n/);
315
+ for (const prefix of ['- next_owner_action:', '- needs_human:']) {
316
+ const line = lines.find((l) => l.startsWith(prefix));
317
+ if (line) {
318
+ const action = truncateLine(line.replace(prefix, ''), 220);
319
+ if (action) return action;
320
+ }
199
321
  }
322
+ return null;
323
+ }
324
+
325
+ function currentMissionMoveLine(root = process.cwd()) {
326
+ try {
327
+ const { selectCodexGoalMission, codexGoalNextCommand, missionBudgetContinuationText } = require('./mission');
328
+ const selected = selectCodexGoalMission(root);
329
+ const mission = selected?.mission || null;
330
+ if (!mission) return null;
331
+ const objective = completeMissionObjective(mission.objective, 140);
332
+ const budgetContinuation = missionBudgetContinuationText(mission);
333
+ if (budgetContinuation) return `The move: ${objective} - next: ${budgetContinuation}`;
334
+ const compact = compactMissionCommand(codexGoalNextCommand(mission), mission);
335
+ const next = compact.length <= 160 ? compact : `atris mission status ${mission.n || mission.id}`;
336
+ if (!objective || !next) return null;
337
+ return `The move: ${objective} - next: ${next}`;
338
+ } catch {
339
+ return null;
340
+ }
341
+ }
200
342
 
201
- return seen.size;
343
+ function completeMissionObjective(objective, max = 140) {
344
+ const clean = String(objective || '').replace(/\s+/g, ' ').trim();
345
+ const firstClause = clean.split(',', 1)[0].trim();
346
+ if (firstClause.length >= 40 && firstClause.length <= max) return firstClause;
347
+ return historicalLandingText(clean, max);
348
+ }
349
+
350
+ function compactMissionCommand(command, mission) {
351
+ const value = String(command || '').trim();
352
+ const id = String(mission?.id || '').trim();
353
+ const shortRef = String(mission?.n || '').trim();
354
+ return id && shortRef ? value.split(id).join(shortRef) : value;
355
+ }
356
+
357
+ function receiptSortKey(receipt) {
358
+ return String(receipt?.at || receipt?.result?.tick?.finished_at || receipt?.result?.tick?.started_at || '');
359
+ }
360
+
361
+ function collectMissionReceiptsToday(root = process.cwd(), date = new Date()) {
362
+ const targetDay = formatLocalDate(date);
363
+ const runsDir = path.join(root, MISSION_RUNS_PATH);
364
+ if (!fs.existsSync(runsDir)) return [];
365
+ let files = [];
366
+ try {
367
+ files = fs.readdirSync(runsDir)
368
+ .filter((file) => file.endsWith('.json'))
369
+ .map((file) => path.join(runsDir, file));
370
+ } catch {
371
+ return [];
372
+ }
373
+ const receipts = [];
374
+ for (const file of files) {
375
+ let receipt = null;
376
+ try {
377
+ receipt = JSON.parse(fs.readFileSync(file, 'utf8'));
378
+ } catch {
379
+ continue;
380
+ }
381
+ if (receipt?.schema !== 'atris.mission_receipt.v1') continue;
382
+ if (localDateKey(receiptSortKey(receipt)) !== targetDay) continue;
383
+ receipts.push(receipt);
384
+ }
385
+ receipts.sort((a, b) => receiptSortKey(a).localeCompare(receiptSortKey(b)));
386
+ return receipts;
387
+ }
388
+
389
+ function countMissionReceiptsToday(root = process.cwd(), date = new Date()) {
390
+ return collectMissionReceiptsToday(root, date).length;
391
+ }
392
+
393
+ function todayMissionReceiptLines(root = process.cwd(), date = new Date(), limit = 6) {
394
+ return collectMissionReceiptsToday(root, date)
395
+ .slice(-limit)
396
+ .reverse()
397
+ .map((receipt) => renderMorningCardRow(receipt));
202
398
  }
203
399
 
204
400
  function currentJournalPath(root = process.cwd()) {
@@ -216,8 +412,26 @@ function renderDefaultNow(root = process.cwd()) {
216
412
  const openTodoCount = countOpenWorkItems(root, todoPath);
217
413
  const inboxCount = countMatches(journalPath, /^-\s+\*\*I\d+:/gm);
218
414
  const taskReceiptCount = countTaskReceiptsToday(root);
219
- const completedCount = taskReceiptCount || countJournalCompletedReceipts(journalPath);
220
- const generated = todayIso();
415
+ const missionReceiptCount = countMissionReceiptsToday(root);
416
+ const completedCount = taskReceiptCount + missionReceiptCount || countJournalCompletedReceipts(journalPath);
417
+ const generated = formatLocalTimestamp();
418
+ const moveLine = currentMissionMoveLine(root);
419
+ const whatMattersNow = moveLine
420
+ ? `${moveLine}\n\n- Run the named next action and leave proof before choosing another.`
421
+ : '- Decide the next useful move before opening more context.';
422
+ const receiptLines = todayTaskReceiptLines(root);
423
+ const missionReceiptLines = todayMissionReceiptLines(root);
424
+ const commitLines = landedCommitLines(root);
425
+ const hiddenReceiptCount = Math.max(0, completedCount - missionReceiptLines.length - receiptLines.length);
426
+ const hiddenReceiptLine = hiddenReceiptCount > 0
427
+ ? [`- ${hiddenReceiptCount} more completed receipt${hiddenReceiptCount === 1 ? '' : 's'} omitted from this short recap; open task results with atris autoland digest and mission proof with atris mission status.`]
428
+ : [];
429
+ const awayLines = [...missionReceiptLines, ...receiptLines, ...hiddenReceiptLine, ...commitLines];
430
+ const whileAway = awayLines.length
431
+ ? awayLines.join('\n')
432
+ : '- Nothing has landed yet today.';
433
+ const ownerAction = nextOwnerActionLine(root);
434
+ const needsYou = ownerAction ? `\n## Needs You\n\n- ${ownerAction}\n` : '';
221
435
 
222
436
  return `# now
223
437
 
@@ -228,8 +442,12 @@ Last updated: ${generated}
228
442
 
229
443
  ## What Matters Now
230
444
 
231
- - Decide the next useful move before opening more context.
445
+ ${whatMattersNow}
446
+
447
+ ## While You Were Away
232
448
 
449
+ ${whileAway}
450
+ ${needsYou}
233
451
  ## Current Priority
234
452
 
235
453
  - Keep the workspace coherent and useful for the next human or agent.
@@ -241,16 +459,6 @@ Last updated: ${generated}
241
459
  - Inbox items today: ${inboxCount}
242
460
  - Completed receipts today: ${completedCount}
243
461
 
244
- ## Watchouts
245
-
246
- - Do not treat old logs as current truth unless this file links to them.
247
- - Do not create motion for its own sake.
248
- - If facts conflict, surface the conflict and cite the receipts.
249
-
250
- ## Next Move
251
-
252
- - Read \`atris/MAP.md\`, \`atris/TODO.md\`, and today's journal only as needed for the task in front of you.
253
-
254
462
  ## Receipts
255
463
 
256
464
  - \`atris/MAP.md\`
@@ -265,7 +473,7 @@ function renderPortfolioNow(root = process.cwd()) {
265
473
  throw new Error('atris/ folder not found. Run "atris init" first.');
266
474
  }
267
475
 
268
- const generated = todayIso();
476
+ const generated = formatLocalTimestamp();
269
477
  const lines = workspaces.map((workspace) => {
270
478
  const heading = readFirstHeading(workspace.mapPath) || workspace.slug;
271
479
  const todoCount = countOpenWorkItems(workspace.root, workspace.todoPath);
@@ -421,10 +629,21 @@ module.exports = {
421
629
  CAREER_XP_RECEIPTS_PATH,
422
630
  ensureNowFile,
423
631
  formatLocalDate,
632
+ formatLocalTimestamp,
424
633
  countJournalCompletedReceipts,
425
634
  countOpenWorkItems,
426
635
  countOpenTodoItems,
636
+ countMissionReceiptsToday,
427
637
  countTaskReceiptsToday,
638
+ currentMissionMoveLine,
639
+ compactMissionCommand,
640
+ compactCommitSubject,
641
+ completeMissionObjective,
642
+ landedCommitLines,
643
+ nextOwnerActionLine,
644
+ todayMissionReceiptLines,
645
+ todayTaskReceiptLines,
646
+ truncateLine,
428
647
  findChildWorkspaces,
429
648
  isGeneratedNowFile,
430
649
  nowAtris,