atris 3.74.0 → 3.74.2

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 (59) hide show
  1. package/bin/atris.js +85 -82
  2. package/commands/aeo.js +2 -2
  3. package/commands/align.js +10 -19
  4. package/commands/auth.js +1 -1
  5. package/commands/autopilot.js +9 -10
  6. package/commands/avail.js +36 -7
  7. package/commands/clean.js +2 -1
  8. package/commands/close.js +7 -4
  9. package/commands/computer.js +3 -1
  10. package/commands/console.js +1 -1
  11. package/commands/context-sync.js +1 -60
  12. package/commands/experiments.js +4 -1
  13. package/commands/fleet.js +0 -1
  14. package/commands/improve.js +3 -1
  15. package/commands/interview.js +13 -5
  16. package/commands/land.js +5 -1
  17. package/commands/lifecycle.js +17 -6
  18. package/commands/log-sync.js +1 -1
  19. package/commands/loops.js +5 -2
  20. package/commands/member.js +17 -11
  21. package/commands/mission.js +1 -4
  22. package/commands/now.js +0 -7
  23. package/commands/play.js +3 -1
  24. package/commands/plugin.js +1 -1
  25. package/commands/pull.js +5 -3
  26. package/commands/push.js +0 -1
  27. package/commands/radar.js +0 -4
  28. package/commands/recap.js +25 -1
  29. package/commands/report.js +0 -4
  30. package/commands/review.js +1 -1
  31. package/commands/security-review.js +4 -1
  32. package/commands/setup.js +6 -2
  33. package/commands/soul.js +2 -1
  34. package/commands/sync.js +8 -2
  35. package/commands/task.js +0 -522
  36. package/commands/terminal.js +1 -1
  37. package/commands/x-search.js +5 -3
  38. package/commands/youtube.js +9 -7
  39. package/commands/zen.js +4 -4
  40. package/lib/agent-today.js +412 -0
  41. package/lib/apply-gate.js +5 -3
  42. package/lib/autoland.js +0 -8
  43. package/lib/bench/runner.js +0 -4
  44. package/lib/chat-log-scan.js +0 -28
  45. package/lib/cli-scope.js +4 -2
  46. package/lib/engine-registry.js +0 -4
  47. package/lib/experiments/daily.js +2 -1
  48. package/lib/file-ops.js +6 -1
  49. package/lib/first-minute.js +2 -0
  50. package/lib/gm-bet.js +73 -9
  51. package/lib/gm-phone-page.js +42 -5
  52. package/lib/gm-phone.js +3 -0
  53. package/lib/local-day.js +10 -0
  54. package/lib/manifest.js +0 -10
  55. package/lib/member-switches.js +0 -11
  56. package/lib/review-integrity.js +0 -4
  57. package/lib/scorecard.js +7 -6
  58. package/lib/wiki.js +4 -1
  59. package/package.json +1 -1
package/commands/task.js CHANGED
@@ -94,10 +94,6 @@ const PROOF_BOUNDARY_BLOCKED_REASON = 'proof_boundary_blocked_requires_revision'
94
94
  const MISSION_XP_END_TO_END_REASON = 'mission_xp_requires_end_to_end_receipt';
95
95
  const MISSION_XP_END_TO_END_DETAIL = 'mission XP proof must name a zero-papercut end-to-end fresh-laptop pass through install, init, first mission, and first self-landed task; generic mission/tick receipts are not enough';
96
96
  const READY_RESULT_TEACHING = 'ready needs --result: one plain sentence someone new to the project can understand. say what someone can do now and why it matters. no ids, no paths, no commands. example: operators can now read the whole team day on one page instead of scrolling raw logs.';
97
- const REVIEW_AUTO_ACCEPT_ACTOR = 'auto (certified, small)';
98
- const REVIEW_AUTO_ACCEPT_POLICY = 'review_autoaccept_certified_small';
99
- const REVIEW_AUTO_ACCEPT_FILE_LIMIT = 10;
100
- const REVIEW_AUTO_ACCEPT_LINE_LIMIT = 300;
101
97
 
