atris 3.35.0 → 3.37.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 (133) hide show
  1. package/AGENTS.md +37 -0
  2. package/README.md +5 -3
  3. package/atris/GETTING_STARTED.md +1 -1
  4. package/atris/atris.md +3 -0
  5. package/atris/policies/day-loop-voice.md +102 -0
  6. package/atris/policies/outbound-artifact-gate.md +2 -0
  7. package/atris/skills/design/SKILL.md +56 -32
  8. package/atris/skills/endgame/SKILL.md +12 -6
  9. package/atris/skills/engines/SKILL.md +22 -4
  10. package/atris/skills/fable-method/SKILL.md +66 -0
  11. package/atris/skills/improve/SKILL.md +65 -45
  12. package/atris/skills/youtube/SKILL.md +10 -1
  13. package/atris.md +2 -0
  14. package/ax +147 -19
  15. package/bin/atris.js +576 -267
  16. package/commands/activate.js +194 -88
  17. package/commands/agents.js +166 -0
  18. package/commands/autoland.js +459 -107
  19. package/commands/autopilot-front.js +20 -2
  20. package/commands/autopilot.js +118 -2
  21. package/commands/avail.js +407 -0
  22. package/commands/bench.js +188 -0
  23. package/commands/brain.js +3 -0
  24. package/commands/brief.js +651 -0
  25. package/commands/business-sync.js +192 -6
  26. package/commands/clean.js +50 -24
  27. package/commands/close.js +1083 -0
  28. package/commands/cloud.js +245 -0
  29. package/commands/compile.js +292 -1
  30. package/commands/computer.js +150 -3
  31. package/commands/dream.js +365 -0
  32. package/commands/drill.js +371 -0
  33. package/commands/engine.js +993 -32
  34. package/commands/experiments.js +28 -0
  35. package/commands/feedback.js +34 -12
  36. package/commands/fleet-report.js +206 -0
  37. package/commands/gm.js +23 -0
  38. package/commands/goal.js +247 -0
  39. package/commands/improve.js +642 -26
  40. package/commands/init.js +72 -44
  41. package/commands/interview.js +67 -1
  42. package/commands/land.js +152 -52
  43. package/commands/lifecycle.js +39 -3
  44. package/commands/log.js +84 -1
  45. package/commands/loops.js +220 -16
  46. package/commands/meet.js +220 -0
  47. package/commands/member.js +511 -34
  48. package/commands/mission.js +3144 -338
  49. package/commands/next.js +137 -0
  50. package/commands/now.js +220 -25
  51. package/commands/one-lap.js +776 -0
  52. package/commands/orb.js +314 -0
  53. package/commands/pack-craft.js +179 -0
  54. package/commands/pack.js +823 -0
  55. package/commands/play.js +3 -2
  56. package/commands/probe.js +30 -3
  57. package/commands/pulse.js +241 -46
  58. package/commands/push.js +260 -82
  59. package/commands/rainmaker.js +49 -0
  60. package/commands/report.js +415 -0
  61. package/commands/scout.js +147 -0
  62. package/commands/search.js +363 -0
  63. package/commands/skill.js +47 -3
  64. package/commands/slop.js +50 -2
  65. package/commands/soul.js +1 -1
  66. package/commands/stream.js +861 -0
  67. package/commands/study.js +693 -0
  68. package/commands/sync.js +67 -54
  69. package/commands/task.js +1346 -117
  70. package/commands/team.js +73 -0
  71. package/commands/verify.js +96 -0
  72. package/commands/watch.js +303 -0
  73. package/commands/wish.js +500 -0
  74. package/commands/workflow.js +11 -5
  75. package/commands/worktree.js +234 -13
  76. package/commands/xp.js +29 -11
  77. package/lib/auto-accept-certified.js +331 -34
  78. package/lib/autoland.js +319 -54
  79. package/lib/ax-auto-lane.js +96 -0
  80. package/lib/bench/context.js +147 -0
  81. package/lib/bench/engines.js +141 -0
  82. package/lib/bench/report.js +140 -0
  83. package/lib/bench/runner.js +512 -0
  84. package/lib/brief-ledger.js +350 -0
  85. package/lib/cloud-mission.js +259 -0
  86. package/lib/codex-flight.js +154 -0
  87. package/lib/default-runner.js +45 -0
  88. package/lib/default-verifier.js +70 -0
  89. package/lib/engine-registry.js +232 -0
  90. package/lib/experiments/daily.js +640 -0
  91. package/lib/fleet.js +2219 -67
  92. package/lib/improve-vitals-html.js +171 -0
  93. package/lib/known-commands.js +58 -0
  94. package/lib/loop-doctor.js +416 -0
  95. package/lib/member-switches.js +144 -0
  96. package/lib/mission-room.js +1 -0
  97. package/lib/mission-root.js +52 -0
  98. package/lib/next-moves.js +327 -10
  99. package/lib/one-lap-validator.js +60 -0
  100. package/lib/orb-context.js +477 -0
  101. package/lib/orb-scorecard.js +224 -0
  102. package/lib/policy-lessons.js +52 -1
  103. package/lib/pulse.js +277 -3
  104. package/lib/receipt-block.js +168 -0
  105. package/lib/receipt-evidence.js +65 -4
  106. package/lib/router-brain.js +352 -0
  107. package/lib/runner-command.js +10 -0
  108. package/lib/self-drive.js +258 -0
  109. package/lib/short-name.js +103 -0
  110. package/lib/spawn-env.js +18 -0
  111. package/lib/state-detection.js +56 -1
  112. package/lib/sync-status.js +59 -0
  113. package/lib/task-db.js +108 -29
  114. package/lib/task-proof.js +23 -1
  115. package/lib/team-presence.js +260 -0
  116. package/lib/tool-result-encode.js +7 -0
  117. package/lib/trust-tiers.js +90 -0
  118. package/lib/usage.js +107 -0
  119. package/lib/voice-gate.js +163 -0
  120. package/lib/wish-audit.js +1368 -0
  121. package/lib/wish-delegate.js +1840 -0
  122. package/lib/wish-design.js +110 -0
  123. package/lib/wish-stats.js +183 -0
  124. package/lib/wish-store.js +354 -0
  125. package/lib/zip.js +221 -0
  126. package/package.json +3 -1
  127. package/templates/loops/atris/loops/LOOPS.md +55 -0
  128. package/templates/loops/atris/loops/TICK.md +24 -0
  129. package/templates/loops/atris/loops/feedback.md +22 -0
  130. package/templates/loops/atris/loops/quality.md +22 -0
  131. package/templates/loops/atris/wiki/systems/loops.md +41 -0
  132. package/utils/api.js +5 -1
  133. package/utils/auth.js +57 -21