102
98
  const STATUS_PLAN_TAGS = new Set([
103
99
  'agent',
@@ -5010,69 +5006,6 @@ function cmdReviewLaneRun(args) {
5010
5006
  if (!result.ok) process.exit(1);
5011
5007
  }
5012
5008
 
5013
- function configValueDisabled(value) {
5014
- if (value === false) return true;
5015
- const text = String(value === undefined || value === null ? '' : value).trim().toLowerCase();
5016
- return ['0', 'false', 'off', 'no'].includes(text);
5017
- }
5018
-
5019
- function reviewAutoAcceptEnabled() {
5020
- try {
5021
- const { loadConfig } = require('../utils/config');
5022
- const config = loadConfig();
5023
- const value = Object.prototype.hasOwnProperty.call(config, 'autoaccept')
5024
- ? config.autoaccept
5025
- : Object.prototype.hasOwnProperty.call(config, 'review_autoaccept')
5026
- ? config.review_autoaccept
5027
- : config.reviewAutoaccept;
5028
- return !configValueDisabled(value);
5029
- } catch {
5030
- return true;
5031
- }
5032
- }
5033
-
5034
- function reviewAutoAcceptStatePath(root = process.cwd()) {
5035
- return path.join(root, '.atris', 'state', 'review-autoaccept.json');
5036
- }
5037
-
5038
- function readReviewAutoAcceptState(root = process.cwd()) {
5039
- const file = reviewAutoAcceptStatePath(root);
5040
- try {
5041
- if (!fs.existsSync(file)) return {};
5042
- const parsed = JSON.parse(fs.readFileSync(file, 'utf8'));
5043
- return parsed && typeof parsed === 'object' ? parsed : {};
5044
- } catch {
5045
- return {};
5046
- }
5047
- }
5048
-
5049
- function writeReviewAutoAcceptState(root = process.cwd(), state = {}) {
5050
- const file = reviewAutoAcceptStatePath(root);
5051
- fs.mkdirSync(path.dirname(file), { recursive: true });
5052
- fs.writeFileSync(file, JSON.stringify(state, null, 2) + '\n', 'utf8');
5053
- }
5054
-
5055
- function finiteNumber(value) {
5056
- const number = Number(value);
5057
- return Number.isFinite(number) ? number : null;
5058
- }
5059
-
5060
- function firstNumberFromObject(source, keys) {
5061
- if (!source || typeof source !== 'object') return null;
5062
- for (const key of keys) {
5063
- if (!Object.prototype.hasOwnProperty.call(source, key)) continue;
5064
- const value = source[key];
5065
- if (Array.isArray(value)) return value.length;
5066
- if (typeof value === 'string' && value.includes(',')) {
5067
- const parts = value.split(',').map(part => part.trim()).filter(Boolean);
5068
- if (parts.length > 1) return parts.length;
5069
- }
5070
- const number = finiteNumber(value);
5071
- if (number !== null) return number;
5072
- }
5073
- return null;
5074
- }
5075
-
5076
5009
  function arrayFromPathValue(value) {
5077
5010
  if (!value) return [];
5078
5011
  if (Array.isArray(value)) return value.flatMap(arrayFromPathValue);
@@ -5088,448 +5021,6 @@ function arrayFromPathValue(value) {
5088
5021
  .filter(Boolean);
5089
5022
  }
5090
5023
 
5091
- function collectRecordedDiffPaths(source) {
5092
- if (!source || typeof source !== 'object') return [];
5093
- const keys = [
5094
- 'files',
5095
- 'paths',
5096
- 'touched_files',
5097
- 'touchedFiles',
5098
- 'changed_files',
5099
- 'changedFiles',
5100
- 'modified_files',
5101
- 'modifiedFiles',
5102
- ];
5103
- const out = [];
5104
- for (const key of keys) out.push(...arrayFromPathValue(source[key]));
5105
- return out;
5106
- }
5107
-
5108
- function normalizeRecordedDiffStats(source, label = 'recorded') {
5109
- if (!source || typeof source !== 'object') return null;
5110
- const fileKeys = [
5111
- 'files_touched',
5112
- 'filesTouched',
5113
- 'changed_files_count',
5114
- 'changedFilesCount',
5115
- 'files_changed',
5116
- 'filesChanged',
5117
- 'file_count',
5118
- 'fileCount',
5119
- 'files',
5120
- 'changed_files',
5121
- 'changedFiles',
5122
- ];
5123
- const changedLineKeys = [
5124
- 'changed_lines',
5125
- 'changedLines',
5126
- 'lines_changed',
5127
- 'linesChanged',
5128
- 'changed_line_count',
5129
- 'changedLineCount',
5130
- 'total_changed_lines',
5131
- 'totalChangedLines',
5132
- 'line_count',
5133
- 'lineCount',
5134
- ];
5135
- const insertions = firstNumberFromObject(source, ['insertions', 'added', 'additions', 'lines_added', 'linesAdded']);
5136
- const deletions = firstNumberFromObject(source, ['deletions', 'deleted', 'removals', 'lines_deleted', 'linesDeleted']);
5137
- const paths = collectRecordedDiffPaths(source);
5138
- const filesTouched = firstNumberFromObject(source, fileKeys) ?? (paths.length ? paths.length : null);
5139
- const changedLines = firstNumberFromObject(source, changedLineKeys)
5140
- ?? (insertions !== null || deletions !== null ? Number(insertions || 0) + Number(deletions || 0) : null);
5141
- if (filesTouched === null && changedLines === null && !paths.length) return null;
5142
- return {
5143
- source: label,
5144
- files_touched: filesTouched,
5145
- changed_lines: changedLines,
5146
- paths,
5147
- };
5148
- }
5149
-
5150
- function recordedDiffStatsForTask(task) {
5151
- const metadata = task && task.metadata && typeof task.metadata === 'object' ? task.metadata : {};
5152
- const candidates = [
5153
- metadata.diff_stats,
5154
- metadata.diffStats,
5155
- metadata.git_diff_stats,
5156
- metadata.gitDiffStats,
5157
- metadata.change_stats,
5158
- metadata.changeStats,
5159
- metadata.stats,
5160
- metadata,
5161
- ];
5162
- for (const candidate of candidates) {
5163
- const stats = normalizeRecordedDiffStats(candidate, 'recorded');
5164
- if (stats) return stats;
5165
- }
5166
- return null;
5167
- }
5168
-
5169
- function proofDiffStats(proof) {
5170
- const text = String(proof || '').replace(/\s+/g, ' ').trim();
5171
- if (!text) return null;
5172
- let filesTouched = null;
5173
- let changedLines = null;
5174
- const gitStat = text.match(/(\d+)\s+files?\s+changed(?:,\s*(\d+)\s+insertions?\(\+\))?(?:,\s*(\d+)\s+deletions?\(-\))?/i);
5175
- if (gitStat) {
5176
- filesTouched = Number(gitStat[1]);
5177
- const insertions = gitStat[2] ? Number(gitStat[2]) : 0;
5178
- const deletions = gitStat[3] ? Number(gitStat[3]) : 0;
5179
- if (gitStat[2] || gitStat[3]) changedLines = insertions + deletions;
5180
- }
5181
- const filePatterns = [
5182
- /files?\s+(?:touched|changed|modified)\s*[:=]\s*(\d+)/i,
5183
- /(\d+)\s+files?\s+(?:touched|changed|modified)/i,
5184
- ];
5185
- for (const pattern of filePatterns) {
5186
- const match = text.match(pattern);
5187
- if (match) filesTouched = Number(match[1]);
5188
- }
5189
- const linePatterns = [
5190
- /changed\s+lines?\s*[:=]\s*(\d+)/i,
5191
- /(\d+)\s+changed\s+lines?/i,
5192
- /lines?\s+changed\s*[:=]\s*(\d+)/i,
5193
- ];
5194
- for (const pattern of linePatterns) {
5195
- const match = text.match(pattern);
5196
- if (match) changedLines = Number(match[1]);
5197
- }
5198
- if (filesTouched === null && changedLines === null) return null;
5199
- return {
5200
- source: 'proof',
5201
- files_touched: filesTouched,
5202
- changed_lines: changedLines,
5203
- paths: [],
5204
- };
5205
- }
5206
-
5207
- function safeGitRef(value) {
5208
- const text = String(value || '').trim();
5209
- if (!text || text.startsWith('-') || text.includes('..')) return null;
5210
- if (!/^[a-zA-Z0-9_./@=+~^-]+$/.test(text)) return null;
5211
- return text;
5212
- }
5213
-
5214
- function runGitForAutoAccept(root, args) {
5215
- try {
5216
- const { spawnSync } = require('child_process');
5217
- return spawnSync('git', args, {
5218
- cwd: root || process.cwd(),
5219
- encoding: 'utf8',
5220
- timeout: 10000,
5221
- });
5222
- } catch {
5223
- return { status: 1, stdout: '', stderr: '' };
5224
- }
5225
- }
5226
-
5227
- function gitRefExists(root, ref) {
5228
- const result = runGitForAutoAccept(root, ['rev-parse', '--verify', `${ref}^{commit}`]);
5229
- return result.status === 0;
5230
- }
5231
-
5232
- function defaultDiffBase(root) {
5233
- for (const ref of ['origin/master', 'origin/main', 'master', 'main']) {
5234
- if (gitRefExists(root, ref)) return ref;
5235
- }
5236
- return null;
5237
- }
5238
-
5239
- function branchDiffStatsForTask(task, root) {
5240
- const metadata = task && task.metadata && typeof task.metadata === 'object' ? task.metadata : {};
5241
- const branch = safeGitRef(
5242
- metadata.branch
5243
- || metadata.worktree_branch
5244
- || metadata.git_branch
5245
- || metadata.pr_branch
5246
- || metadata.head_branch
5247
- || metadata.worktree?.branch
5248
- );
5249
- if (!branch) return null;
5250
- const base = safeGitRef(
5251
- metadata.base
5252
- || metadata.base_ref
5253
- || metadata.target_ref
5254
- || metadata.target_branch
5255
- || metadata.worktree?.base
5256
- ) || defaultDiffBase(root);
5257
- if (!base) return null;
5258
- const result = runGitForAutoAccept(root, ['diff', '--numstat', `${base}...${branch}`]);
5259
- if (result.status !== 0) return null;
5260
- const lines = String(result.stdout || '').split(/\r?\n/).filter(Boolean);
5261
- if (!lines.length) return { source: 'branch', files_touched: 0, changed_lines: 0, paths: [] };
5262
- let changedLines = 0;
5263
- let unknownLines = false;
5264
- const paths = [];
5265
- for (const line of lines) {
5266
- const parts = line.split(/\t+/);
5267
- const added = Number(parts[0]);
5268
- const deleted = Number(parts[1]);
5269
- if (!Number.isFinite(added) || !Number.isFinite(deleted)) unknownLines = true;
5270
- else changedLines += added + deleted;
5271
- if (parts[2]) paths.push(parts.slice(2).join('\t'));
5272
- }
5273
- return {
5274
- source: 'branch',
5275
- files_touched: lines.length,
5276
- changed_lines: unknownLines ? null : changedLines,
5277
- paths,
5278
- };
5279
- }
5280
-
5281
- function reviewAutoAcceptDiffStats(task, root) {
5282
- const recorded = recordedDiffStatsForTask(task);
5283
- if (recorded) return recorded;
5284
- const branch = branchDiffStatsForTask(task, root);
5285
- if (branch) return branch;
5286
- const proof = String(task?.review?.proof || task?.metadata?.latest_agent_proof || '');
5287
- return proofDiffStats(proof);
5288
- }
5289
-
5290
- function reviewAutoAcceptBigTitle(task) {
5291
- const metadata = task && task.metadata && typeof task.metadata === 'object' ? task.metadata : {};
5292
- const text = [
5293
- task && task.title,
5294
- metadata.kind,
5295
- metadata.type,
5296
- metadata.category,
5297
- metadata.report_type,
5298
- ].map(value => String(value || '')).join(' ').toLowerCase();
5299
- const match = text.match(/\b(daily\s+update|summary|report|digest|retro|retrospective)\b/i);
5300
- if (!match) return null;
5301
- return {
5302
- ok: false,
5303
- reason: `big_title_${match[1].toLowerCase().replace(/\s+/g, '_')}`,
5304
- };
5305
- }
5306
-
5307
- function reviewAutoAcceptSizeGate(task, root) {
5308
- const titleGate = reviewAutoAcceptBigTitle(task);
5309
- if (titleGate) return titleGate;
5310
- const stats = reviewAutoAcceptDiffStats(task, root);
5311
- if (!stats) return { ok: false, reason: 'size_unknown', stats: null };
5312
- const filesTouched = finiteNumber(stats.files_touched);
5313
- const changedLines = finiteNumber(stats.changed_lines);
5314
- if (filesTouched !== null && filesTouched > REVIEW_AUTO_ACCEPT_FILE_LIMIT) {
5315
- return { ok: false, reason: 'big_files', stats };
5316
- }
5317
- if (changedLines !== null && changedLines > REVIEW_AUTO_ACCEPT_LINE_LIMIT) {
5318
- return { ok: false, reason: 'big_changed_lines', stats };
5319
- }
5320
- if (filesTouched === null || changedLines === null) {
5321
- return { ok: false, reason: 'size_unknown', stats };
5322
- }
5323
- return { ok: true, reason: 'small_change', stats };
5324
- }
5325
-
5326
- function reviewAutoAcceptMetadataText(task) {
5327
- const metadata = task && task.metadata && typeof task.metadata === 'object' ? task.metadata : {};
5328
- return [
5329
- task && task.title,
5330
- task && task.tag,
5331
- task && task.source_key,
5332
- metadata.kind,
5333
- metadata.type,
5334
- metadata.category,
5335
- metadata.lane,
5336
- metadata.stage,
5337
- metadata.area,
5338
- JSON.stringify(metadata),
5339
- ].map(value => String(value || '')).join(' ').toLowerCase();
5340
- }
5341
-
5342
- function reviewAutoAcceptTouchedPaths(task) {
5343
- const metadata = task && task.metadata && typeof task.metadata === 'object' ? task.metadata : {};
5344
- const stats = recordedDiffStatsForTask(task);
5345
- const proof = String(task?.review?.proof || metadata.latest_agent_proof || '');
5346
- return [
5347
- ...(stats && stats.paths ? stats.paths : []),
5348
- ...collectRecordedDiffPaths(metadata),
5349
- ...taskReviewEvidencePaths(proof, 50),
5350
- ].map(value => String(value || '').trim()).filter(Boolean);
5351
- }
5352
-
5353
- function reviewProtectedMatch(task) {
5354
- const text = reviewAutoAcceptMetadataText(task);
5355
- const paths = reviewAutoAcceptTouchedPaths(task).join(' ').toLowerCase();
5356
- const combined = `${text} ${paths}`;
5357
- const checks = [
5358
- ['auth', /\b(auth|authentication|authorization|oauth|login|session)\b/i],
5359
- ['credentials', /\b(credentials?|secrets?|passwords?|api[-_ ]?keys?|tokens?)\b/i],
5360
- ['csp', /\b(csp|content[-_ ]security[-_ ]policy)\b/i],
5361
- ['sandbox', /\b(sandbox|allow-scripts|allow-same-origin)\b/i],
5362
- ['billing', /\b(billing|invoice|invoices|subscription|subscriptions)\b/i],
5363
- ['payments', /\b(payments?|stripe|checkout|refunds?)\b/i],
5364
- ['outbound_sends', /\b(outbound|send|sending|email|sms|webhook|notification|notifications)\b/i],
5365
- ];
5366
- for (const [key, pattern] of checks) {
5367
- if (pattern.test(combined)) return { ok: false, reason: `protected_${key}` };
5368
- }
5369
- return { ok: true };
5370
- }
5371
-
5372
- function evaluateReviewAutoAccept(task, root) {
5373
- const ref = taskRef(task);
5374
- if (!task) return { eligible: false, ref, reason: 'task_not_found' };
5375
- const protectedGate = reviewProtectedMatch(task);
5376
- if (!protectedGate.ok) return { eligible: false, ref, reason: protectedGate.reason };
5377
- const sizeGate = reviewAutoAcceptSizeGate(task, root);
5378
- if (!sizeGate.ok) return { eligible: false, ref, reason: sizeGate.reason, size: sizeGate.stats };
5379
- // executeVerify: false, this is a READ path. The verdict comes from
5380
- // metadata.verify_cache, stamped by the two lanes allowed to spawn
5381
- // checks (certify-verified, autoland landing re-check). Spawning here is
5382
- // what turned `atris task reviews` into a fork-bomb multiplier.
5383
- const evaluation = evaluateAutoAccept(task, { strictVerify: true, executeVerify: false });
5384
- if (!evaluation.eligible) {
5385
- return {
5386
- ...evaluation,
5387
- size: sizeGate.stats,
5388
- };
5389
- }
5390
- if (evaluation.verification_pending) {
5391
- const verdict = cachedVerifyVerdict(task, root);
5392
- if (!verdict.fresh) {
5393
- return {
5394
- eligible: false,
5395
- ref,
5396
- reason: 'verification_pending',
5397
- verify: verdict.command || null,
5398
- next_action: 'no fresh executed verify on record; `atris task certify-verified` or the autoland tick runs the stored check and stamps metadata.verify_cache',
5399
- size: sizeGate.stats,
5400
- };
5401
- }
5402
- if (!verdict.ok) {
5403
- return {
5404
- eligible: false,
5405
- ref,
5406
- reason: verdict.reason || 'verify_failed',
5407
- verify: verdict.command,
5408
- verify_cache: verdict,
5409
- size: sizeGate.stats,
5410
- };
5411
- }
5412
- return {
5413
- ...evaluation,
5414
- verification_pending: false,
5415
- verify_cache: verdict,
5416
- eligible: true,
5417
- reason: 'certified_small',
5418
- policy: REVIEW_AUTO_ACCEPT_POLICY,
5419
- size: sizeGate.stats,
5420
- };
5421
- }
5422
- return {
5423
- ...evaluation,
5424
- eligible: true,
5425
- reason: 'certified_small',
5426
- policy: REVIEW_AUTO_ACCEPT_POLICY,
5427
- size: sizeGate.stats,
5428
- };
5429
- }
5430
-
5431
- function autoAcceptCertifiedSmallReviews(taskDb, db, projection) {
5432
- const enabled = reviewAutoAcceptEnabled();
5433
- const root = projection.workspace_root || process.cwd();
5434
- const results = [];
5435
- if (!enabled) {
5436
- return {
5437
- enabled,
5438
- scanned: 0,
5439
- accepted: 0,
5440
- changed: false,
5441
- results,
5442
- };
5443
- }
5444
- const certified = certifiedPendingReviewTasks(projection);
5445
- for (const item of certified) {
5446
- const fullProjection = enrichTaskProjection(taskDb.taskProjection(db, { taskId: item.id }));
5447
- const task = fullProjection.tasks[0] || null;
5448
- const evaluation = evaluateReviewAutoAccept(task, root);
5449
- if (!evaluation.eligible) {
5450
- results.push({ ...evaluation, action: 'queued', task_id: task?.id || item.id || null });
5451
- continue;
5452
- }
5453
- const policyGate = candidatePolicyGate(task, { executeDetectors: true });
5454
- if (!policyGate.ok) {
5455
- results.push({ ...policyGate, action: 'queued', task_id: task?.id || item.id || null });
5456
- continue;
5457
- }
5458
- evaluation.candidate_gate = policyGate.gate;
5459
- const accepted = acceptReviewTask(taskDb, db, task.id, {
5460
- actor: REVIEW_AUTO_ACCEPT_ACTOR,
5461
- proof: evaluation.proof,
5462
- reward: 1,
5463
- lesson: String(task.review?.lesson || task.metadata?.latest_agent_lesson || ''),
5464
- nextTask: String(task.review?.next_task || task.metadata?.latest_agent_next_task || ''),
5465
- autoAccepted: true,
5466
- });
5467
- if (!accepted.ok) {
5468
- results.push({ ...evaluation, action: 'accept_failed', task_id: task.id, reason: accepted.reason });
5469
- continue;
5470
- }
5471
- const landing = stampAutoAcceptMetadata(taskDb, db, task, REVIEW_AUTO_ACCEPT_ACTOR, REVIEW_AUTO_ACCEPT_POLICY);
5472
- refreshCareerXpAfterReview(accepted.reviewed);
5473
- results.push({
5474
- ...evaluation,
5475
- action: 'accepted',
5476
- landing,
5477
- task_id: task.id,
5478
- reward: accepted.reviewed.episode.reward.value,
5479
- });
5480
- }
5481
- const acceptedCount = results.filter(row => row.action === 'accepted').length;
5482
- return {
5483
- enabled,
5484
- scanned: certified.length,
5485
- accepted: acceptedCount,
5486
- changed: acceptedCount > 0,
5487
- results,
5488
- };
5489
- }
5490
-
5491
- function autoAcceptedReviewRowsSince(taskDb, db, workspaceRoot, sinceIso, acceptedNowIds = []) {
5492
- const sinceMs = Date.parse(sinceIso || '');
5493
- const rows = taskDb.withTaskDisplayRefs(taskDb.listTasks(db, { workspaceRoot }));
5494
- const acceptedNow = new Set(acceptedNowIds.filter(Boolean));
5495
- const seen = new Set();
5496
- const out = [];
5497
- for (const row of rows) {
5498
- const metadata = row.metadata && typeof row.metadata === 'object' ? row.metadata : {};
5499
- const acceptedAt = Date.parse(metadata.auto_accepted_at || metadata.accepted_at || '');
5500
- const policyMatch = metadata.auto_accept_policy === REVIEW_AUTO_ACCEPT_POLICY
5501
- || metadata.auto_accepted_by === REVIEW_AUTO_ACCEPT_ACTOR
5502
- || metadata.accepted_by === REVIEW_AUTO_ACCEPT_ACTOR;
5503
- const isCurrent = acceptedNow.has(row.id);
5504
- const isSince = Number.isFinite(sinceMs) && Number.isFinite(acceptedAt) && acceptedAt > sinceMs;
5505
- const firstLook = !Number.isFinite(sinceMs) && Number.isFinite(acceptedAt);
5506
- if (row.status !== 'done' || !policyMatch || (!isCurrent && !isSince && !firstLook)) continue;
5507
- if (seen.has(row.id)) continue;
5508
- seen.add(row.id);
5509
- out.push({
5510
- id: row.id,
5511
- ref: row.display_id || taskRef(row),
5512
- title: row.title,
5513
- accepted_at: metadata.auto_accepted_at || metadata.accepted_at || null,
5514
- });
5515
- }
5516
- out.sort((a, b) => String(b.accepted_at || '').localeCompare(String(a.accepted_at || '')));
5517
- return out;
5518
- }
5519
-
5520
- function reviewAutoAcceptRollup(taskDb, db, workspaceRoot, previousState, autoAcceptRun) {
5521
- const acceptedNowIds = (autoAcceptRun.results || [])
5522
- .filter(row => row.action === 'accepted')
5523
- .map(row => row.task_id)
5524
- .filter(Boolean);
5525
- const rows = autoAcceptedReviewRowsSince(taskDb, db, workspaceRoot, previousState.last_look_at, acceptedNowIds);
5526
- return {
5527
- count: rows.length,
5528
- items: rows,
5529
- since: previousState.last_look_at || null,
5530
- };
5531
- }
5532
-
5533
5024
  function reviewQueueLimit(args, total) {
5534
5025
  if (hasFlag(args, '--all')) return total;
5535
5026
  const raw = flag(args, '--limit');
@@ -10674,19 +10165,6 @@ function failedOnSameCode(task, verify, codeSha) {
10674
10165
  && cache.command === verify && codeSha && cache.code_sha === codeSha);
10675
10166
  }
10676
10167
 
10677
- function cachedVerifyVerdict(task, root) {
10678
- const metadata = (task && task.metadata) || {};
10679
- const cache = metadata.verify_cache;
10680
- if (!cache || typeof cache !== 'object') return { fresh: false, command: metadata.verify || null };
10681
- // A check run on the job's own code stays fresh until that code changes.
10682
- const currentCode = cache.code_source && cache.code_source !== 'checkout'
10683
- ? checkedCodeSha(task, root)
10684
- : currentHeadSha(task.workspace_root || root);
10685
- const fresh = cache.command === metadata.verify
10686
- && (!cache.code_sha || cache.code_sha === currentCode);
10687
- return { ...cache, fresh: Boolean(fresh) };
10688
- }
10689
-
10690
10168
  // `atris task ready --verify "<cmd>"` already runs the command live and
10691
10169
  // gates on exit 0, but that alone leaves no machine re-runnable trace once
10692
10170
  // the proof text scrolls out of easy reach: storing the exact command on
@@ -188,7 +188,7 @@ async function terminalAtris() {
188
188
  // Auto-wake (the rule)
189
189
  const awake = await ensureAwake(creds.token, biz.businessId);
190
190
  if (!awake) {
191
- console.error(' EC2 computer did not become ready in time. Aborting.');
191
+ console.error(` The cloud computer for "${slug}" did not wake up in time, so nothing ran. Wait a minute, then run the same command again.`);
192
192
  process.exit(1);
193
193
  }
194
194
 
@@ -472,9 +472,11 @@ function dateStamp(now) {
472
472
  if (typeof now === 'string' && /^\d{4}-\d{2}-\d{2}/.test(now)) {
473
473
  return now.slice(0, 10);
474
474
  }
475
- const value = now instanceof Date ? now : new Date(now || Date.now());
476
- if (Number.isNaN(value.getTime())) return new Date().toISOString().slice(0, 10);
477
- return value.toISOString().slice(0, 10);
475
+ const parsed = now instanceof Date ? now : new Date(now || Date.now());
476
+ const value = Number.isNaN(parsed.getTime()) ? new Date() : parsed;
477
+ // Local calendar day. toISOString() is the UTC day, which is already
478
+ // tomorrow in the evening anywhere west of Greenwich.
479
+ return `${value.getFullYear()}-${String(value.getMonth() + 1).padStart(2, '0')}-${String(value.getDate()).padStart(2, '0')}`;
478
480
  }
479
481
 
480
482
  function fileXSearchBrief({ cwd, source, text, now } = {}) {
@@ -262,10 +262,6 @@ function captionTrackCandidates(info = {}) {
262
262
  return candidates;
263
263
  }
264
264
 
265
- function chooseCaptionTrack(info = {}) {
266
- return captionTrackCandidates(info)[0] || null;
267
- }
268
-
269
265
  function formatTimestampFromMs(ms) {
270
266
  const totalSeconds = Math.max(0, Math.floor(Number(ms || 0) / 1000));
271
267
  const hours = Math.floor(totalSeconds / 3600);
@@ -772,8 +768,10 @@ function dateStamp(now) {
772
768
  return now.slice(0, 10);
773
769
  }
774
770
  const value = now instanceof Date ? now : new Date(now || Date.now());
775
- if (Number.isNaN(value.getTime())) return new Date().toISOString().slice(0, 10);
776
- return value.toISOString().slice(0, 10);
771
+ // Local calendar day, the same journal file every other writer uses;
772
+ // toISOString is UTC and files tonight's notes under tomorrow west of Greenwich.
773
+ const day = Number.isNaN(value.getTime()) ? new Date() : value;
774
+ return `${day.getFullYear()}-${String(day.getMonth() + 1).padStart(2, '0')}-${String(day.getDate()).padStart(2, '0')}`;
777
775
  }
778
776
 
779
777
  function firstHeading(notes) {
@@ -2532,9 +2530,12 @@ function searchApplyNow(now) {
2532
2530
  const value = now();
2533
2531
  if (typeof value === 'string' && /^\d{4}-\d{2}-\d{2}/.test(value)) return value;
2534
2532
  const ms = Number(value);
2535
- if (Number.isFinite(ms) && ms > 0) return new Date(ms).toISOString().slice(0, 10);
2533
+ if (Number.isFinite(ms) && ms > 0) return dateStamp(ms);
2536
2534
  return undefined;
2537
2535
  }
2536
+ // The apply stub is dated by the local day, like the notes it follows;
2537
+ // left to itself the apply gate would stamp the UTC day.
2538
+ if (now === undefined || now === null || now instanceof Date || typeof now === 'number') return dateStamp(now);
2538
2539
  return now;
2539
2540
  }
2540
2541
 
@@ -3966,6 +3967,7 @@ async function youtubeCommand(argv = process.argv.slice(3), deps = {}) {
3966
3967
 
3967
3968
  module.exports = {
3968
3969
  DEFAULT_QUERY,
3970
+ searchApplyNow,
3969
3971
  DEFAULT_TIMEOUT_MS,
3970
3972
  parseYoutubeArgs,
3971
3973
  buildYoutubePayload,
package/commands/zen.js CHANGED
@@ -58,8 +58,8 @@ function makeOut(calm) {
58
58
  }
59
59
 
60
60
  function teamLine(n) {
61
- if (!n) return 'no team notes changed.';
62
- return n === 1 ? '1 team note kept as it is.' : `${n} team notes kept as they are.`;
61
+ if (!n) return 'no unsaved team notes.';
62
+ return n === 1 ? '1 unsaved team note left in place.' : `${n} unsaved team notes left in place.`;
63
63
  }
64
64
 
65
65
  function printYours(yours, line, dim) {
@@ -69,8 +69,8 @@ function printYours(yours, line, dim) {
69
69
 
70
70
  function printCheck(result, workspace) {
71
71
  const kept = result.team_files
72
- ? (result.team_files === 1 ? '1 team file kept as it is' : `${result.team_files} team files kept as they are`)
73
- : 'no team files changed';
72
+ ? (result.team_files === 1 ? '1 unsaved team file left in place' : `${result.team_files} unsaved team files left in place`)
73
+ : 'no unsaved team files';
74
74
  const stale = result.fetched === false ? ' (could not reach origin, using last fetch)' : '';
75
75
  const tidy = workspace?.todo.length || 0;
76
76
  if (result.zen && tidy) {