@@ -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,51 +198,205 @@ 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);
199
242
  }
200
243
 
201
- return seen.size;
244
+ return Array.from(byKey.values());
245
+ }
246
+
247
+ function countTaskReceiptsToday(root = process.cwd(), date = new Date()) {
248
+ return collectTaskReceiptsToday(root, date).length;
202
249
  }
203
250
 
204
251
  function compactLine(value) {
205
252
  return String(value || '').replace(/\s+/g, ' ').trim();
206
253
  }
207
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
+ }
321
+ }
322
+ return null;
323
+ }
324
+
208
325
  function currentMissionMoveLine(root = process.cwd()) {
209
326
  try {
210
- const { selectCodexGoalMission, codexGoalNextCommand } = require('./mission');
327
+ const { selectCodexGoalMission, codexGoalNextCommand, missionBudgetContinuationText } = require('./mission');
211
328
  const selected = selectCodexGoalMission(root);
212
329
  const mission = selected?.mission || null;
213
330
  if (!mission) return null;
214
- const objective = compactLine(mission.objective);
215
- const next = compactLine(codexGoalNextCommand(mission));
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}`;
216
336
  if (!objective || !next) return null;
217
- return `The move: ${objective} — next: ${next}`;
337
+ return `The move: ${objective} - next: ${next}`;
218
338
  } catch {
219
339
  return null;
220
340
  }
221
341
  }
222
342
 
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));
398
+ }
399
+
223
400
  function currentJournalPath(root = process.cwd()) {
224
401
  const now = new Date();
225
402
  const year = String(now.getFullYear());
@@ -235,12 +412,26 @@ function renderDefaultNow(root = process.cwd()) {
235
412
  const openTodoCount = countOpenWorkItems(root, todoPath);
236
413
  const inboxCount = countMatches(journalPath, /^-\s+\*\*I\d+:/gm);
237
414
  const taskReceiptCount = countTaskReceiptsToday(root);
238
- const completedCount = taskReceiptCount || countJournalCompletedReceipts(journalPath);
239
- const generated = todayIso();
415
+ const missionReceiptCount = countMissionReceiptsToday(root);
416
+ const completedCount = taskReceiptCount + missionReceiptCount || countJournalCompletedReceipts(journalPath);
417
+ const generated = formatLocalTimestamp();
240
418
  const moveLine = currentMissionMoveLine(root);
241
419
  const whatMattersNow = moveLine
242
- ? `${moveLine}\n\n- Decide the next useful move before opening more context.`
420
+ ? `${moveLine}\n\n- Run the named next action and leave proof before choosing another.`
243
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` : '';
244
435
 
245
436
  return `# now
246
437
 
@@ -253,6 +444,10 @@ Last updated: ${generated}
253
444
 
254
445
  ${whatMattersNow}
255
446
 
447
+ ## While You Were Away
448
+
449
+ ${whileAway}
450
+ ${needsYou}
256
451
  ## Current Priority
257
452
 
258
453
  - Keep the workspace coherent and useful for the next human or agent.
@@ -264,16 +459,6 @@ ${whatMattersNow}
264
459
  - Inbox items today: ${inboxCount}
265
460
  - Completed receipts today: ${completedCount}
266
461
 
267
- ## Watchouts
268
-
269
- - Do not treat old logs as current truth unless this file links to them.
270
- - Do not create motion for its own sake.
271
- - If facts conflict, surface the conflict and cite the receipts.
272
-
273
- ## Next Move
274
-
275
- - Read \`atris/MAP.md\`, \`atris/TODO.md\`, and today's journal only as needed for the task in front of you.
276
-
277
462
  ## Receipts
278
463
 
279
464
  - \`atris/MAP.md\`
@@ -288,7 +473,7 @@ function renderPortfolioNow(root = process.cwd()) {
288
473
  throw new Error('atris/ folder not found. Run "atris init" first.');
289
474
  }
290
475
 
291
- const generated = todayIso();
476
+ const generated = formatLocalTimestamp();
292
477
  const lines = workspaces.map((workspace) => {
293
478
  const heading = readFirstHeading(workspace.mapPath) || workspace.slug;
294
479
  const todoCount = countOpenWorkItems(workspace.root, workspace.todoPath);
@@ -444,11 +629,21 @@ module.exports = {
444
629
  CAREER_XP_RECEIPTS_PATH,
445
630
  ensureNowFile,
446
631
  formatLocalDate,
632
+ formatLocalTimestamp,
447
633
  countJournalCompletedReceipts,
448
634
  countOpenWorkItems,
449
635
  countOpenTodoItems,
636
+ countMissionReceiptsToday,
450
637
  countTaskReceiptsToday,
451
638
  currentMissionMoveLine,
639
+ compactMissionCommand,
640
+ compactCommitSubject,
641
+ completeMissionObjective,
642
+ landedCommitLines,
643
+ nextOwnerActionLine,
644
+ todayMissionReceiptLines,
645
+ todayTaskReceiptLines,
646
+ truncateLine,
452
647
  findChildWorkspaces,
453
648
  isGeneratedNowFile,
454
649
  nowAtris,