atris 3.30.8 → 3.31.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 (59) hide show
  1. package/AGENTS.md +16 -3
  2. package/FOR_AGENTS.md +81 -0
  3. package/README.md +4 -2
  4. package/atris/atris.md +51 -19
  5. package/atris/skills/README.md +1 -0
  6. package/atris/skills/blocks/SKILL.md +134 -0
  7. package/atris/skills/clawhub/philosophy-of-work/SKILL.md +56 -0
  8. package/atris/skills/youtube/SKILL.md +31 -11
  9. package/atris.md +7 -0
  10. package/ax +367 -93
  11. package/bin/atris.js +317 -155
  12. package/commands/autoland.js +319 -0
  13. package/commands/autopilot.js +94 -4
  14. package/commands/business.js +1 -1
  15. package/commands/clean.js +72 -9
  16. package/commands/codex-goal.js +72 -22
  17. package/commands/computer.js +48 -3
  18. package/commands/gm.js +1 -1
  19. package/commands/harvest.js +179 -0
  20. package/commands/init.js +1 -1
  21. package/commands/land.js +442 -0
  22. package/commands/loop-front.js +122 -4
  23. package/commands/member.js +519 -19
  24. package/commands/mission.js +3659 -282
  25. package/commands/play.js +1 -1
  26. package/commands/pulse.js +65 -7
  27. package/commands/run.js +3 -3
  28. package/commands/strings.js +301 -0
  29. package/commands/task.js +575 -102
  30. package/commands/truth.js +170 -0
  31. package/commands/xp.js +32 -8
  32. package/commands/youtube.js +72 -5
  33. package/decks/README.md +6 -12
  34. package/lib/auto-accept-certified.js +10 -0
  35. package/lib/autoland.js +283 -0
  36. package/lib/context-gatherer.js +0 -8
  37. package/lib/mission-artifact.js +504 -0
  38. package/lib/mission-room.js +846 -0
  39. package/lib/mission-runtime-loop.js +320 -0
  40. package/lib/next-moves.js +212 -6
  41. package/lib/pulse.js +74 -1
  42. package/lib/runner-command.js +20 -8
  43. package/lib/runs-prune.js +242 -0
  44. package/lib/task-proof.js +1 -1
  45. package/package.json +3 -3
  46. package/decks/atris-seed-pitch-v3.json +0 -118
  47. package/decks/atris-seed-pitch-v4-skeleton.json +0 -106
  48. package/decks/atris-seed-pitch-v5.json +0 -109
  49. package/decks/atris-seed-pitch-v6.json +0 -137
  50. package/decks/atris-seed-pitch-v7.json +0 -133
  51. package/decks/mark-pincus-narrative.json +0 -102
  52. package/decks/mark-pincus-sourcery.json +0 -94
  53. package/decks/yash-applied-compute-detailed.json +0 -150
  54. package/decks/yash-applied-compute-generalist.json +0 -82
  55. package/decks/yash-applied-compute-narrative.json +0 -54
  56. package/lib/ax-chat-input.js +0 -164
  57. package/lib/ax-goal.js +0 -307
  58. package/lib/ax-prefs.js +0 -63
  59. package/lib/ax-shimmer.js +0 -63
package/commands/task.js CHANGED
@@ -150,8 +150,8 @@ atris task - durable local task state (SQLite, gitignored)
150
150
  atris task finish <id> --proof "..." Legacy alias for done with proof
151
151
  atris task review <id> --reward <n> [--verify "<cmd>"]
152
152
  Write review event + RSI episode
153
- atris task reviews [--limit <n>] Show certified Review items for human accept/revise
154
- atris task reviews --group-by <tag|owner|source> Cluster ready-to-land work for fast triage
153
+ atris task reviews [--all|--limit <n>] [--verbose] Show certified Review items for human accept/revise
154
+ atris task reviews --group-by <tag|owner|source> Cluster approval-ready work for fast triage
155
155
  atris task accept-group <key>=<value> --spot-check K --confirm-human-accept --as <you> --verified <ids>
156
156
  Accept a whole cluster; career XP only on the K you verified
157
157
  atris task status [--json] [--history] Compact live status for web/Swarlo
@@ -284,6 +284,63 @@ function refreshCareerXpAfterReview(reviewed) {
284
284
  return refreshCareerXpProjection(reviewed?.episode?.workspace_root);
285
285
  }
286
286
 
287
+ function compactCareerXpProjection(projection) {
288
+ if (!projection || typeof projection !== 'object') return projection;
289
+ const progress = projection.next_level_progress && typeof projection.next_level_progress === 'object'
290
+ ? {
291
+ level: projection.next_level_progress.level ?? null,
292
+ next_level: projection.next_level_progress.next_level ?? null,
293
+ current_xp: projection.next_level_progress.current_xp ?? null,
294
+ required_xp: projection.next_level_progress.required_xp ?? null,
295
+ remaining_xp: projection.next_level_progress.remaining_xp ?? null,
296
+ percent: projection.next_level_progress.percent ?? null,
297
+ }
298
+ : null;
299
+ const latest = projection.latest_accepted_proof && typeof projection.latest_accepted_proof === 'object'
300
+ ? {
301
+ label: projection.latest_accepted_proof.label ?? null,
302
+ receipt_id: projection.latest_accepted_proof.receipt_id ?? null,
303
+ source: projection.latest_accepted_proof.source ?? null,
304
+ source_task_id: projection.latest_accepted_proof.source_task_id ?? null,
305
+ title: projection.latest_accepted_proof.title ?? null,
306
+ xp: projection.latest_accepted_proof.xp ?? null,
307
+ reward: projection.latest_accepted_proof.reward ?? null,
308
+ accepted_at: projection.latest_accepted_proof.accepted_at ?? null,
309
+ goal: projection.latest_accepted_proof.goal ?? null,
310
+ }
311
+ : null;
312
+ const integrity = projection.integrity && typeof projection.integrity === 'object'
313
+ ? {
314
+ status: projection.integrity.status ?? null,
315
+ receipts_count: projection.integrity.receipts_count ?? projection.receipts_count ?? null,
316
+ head_hash: projection.integrity.head_hash ?? null,
317
+ }
318
+ : null;
319
+ return {
320
+ schema: projection.schema,
321
+ compact: true,
322
+ total_xp: projection.total_xp ?? projection.total_agent_xp ?? projection.agent_xp ?? null,
323
+ agent_xp: projection.agent_xp ?? projection.total_agent_xp ?? null,
324
+ today_xp: projection.today_xp ?? projection.today_agent_xp ?? null,
325
+ collected_receipts: projection.collected_receipts ?? 0,
326
+ receipts_count: projection.receipts_count ?? null,
327
+ level: projection.level ?? null,
328
+ leaderboard_eligible: projection.leaderboard_eligible ?? null,
329
+ integrity_status: projection.integrity_status ?? integrity?.status ?? null,
330
+ next_level_progress: progress,
331
+ latest_accepted_proof: latest,
332
+ integrity,
333
+ omitted: [
334
+ 'contribution_graph',
335
+ 'career',
336
+ 'earning_model',
337
+ 'local_activity',
338
+ 'ledger',
339
+ 'sources',
340
+ ],
341
+ };
342
+ }
343
+
287
344
  function jsonModeActive() {
288
345
  return process.argv.includes('--json');
289
346
  }
@@ -678,47 +735,103 @@ function reviewSummary(task, payload = {}) {
678
735
  || careerText.includes('agent xp')
679
736
  ) {
680
737
  if (task.status === 'done') {
681
- return `Landed AgentXP result for ${plainTitle}.`;
738
+ return `Completed AgentXP result for ${plainTitle}.`;
682
739
  }
683
740
  if (task.status === 'review') {
684
- return `${plainTitle} is ready to land; land only if the proof is real.`;
741
+ return `${plainTitle}: proof is ready for human approval; approve only if the evidence is real.`;
685
742
  }
686
- return `This explains what landing ${plainTitle} would make real for AgentXP.`;
743
+ return `This explains the AgentXP result ${plainTitle} would make real.`;
687
744
  }
688
745
  if (task.status === 'done') {
689
- return `Landed result for ${plainTitle}.`;
746
+ return `Completed result for ${plainTitle}.`;
690
747
  }
691
748
  if (task.status === 'review') {
692
- return `${plainTitle} is ready to land; land it or send it back.`;
749
+ return `${plainTitle}: review the completed result, then approve or ask for rework.`;
693
750
  }
694
- return `This explains what landing ${plainTitle} would make real.`;
751
+ return `This explains what ${plainTitle} would make real.`;
695
752
  }
696
753
 
697
754
  function titleToResultText(title) {
698
755
  const text = String(title || 'this task').replace(/\s+/g, ' ').trim();
699
- if (!text) return 'This task is ready to review.';
756
+ if (!text) return 'Completed this task.';
757
+ const resultSentence = (prefix, body) => {
758
+ const clean = String(body || '').replace(/\s+/g, ' ').trim().replace(/[.!?]+$/, '');
759
+ return clean ? `${prefix} ${clean}.` : `${prefix}.`;
760
+ };
761
+ const missionXp = text.match(/^Mission XP:\s*(.+)$/i);
762
+ if (missionXp) {
763
+ const missionResult = titleToResultText(missionXp[1]).replace(/[.!?]+$/, '');
764
+ return resultSentence('Completed mission work:', missionResult.replace(/^Completed:\s*/i, ''));
765
+ }
700
766
  const [first, ...restParts] = text.split(' ');
701
767
  const rest = restParts.join(' ');
768
+ const firstLower = String(first || '').toLowerCase();
769
+ const compound = rest.match(/^and\s+([a-z]+)\s+(.+)$/i);
770
+ const compoundPast = compound ? ({
771
+ audit: { close: 'Audited and closed' },
772
+ decide: { start: 'Decided and started' },
773
+ prune: { compress: 'Pruned and compressed' },
774
+ }[firstLower] || {})[String(compound[1] || '').toLowerCase()] : null;
775
+ if (compoundPast && compound[2]) return resultSentence(compoundPast, compound[2]);
702
776
  const past = {
703
777
  add: 'Added',
704
778
  approve: 'Prepared approval for',
779
+ audit: 'Audited',
780
+ batch: 'Batched',
705
781
  build: 'Built',
706
782
  clean: 'Cleaned',
707
783
  create: 'Created',
784
+ decide: 'Decided',
708
785
  design: 'Designed',
709
786
  document: 'Documented',
787
+ find: 'Found',
710
788
  fix: 'Fixed',
789
+ heal: 'Healed',
790
+ ignore: 'Ignored',
791
+ infer: 'Inferred',
792
+ keep: 'Kept',
711
793
  make: 'Made',
794
+ number: 'Numbered',
795
+ prune: 'Pruned',
796
+ reconcile: 'Reconciled',
797
+ refresh: 'Refreshed',
798
+ render: 'Rendered',
799
+ repair: 'Repaired',
712
800
  replace: 'Replaced',
801
+ respect: 'Respected',
713
802
  route: 'Routed',
714
803
  run: 'Ran',
715
804
  ship: 'Shipped',
805
+ show: 'Showed',
806
+ stop: 'Stopped',
807
+ summarize: 'Summarized',
808
+ suppress: 'Suppressed',
809
+ trim: 'Trimmed',
716
810
  update: 'Updated',
811
+ use: 'Used',
717
812
  validate: 'Validated',
718
813
  wire: 'Wired',
719
- }[String(first || '').toLowerCase()];
720
- if (past && rest) return `${past} ${rest}.`;
721
- return `${text} is ready to review.`;
814
+ }[firstLower];
815
+ if (past && rest) return resultSentence(past, rest);
816
+ return resultSentence('Completed:', text);
817
+ }
818
+
819
+ function proofToReasonText(proof) {
820
+ const text = String(proof || '').replace(/\s+/g, ' ').trim();
821
+ if (!text) return '';
822
+ const label = /\b(?:Product proof|Why it matters)\s*:\s*/i.exec(text);
823
+ if (!label) return '';
824
+ const rest = text.slice(label.index + label[0].length);
825
+ const stop = rest.search(/(?:^|[.;]\s+)\b(?:Checks?|Mission receipt|Receipt|Landing|Changed|How I checked|What I tested|Saved|Decision)\s*:/i);
826
+ let section = (stop >= 0 ? rest.slice(0, stop) : rest)
827
+ .replace(/^(?:[-:]\s*)+/, '')
828
+ .replace(/\s+/g, ' ')
829
+ .replace(/\s*[;,:-]\s*$/, '')
830
+ .trim();
831
+ const sentenceStop = section.search(/[.!?]\s+/);
832
+ if (sentenceStop >= 0) section = section.slice(0, sentenceStop + 1).trim();
833
+ if (!section) return '';
834
+ return /[.!?]$/.test(section) ? section : `${section}.`;
722
835
  }
723
836
 
724
837
  function titleToReasonText(task, proof = '') {
@@ -737,35 +850,102 @@ function titleToReasonText(task, proof = '') {
737
850
  return 'It keeps real-world side effects behind a clear human decision.';
738
851
  }
739
852
  if (/\btest|self-test|harness|verifier|proof\b/.test(text)) {
740
- return 'It makes the check repeatable before the work lands.';
853
+ return 'It gives the human a repeatable check before approval.';
741
854
  }
742
- return 'It closes the user-visible gap named by the task.';
855
+ return 'It turns the task title into a concrete result the human can approve.';
743
856
  }
744
857
 
745
858
  function proofToHumanCheck(proof) {
746
859
  const text = String(proof || '').replace(/\s+/g, ' ').trim();
747
860
  if (!text) return 'Proof is attached below.';
748
861
  const lower = text.toLowerCase();
862
+ const passSignal = /\b(?:pass(?:es|ed|ing)?|ok|green|succeeded|verified)\b/.test(lower);
863
+ const verifierCommandSignal = /\b(?:npm|node|git|atris|npx|pnpm|yarn|python3?|pytest|bash|sh|tsc|vitest|curl|gh|rg)\s+\S+/.test(lower);
749
864
  if (/\b(?:atris\s+)?task\s+(?:show|page)\b/.test(lower) && /\b(?:print|prints|printed|render|renders|rendered|show|shows|showed)\b/.test(lower)) {
750
865
  return 'I opened the real task screen and checked the receipt renders before raw proof.';
751
866
  }
752
- if (/git\s+diff\s+--check/.test(lower) && /\bpass(?:ed)?\b/.test(lower)) {
867
+ if (/git\s+diff\s+--check/.test(lower) && passSignal) {
753
868
  if (/node\s+--test/.test(lower)) return 'I ran the behavior check and the diff cleanliness check.';
754
869
  return 'I ran the diff cleanliness check.';
755
870
  }
756
- if (/\btypecheck\b/.test(lower) && /\bpass(?:ed)?\b/.test(lower)) return 'I ran the typecheck.';
757
- if (/\bbuild\b/.test(lower) && /\bpass(?:ed)?\b/.test(lower)) return 'I ran the build check.';
758
- if (/\btest\b/.test(lower) && /\bpass(?:ed)?\b/.test(lower)) return 'I ran the verifier named in the proof.';
759
- if (/\bvalidat(?:e|ion|ed)\b/.test(lower) && /\bpass(?:ed)?\b/.test(lower)) return 'I ran the validation check.';
871
+ if (/\btypecheck\b/.test(lower) && passSignal) return 'I ran the typecheck.';
872
+ if (/\bbuild\b/.test(lower) && passSignal) return 'I ran the build check.';
873
+ if (/\btest\b/.test(lower) && passSignal) return 'I ran the verifier named in the proof.';
874
+ if (/\bvalidat(?:e|ion|ed)\b/.test(lower) && passSignal) return 'I ran the validation check.';
875
+ if (verifierCommandSignal && /\b(?:print|prints|printed|show|shows|showed|report|reports|reported|return|returns|returned)\b/.test(lower)) {
876
+ return 'I checked the verifier output named in the proof.';
877
+ }
878
+ if (verifierCommandSignal && passSignal) return 'I ran the verifier named in the proof.';
879
+ if (/atris\/runs\/[^\s),.;:]+\.json/.test(text)) {
880
+ return passSignal
881
+ ? 'I inspected the passing receipt named in the proof.'
882
+ : 'I inspected the receipt named in the proof.';
883
+ }
884
+ if (/(?:^|\s)(?:\.{0,2}\/)?scripts\/[^\s),.;:]+\.(?:js|mjs|cjs|py|sh)\b/.test(text)) {
885
+ return 'I inspected the verifier artifact named in the proof.';
886
+ }
760
887
  if (/\breview(?:ed)?\b/.test(lower)) return 'Review proof is attached below.';
761
888
  return 'Proof is attached below.';
762
889
  }
763
890
 
891
+ function taskReviewFormatCommandList(commands) {
892
+ const list = Array.isArray(commands)
893
+ ? commands.map(command => String(command || '').trim()).filter(Boolean)
894
+ : [];
895
+ if (!list.length) return '';
896
+ if (list.length > 1) {
897
+ return list.map((command, index) => `command ${index + 1}: ${command}`).join('; ');
898
+ }
899
+ return list[0];
900
+ }
901
+
902
+ function taskReviewProseCheckLabels(proof) {
903
+ const text = String(proof || '').replace(/\s+/g, ' ').trim();
904
+ if (!text) return [];
905
+ const lower = text.toLowerCase();
906
+ const hasPass = /\b(?:pass(?:es|ed|ing)?|green|verified|current|clean|complete|completed)\b/.test(lower);
907
+ if (!hasPass) return [];
908
+ const labels = [];
909
+ const add = label => {
910
+ if (!labels.includes(label)) labels.push(label);
911
+ };
912
+ if (/\bhelp output\b/.test(lower)) add('help output');
913
+ if (/\b(?:wiki verify|public wiki verify|loop wiki)\b/.test(lower)) add('wiki verification');
914
+ if (/\bclean dry-run\b/.test(lower)) add('clean dry-run');
915
+ if (/\b(?:test slice|regression run|tests?|test)\b.*\bpass(?:es|ed|ing)?\b/.test(lower)
916
+ || /\bpass(?:es|ed|ing)?\b.*\b(?:test slice|regression run|tests?|test)\b/.test(lower)) {
917
+ add('passing tests');
918
+ }
919
+ if (/\bverifier\b.*\bpass(?:es|ed|ing)?\b/.test(lower)
920
+ || /\bpass(?:es|ed|ing)?\b.*\bverifier\b/.test(lower)) {
921
+ add('passing verifier');
922
+ }
923
+ if (/\bmission\b.*\bcomplete(?:d)?\b/.test(lower)) add('completed mission');
924
+ if (/\bgit diff --check\b/.test(lower) || /\bdiff cleanliness\b/.test(lower)) add('diff cleanliness');
925
+ return labels;
926
+ }
927
+
764
928
  function taskReviewLandingTested(proof) {
765
- const commands = taskReviewEvidenceCommands(proof, 3);
766
- if (commands.length) return `I ran: ${commands.join(' | ')}.`;
767
- const paths = taskReviewEvidencePaths(proof, 3);
768
- if (paths.length) return `I inspected: ${paths.join(', ')}.`;
929
+ const maxVisible = 3;
930
+ const commands = taskReviewEvidenceCommands(proof, 20);
931
+ if (commands.length) {
932
+ return commands.length === 1
933
+ ? 'I ran the listed check for this result.'
934
+ : 'I ran the listed checks for this result.';
935
+ }
936
+ const paths = taskReviewEvidencePaths(proof, 20);
937
+ if (paths.length) {
938
+ return paths.length === 1
939
+ ? 'I inspected the artifact named in the proof.'
940
+ : `I inspected ${paths.length} artifacts named in the proof.`;
941
+ }
942
+ const proseChecks = taskReviewProseCheckLabels(proof);
943
+ if (proseChecks.length) {
944
+ const visible = proseChecks.slice(0, maxVisible);
945
+ const omitted = proseChecks.length - visible.length;
946
+ const suffix = omitted > 0 ? `, and ${omitted} more ${omitted === 1 ? 'check' : 'checks'}` : '';
947
+ return `I checked: ${visible.join(', ')}${suffix}.`;
948
+ }
769
949
  return String(proof || '').trim() ? 'I attached the proof below.' : 'No verifier command recorded yet.';
770
950
  }
771
951
 
@@ -793,16 +973,16 @@ function taskReviewLanding(task, review = {}, payload = {}) {
793
973
  || payload.reason || payload.why || metadata.result_reason || metadata.review_reason || metadata.why_it_matters;
794
974
  return {
795
975
  happened: clipStatusText(explicitHappened || titleToResultText(task.title), 220),
796
- reason: clipStatusText(explicitReason || titleToReasonText(task, proof), 220),
976
+ reason: clipStatusText(explicitReason || proofToReasonText(proof) || titleToReasonText(task, proof), 220),
797
977
  checked: clipStatusText(explicitChecked || proofToHumanCheck(proof), 220),
798
978
  tested: clipStatusText(explicitTested || taskReviewLandingTested(proof), 260),
799
979
  decision: clipStatusText(explicitDecision || (task.status === 'done'
800
- ? 'Landed. No action needed unless this regresses.'
980
+ ? 'Done. No action needed unless this regresses.'
801
981
  : approvalStatus === 'revise'
802
982
  ? 'Rework requested. Fix the note, then send a new receipt.'
803
983
  : approvalStatus === 'pending' && !agentCertified
804
- ? 'Needs one more check before landing; send it back if the receipt misses the point.'
805
- : 'Land it if this matches the request; send it back if not.'), 220),
984
+ ? 'Needs one more check; ask for rework if the receipt misses the point.'
985
+ : 'Approve if this matches the request; ask for rework if not.'), 220),
806
986
  };
807
987
  }
808
988
 
@@ -815,13 +995,20 @@ function taskReviewResult(task, review = {}, payload = {}) {
815
995
  changed: landing.happened,
816
996
  reason: landing.reason,
817
997
  checked: landing.checked,
818
- saved: clipStatusText(explicitSaved || (task.status === 'done'
819
- ? `Landed as ${ref}.`
820
- : `Ready to land as ${ref}.`), 180),
998
+ saved: clipStatusText(explicitSaved || taskReviewSavedText(task, review, ref), 180),
821
999
  accept: landing.decision,
822
1000
  };
823
1001
  }
824
1002
 
1003
+ function taskReviewSavedText(task, review = {}, ref = taskRef(task)) {
1004
+ if (task.status === 'done') return `Result accepted as ${ref}.`;
1005
+ const metadata = task.metadata || {};
1006
+ const agentCertified = review.agent_certified === true || metadata.agent_certified === true;
1007
+ if (task.status === 'review' && agentCertified) return `Result is ready for human approval as ${ref}.`;
1008
+ if (task.status === 'review') return `Completed result saved for review as ${ref}.`;
1009
+ return `Work record saved as ${ref}.`;
1010
+ }
1011
+
825
1012
  function taskReviewSummary(task) {
826
1013
  const reviewed = (task.events || []).slice().reverse().find(e => e.event_type === 'reviewed' || e.event_type === 'proof_ready' || e.event_type === 'revision_requested');
827
1014
  const payload = reviewed && reviewed.payload || {};
@@ -978,6 +1165,8 @@ function buildTaskStreams(tasks, goals) {
978
1165
  if (column === 'doing') stream.doing_count += 1;
979
1166
  if (column === 'blocked') stream.blocked_count += 1;
980
1167
  if (column === 'review') stream.review_count += 1;
1168
+ const review = task.review || {};
1169
+ const metadata = task.metadata || {};
981
1170
  stream.tasks.push({
982
1171
  id: task.id,
983
1172
  title: task.title,
@@ -987,7 +1176,7 @@ function buildTaskStreams(tasks, goals) {
987
1176
  assigned_to: taskAssignee(task),
988
1177
  parent_task_id: task.lineage && task.lineage.parent_task_id || null,
989
1178
  child_task_ids: task.lineage && task.lineage.child_task_ids || [],
990
- proof: task.review && task.review.proof || null,
1179
+ proof: review.proof || metadata.latest_agent_proof || null,
991
1180
  });
992
1181
  }
993
1182
  for (const goal of goals) {
@@ -1292,17 +1481,142 @@ function taskReviewCommandLooksSpecific(command) {
1292
1481
  if (/^atris\s+task\s+\w+\s*$/i.test(text)) return false;
1293
1482
  if (/^atris\s+task\s+(?:accept|auto-accept-certified)\b/i.test(text)) return false;
1294
1483
  if (/^atris[/.]/i.test(text)) return false;
1484
+ if (/^atris-\S+/i.test(text)) return false;
1295
1485
  if (/^atris\s+task\s+\w+\s+json\s*$/i.test(text) && !/--json\b/i.test(text)) return false;
1296
1486
  if (/^atris\s+(?:command|review-chat|smoke|temp)\b/i.test(text)) return false;
1297
1487
  if (/^(?:npm|npx|pnpm|yarn|python3?|pytest|bash|sh|tsc|vitest|curl|gh|rg|git)\s+commands?\b/i.test(text)) return false;
1298
1488
  if (/^(?:npm|pnpm|yarn)\s+(?:tests|checks?)$/i.test(text)) return false;
1299
1489
  if (/^(?:git|gh|rg|curl|bash|sh|tsc)\s+(?:tests?|checks?)$/i.test(text)) return false;
1300
- if (/\s+(?:and|then)\s+\S+/i.test(text)) return false;
1490
+ if (/\s+(?:and|then)\s+\S+/i.test(taskReviewWithoutQuotedSegments(text))) return false;
1301
1491
  if (/^node\s+(?!-|\S*(?:[/.]))/i.test(text)) return false;
1302
1492
  if (/^node\s+--test\s+[\w-]+(?:\s+[\w-]+)+$/i.test(text)) return false;
1303
1493
  return true;
1304
1494
  }
1305
1495
 
1496
+ function taskReviewUnescapedQuoteCount(text, quote) {
1497
+ let count = 0;
1498
+ const source = String(text || '');
1499
+ for (let index = 0; index < source.length; index += 1) {
1500
+ if (source[index] === quote && source[index - 1] !== '\\') count += 1;
1501
+ }
1502
+ return count;
1503
+ }
1504
+
1505
+ function taskReviewCommandStartsInsideQuote(clause, commandIndex) {
1506
+ const before = String(clause || '').slice(0, Math.max(0, commandIndex));
1507
+ return taskReviewUnescapedQuoteCount(before, "'") % 2 === 1
1508
+ || taskReviewUnescapedQuoteCount(before, '"') % 2 === 1;
1509
+ }
1510
+
1511
+ function taskReviewWithoutQuotedSegments(text) {
1512
+ const source = String(text || '');
1513
+ let quote = null;
1514
+ let out = '';
1515
+ for (let index = 0; index < source.length; index += 1) {
1516
+ const char = source[index];
1517
+ if ((char === "'" || char === '"') && source[index - 1] !== '\\') {
1518
+ quote = quote === char ? null : (!quote ? char : quote);
1519
+ out += ' ';
1520
+ continue;
1521
+ }
1522
+ out += quote ? ' ' : char;
1523
+ }
1524
+ return out;
1525
+ }
1526
+
1527
+ function taskReviewTrimOutsideQuotedTail(text, pattern) {
1528
+ const source = String(text || '');
1529
+ const flags = pattern.flags.includes('g') ? pattern.flags : `${pattern.flags}g`;
1530
+ const scanner = new RegExp(pattern.source, flags);
1531
+ let match;
1532
+ while ((match = scanner.exec(source)) !== null) {
1533
+ if (!taskReviewCommandStartsInsideQuote(source, match.index)) {
1534
+ return source.slice(0, match.index);
1535
+ }
1536
+ if (match[0] === '') scanner.lastIndex += 1;
1537
+ }
1538
+ return source;
1539
+ }
1540
+
1541
+ function taskReviewTrimTrailingOutsideQuote(text) {
1542
+ let clean = String(text || '');
1543
+ while (clean.length > 0) {
1544
+ const index = clean.length - 1;
1545
+ if (!/[\]),.;:]/.test(clean[index])) break;
1546
+ if (taskReviewCommandStartsInsideQuote(clean, index)) break;
1547
+ clean = clean.slice(0, index);
1548
+ }
1549
+ return clean;
1550
+ }
1551
+
1552
+ function taskReviewCleanEvidenceCommand(command) {
1553
+ let clean = String(command || '');
1554
+ const tailPatterns = [
1555
+ /\s+from\s+[^,;]*(?:showed|shows|showing|returned|returns|printed|prints|wrote|writes|reported|reports)\b.*$/i,
1556
+ /\s+(?:showed|shows|showing|returned|returns|printed|prints|wrote|writes|reported|reports)\b.*$/i,
1557
+ /\.\s+(?:Reward remains|No human|Human accept|AgentXP|XP)\b.*$/i,
1558
+ /\s+with\s+\d+\s+(?:passing\s+)?(?:tests?|checks?|passes|passed|pass|ok|clean)\b.*$/i,
1559
+ /\s+\((?:passed|ok|clean|failed|errored|timed out|succeeded|succeeds|successful|confirmed)\)$/i,
1560
+ /\s+\(?(?:exit|status|code)\s+\d+\)?$/i,
1561
+ /\s+\(?(?:passed|ok|clean|failed|errored|timed out|succeeded|succeeds|successful|confirmed)\s+\d+\/\d+(?:[,.]\s+.*)?$/i,
1562
+ /\s+\(?(?:passed|ok|clean|failed|errored|timed out|succeeded|succeeds|successful|confirmed)\s+\d+\/\d+(?:\s+(?:tests?|checks?|passed|pass|ok|clean|failed|failures?))?$/i,
1563
+ /\s+\(?(?:passed|ok|clean|failed|errored|timed out|succeeded|succeeds|successful|confirmed)(?:[,.]\s+.*|\s+and\b.*)$/i,
1564
+ /\s+\(?(?:passed|ok|clean|failed|errored|timed out|succeeded|succeeds|successful|confirmed)\s+with\b.*$/i,
1565
+ /,\s+(?:command suite|mission-status tests?|live\b|focused suite|clean dry-run|brain compile)\b.*$/i,
1566
+ /\.\s+(?:The|This|It|Focused|Dogfood|Landing|Product|Review|Receipt|Human|No|Note)\b.*$/i,
1567
+ /\s+\(?\d+\s+(?:passes|passed|pass|ok|clean|tests?|checks?)\)?$/i,
1568
+ /\s+only$/i,
1569
+ /\s+\(?(?:passed|ok|clean|failed|errored|timed out|succeeded|succeeds|successful|confirmed)(?:[.:]\s+.*|\s+after\b.*)$/i,
1570
+ /\s+\(?(?:passed|ok|clean|failed|errored|timed out|succeeded|succeeds|successful|confirmed)\)?$/i,
1571
+ /\s+\(?\d+\/\d+(?:\s+(?:tests?|checks?|passed|pass|ok|clean|failed|failures?))?\)?$/i,
1572
+ /\s+\d+\/\d+(?:\s+(?:tests?|checks?|passed|pass|ok|clean|failed|failures?))?$/i,
1573
+ ];
1574
+ for (const pattern of tailPatterns) {
1575
+ clean = taskReviewTrimOutsideQuotedTail(clean, pattern);
1576
+ clean = taskReviewTrimTrailingOutsideQuote(clean);
1577
+ }
1578
+ return clean.replace(/\s+/g, ' ').trim();
1579
+ }
1580
+
1581
+ function taskReviewSplitEvidenceClauses(source, commandStart) {
1582
+ const text = String(source || '');
1583
+ const hardBoundaryPattern = /^(?:;\s*|\n\s*|\s+&&\s+)/;
1584
+ const softBoundaryPattern = new RegExp(`^(?:,\\s+|\\.\\s+|\\s+and\\s+|\\s+then\\s+|\\.\\s+(?:focused|full|behavior|verifier)[^\\n.;:]{0,80}:\\s+)(?=${commandStart})`, 'i');
1585
+ const clauses = [];
1586
+ let clause = '';
1587
+ let quote = null;
1588
+ for (let index = 0; index < text.length;) {
1589
+ const char = text[index];
1590
+ if ((char === "'" || char === '"') && text[index - 1] !== '\\') {
1591
+ if (quote === char) quote = null;
1592
+ else if (!quote) quote = char;
1593
+ clause += char;
1594
+ index += 1;
1595
+ continue;
1596
+ }
1597
+ if (!quote) {
1598
+ const rest = text.slice(index);
1599
+ const boundary = rest.match(hardBoundaryPattern) || rest.match(softBoundaryPattern);
1600
+ if (boundary) {
1601
+ if (clause.trim()) clauses.push(clause);
1602
+ clause = '';
1603
+ index += boundary[0].length;
1604
+ continue;
1605
+ }
1606
+ }
1607
+ clause += char;
1608
+ index += 1;
1609
+ }
1610
+ if (clause.trim()) clauses.push(clause);
1611
+ return clauses;
1612
+ }
1613
+
1614
+ function taskReviewCommandIsDescribedSubject(clause, commandIndex) {
1615
+ const before = String(clause || '').slice(Math.max(0, commandIndex - 80), commandIndex);
1616
+ return /\b(?:contains?|displays?|extracts?|lists?|mentions?|names?|prints?|renders?|shows?)\s+(?:only\s+)?$/i.test(before)
1617
+ || /\b(?:including|includes?|like|such as|for example|e\.g\.)\s+$/i.test(before);
1618
+ }
1619
+
1306
1620
  function taskReviewEvidenceCommands(text, limit = 8) {
1307
1621
  const source = String(text || '').trim();
1308
1622
  if (!source) return [];
@@ -1310,17 +1624,14 @@ function taskReviewEvidenceCommands(text, limit = 8) {
1310
1624
  const envPrefix = '(?:(?:[A-Z_][A-Z0-9_]*=[^\\s,;|]+)\\s+)*';
1311
1625
  const prosePrefix = '(?:(?:rechecked|reran|re-run|run|verified|validated|passed|validation(?:\\s+passed)?|verification(?:\\s+passed)?|focused|live|scoped|installed|direct|full|current|fresh|then|and|commands?|checks?)[:\\s]+)*';
1312
1626
  const commandStart = `${prosePrefix}${envPrefix}${commandWord}\\b`;
1313
- const commandStartPattern = new RegExp(`(^|[^\\w./-])(${commandStart})`, 'i');
1627
+ const commandStartPattern = new RegExp(`(^|[^\\w./=-])(${commandStart})`, 'i');
1314
1628
  const commandStartInnerPattern = new RegExp(`${envPrefix}${commandWord}\\b`, 'i');
1315
- const commandBoundaryPattern = new RegExp(`(?:;\\s*|\\n\\s*|\\s+&&\\s+|,\\s+|\\.\\s+|\\s+and\\s+|\\s+then\\s+)(?=${commandStart})`, 'gi');
1316
- const clauses = source
1629
+ const normalized = source
1317
1630
  .replace(/\r/g, '\n')
1318
1631
  .replace(/```[ \t]*(?:bash|sh|shell|zsh|console|text|txt)?[ \t]*\n/gi, '\n')
1319
1632
  .replace(/```/g, '\n')
1320
- .replace(/`/g, '')
1321
- .replace(/(?:;\s*|\n\s*|\s+&&\s+)/g, '\n')
1322
- .replace(commandBoundaryPattern, '\n')
1323
- .split('\n');
1633
+ .replace(/`/g, '');
1634
+ const clauses = taskReviewSplitEvidenceClauses(normalized, commandStart);
1324
1635
  const out = [];
1325
1636
  const seen = new Set();
1326
1637
  for (const clause of clauses) {
@@ -1330,22 +1641,13 @@ function taskReviewEvidenceCommands(text, limit = 8) {
1330
1641
  const raw = clause.slice(start.index + Math.max(0, commandStartOffset));
1331
1642
  const prefix = raw.match(commandStartInnerPattern);
1332
1643
  const commandOffset = prefix && prefix.index != null ? prefix.index : 0;
1644
+ const commandIndex = start.index + Math.max(0, commandStartOffset) + Math.max(0, commandOffset);
1645
+ if (
1646
+ taskReviewCommandStartsInsideQuote(clause, commandIndex)
1647
+ || taskReviewCommandIsDescribedSubject(clause, commandIndex)
1648
+ ) continue;
1333
1649
  const command = raw.slice(Math.max(0, commandOffset));
1334
- const clean = command
1335
- .replace(/\s+from\s+[^,;]*(?:showed|shows|showing|returned|returns|printed|prints|wrote|writes|reported|reports)\b.*$/i, '')
1336
- .replace(/\s+(?:showed|shows|showing|returned|returns|printed|prints|wrote|writes|reported|reports)\b.*$/i, '')
1337
- .replace(/\.\s+(?:Reward remains|No human|Human accept|AgentXP|XP)\b.*$/i, '')
1338
- .replace(/\s+\((?:passed|ok|clean|failed|errored|timed out|succeeded|succeeds|successful|confirmed)\)$/i, '')
1339
- .replace(/\s+\(?(?:exit|status|code)\s+\d+\)?$/i, '')
1340
- .replace(/[\]),.;:]+$/g, '')
1341
- .replace(/\s+\(?(?:passed|ok|clean|failed|errored|timed out|succeeded|succeeds|successful|confirmed)\s+\d+\/\d+(?:\s+(?:tests?|checks?|passed|pass|ok|clean|failed|failures?))?$/i, '')
1342
- .replace(/\s+\(?(?:passed|ok|clean|failed|errored|timed out|succeeded|succeeds|successful|confirmed)(?:[.:]\s+.*|\s+after\b.*)$/i, '')
1343
- .replace(/\s+\(?(?:passed|ok|clean|failed|errored|timed out|succeeded|succeeds|successful|confirmed)\)?$/i, '')
1344
- .replace(/\s+\(?\d+\/\d+(?:\s+(?:tests?|checks?|passed|pass|ok|clean|failed|failures?))?\)?$/i, '')
1345
- .replace(/\s+\d+\/\d+(?:\s+(?:tests?|checks?|passed|pass|ok|clean|failed|failures?))?$/i, '')
1346
- .replace(/[\]),.;:]+$/g, '')
1347
- .replace(/\s+/g, ' ')
1348
- .trim();
1650
+ const clean = taskReviewCleanEvidenceCommand(command);
1349
1651
  if (!taskReviewCommandLooksSpecific(clean) || seen.has(clean.toLowerCase())) continue;
1350
1652
  seen.add(clean.toLowerCase());
1351
1653
  out.push(clean);
@@ -1365,13 +1667,24 @@ function taskReviewRecentThread(task, limit = 4) {
1365
1667
  .filter(message => message.content);
1366
1668
  }
1367
1669
 
1670
+ function taskReviewObjective(task) {
1671
+ const metadata = task && task.metadata || {};
1672
+ return task && (
1673
+ metadata.task_goal
1674
+ || task.title
1675
+ || metadata.goal_objective
1676
+ || metadata.objective
1677
+ || task.objective
1678
+ ) || '';
1679
+ }
1680
+
1368
1681
  function taskReviewVerificationFocus(task) {
1369
1682
  const review = task && task.review || {};
1370
1683
  const metadata = task && task.metadata || {};
1371
1684
  const proof = review.proof || metadata.latest_agent_proof || '';
1372
1685
  const lesson = review.lesson || metadata.latest_agent_lesson || '';
1373
1686
  const nextTask = review.next_task || metadata.latest_agent_next_task || '';
1374
- const objective = task && (task.objective || metadata.task_goal || metadata.goal_objective || metadata.objective) || '';
1687
+ const objective = taskReviewObjective(task);
1375
1688
  const evidenceText = [proof].filter(Boolean).join('\n');
1376
1689
  return {
1377
1690
  objective: taskReviewClip(objective, 260) || null,
@@ -1388,7 +1701,7 @@ function taskReviewSpecificCodexPrompt(task, focus, actor) {
1388
1701
  const title = taskReviewClip(task && task.title, 180);
1389
1702
  const proof = focus && focus.proof_claim ? ` Proof: ${taskReviewClip(focus.proof_claim, 1800)}` : '';
1390
1703
  const commands = focus && focus.commands_to_verify && focus.commands_to_verify.length
1391
- ? ` Commands: ${focus.commands_to_verify.join(' | ')}.`
1704
+ ? ` Commands: ${taskReviewFormatCommandList(focus.commands_to_verify)}.`
1392
1705
  : '';
1393
1706
  const files = focus && focus.files_to_inspect && focus.files_to_inspect.length
1394
1707
  ? ` Files/artifacts: ${focus.files_to_inspect.join(', ')}.`
@@ -1475,7 +1788,7 @@ function taskReviewChatContract(task, { reviewer = 'codex-review', allowCertifie
1475
1788
  const proof = review.proof || metadata.latest_agent_proof || '';
1476
1789
  const lesson = review.lesson || metadata.latest_agent_lesson || '';
1477
1790
  const nextTask = review.next_task || metadata.latest_agent_next_task || '';
1478
- const objective = task && (task.objective || metadata.task_goal || metadata.goal_objective || metadata.objective) || '';
1791
+ const objective = taskReviewObjective(task);
1479
1792
  const verificationFocus = taskReviewVerificationFocus(task);
1480
1793
  const actor = reviewActor(reviewer);
1481
1794
  return {
@@ -1501,7 +1814,7 @@ function taskReviewChatContract(task, { reviewer = 'codex-review', allowCertifie
1501
1814
  required_checks: [
1502
1815
  `Run ${`atris task show ${taskRef(task)} --json`} and read the current proof plus dialogue.`,
1503
1816
  verificationFocus.commands_to_verify.length
1504
- ? `Re-run or inspect these proof commands: ${verificationFocus.commands_to_verify.join(' | ')}.`
1817
+ ? `Re-run or inspect these proof commands: ${taskReviewFormatCommandList(verificationFocus.commands_to_verify)}.`
1505
1818
  : 'Find the concrete verifier command because the proof did not name one.',
1506
1819
  verificationFocus.files_to_inspect.length
1507
1820
  ? `Inspect these named files/artifacts before certifying: ${verificationFocus.files_to_inspect.join(', ')}.`
@@ -1755,6 +2068,17 @@ function taskStatusSummary(projection, { history = false, hasExistingReviewFollo
1755
2068
  doing_count: stream.doing_count,
1756
2069
  review_count: stream.review_count,
1757
2070
  blocked_count: stream.blocked_count,
2071
+ tasks: (stream.tasks || []).map(task => ({
2072
+ id: task.id,
2073
+ title: clipStatusText(task.title, 120),
2074
+ status: task.status,
2075
+ tag: task.tag || null,
2076
+ claimed_by: task.claimed_by || null,
2077
+ assigned_to: task.assigned_to || null,
2078
+ parent_task_id: task.parent_task_id || null,
2079
+ child_task_ids: task.child_task_ids || [],
2080
+ proof: task.proof || null,
2081
+ })),
1758
2082
  })),
1759
2083
  last_updated_at: lastUpdated ? new Date(lastUpdated).toISOString() : null,
1760
2084
  };
@@ -3999,8 +4323,19 @@ function cmdReviewLaneRun(args) {
3999
4323
  function reviewQueueLimit(args, total) {
4000
4324
  if (hasFlag(args, '--all')) return total;
4001
4325
  const raw = flag(args, '--limit');
4002
- const limit = raw && raw !== true ? Number(raw) : 12;
4003
- return Number.isFinite(limit) && limit > 0 ? Math.floor(limit) : 12;
4326
+ const limit = raw && raw !== true ? Number(raw) : 5;
4327
+ return Number.isFinite(limit) && limit > 0 ? Math.floor(limit) : 5;
4328
+ }
4329
+
4330
+ function reviewQueueVerbose(args) {
4331
+ return hasFlag(args, '--verbose') || hasFlag(args, '--details') || hasFlag(args, '--all');
4332
+ }
4333
+
4334
+ function reviewGroupTextLimit(args, total) {
4335
+ if (hasFlag(args, '--all')) return total;
4336
+ const raw = flag(args, '--limit');
4337
+ const limit = raw && raw !== true ? Number(raw) : 10;
4338
+ return Number.isFinite(limit) && limit > 0 ? Math.floor(limit) : 10;
4004
4339
  }
4005
4340
 
4006
4341
  // Risk order for human attention: named receipts that are missing/failing (0)
@@ -4175,17 +4510,23 @@ function cmdReviews(args) {
4175
4510
  printJson({ ok: true, action: 'review_groups', projection_path: outPath, groups });
4176
4511
  return;
4177
4512
  }
4178
- console.log(`READY TO LAND — grouped by ${key}`);
4179
- console.log(`${groups.total_certified} ready to land across ${groups.group_count} ${key} group(s)`);
4180
- groups.groups.forEach((g, index) => {
4513
+ console.log(`READY FOR APPROVAL — grouped by ${key}`);
4514
+ console.log(`${groups.total_certified} ready for approval across ${groups.group_count} ${key} group(s)`);
4515
+ const visibleGroups = groups.groups.slice(0, reviewGroupTextLimit(args, groups.groups.length));
4516
+ visibleGroups.forEach((g, index) => {
4181
4517
  console.log('');
4182
4518
  console.log(`${index + 1}. ${g.value} — ${g.count} task${g.count === 1 ? '' : 's'}`);
4183
4519
  g.sample_titles.forEach(title => console.log(` • ${title}`));
4184
- console.log(` land this group: ${g.accept_group_command} --confirm-human-accept --as <you>`);
4520
+ console.log(` approve this group: ${g.accept_group_command} --confirm-human-accept --as <you>`);
4185
4521
  });
4522
+ if (visibleGroups.length < groups.groups.length) {
4523
+ console.log('');
4524
+ console.log(`Showing ${visibleGroups.length}/${groups.groups.length} groups; rerun with --all for every group or --limit N to adjust.`);
4525
+ }
4186
4526
  return;
4187
4527
  }
4188
4528
  const queue = taskReviewQueue(projection, args);
4529
+ const verbose = reviewQueueVerbose(args);
4189
4530
  if (wantsJson(args)) {
4190
4531
  printJson({
4191
4532
  ok: true,
@@ -4195,10 +4536,10 @@ function cmdReviews(args) {
4195
4536
  });
4196
4537
  return;
4197
4538
  }
4198
- console.log('READY TO LAND');
4199
- console.log(`${queue.counts.certified} ready to land / ${queue.counts.blocking} need one more check / ${queue.counts.review} total waiting`);
4539
+ console.log('READY FOR APPROVAL');
4540
+ console.log(`${queue.counts.certified} ready for approval / ${queue.counts.blocking} need one more check / ${queue.counts.review} total waiting`);
4200
4541
  if (!queue.items.length) {
4201
- console.log('Nothing is ready to land.');
4542
+ console.log('Nothing is ready for approval.');
4202
4543
  return;
4203
4544
  }
4204
4545
  queue.items.forEach((item, index) => {
@@ -4216,8 +4557,8 @@ function cmdReviews(args) {
4216
4557
  if (item.result?.saved) console.log(` Saved: ${item.result.saved}`);
4217
4558
  if (item.landing.decision) console.log(` Decision: ${item.landing.decision}`);
4218
4559
  }
4219
- if (item.proof) console.log(` details: ${item.proof}`);
4220
- if (item.evidence) {
4560
+ if (verbose && item.proof) console.log(` details: ${item.proof}`);
4561
+ if (verbose && item.evidence) {
4221
4562
  item.evidence.receipts.forEach((receipt) => {
4222
4563
  const verdict = receipt.verifier_passed === true ? ' verifier:passed'
4223
4564
  : receipt.verifier_passed === false ? ' verifier:FAILED' : '';
@@ -4225,15 +4566,15 @@ function cmdReviews(args) {
4225
4566
  });
4226
4567
  item.evidence.missing.forEach((missingPath) => console.log(` receipt: ${missingPath} MISSING`));
4227
4568
  }
4228
- if (item.review_chat_command) console.log(` /codex: ${item.review_chat_command}`);
4569
+ if (verbose && item.review_chat_command) console.log(` /codex: ${item.review_chat_command}`);
4229
4570
  if (item.continue_work_command) console.log(` continue: ${item.continue_work_command}`);
4230
- if (item.accept_command) console.log(` land: ${item.accept_command}`);
4231
- else if (item.blocked_accept_reason) console.log(` land: blocked (${item.blocked_accept_reason})`);
4232
- console.log(` send back: ${item.revise_command}`);
4571
+ if (item.accept_command) console.log(` approve: ${item.accept_command}`);
4572
+ else if (item.blocked_accept_reason) console.log(` approve: blocked (${item.blocked_accept_reason})`);
4573
+ console.log(` rework: ${item.revise_command}`);
4233
4574
  });
4234
4575
  if (queue.counts.shown < queue.counts.certified) {
4235
4576
  console.log('');
4236
- console.log(`Showing ${queue.counts.shown}/${queue.counts.certified}; rerun with --all or --limit ${queue.counts.certified}.`);
4577
+ console.log(`Showing ${queue.counts.shown}/${queue.counts.certified}; rerun with --all for every row or --verbose for proof details.`);
4237
4578
  }
4238
4579
  }
4239
4580
 
@@ -4858,7 +5199,17 @@ function cmdClaim(args) {
4858
5199
  }
4859
5200
  }
4860
5201
 
4861
- function readEndgameAgentAction(root, owner) {
5202
+ function liveTaskWithTitle(tasks, title) {
5203
+ const wanted = String(title || '').trim().toLowerCase();
5204
+ if (!wanted) return null;
5205
+ return (tasks || []).find(task => {
5206
+ const taskTitle = String(task.title || task.task || '').trim().toLowerCase();
5207
+ const status = String(task.status || '').toLowerCase();
5208
+ return taskTitle === wanted && !new Set(['done', 'accepted', 'failed']).has(status);
5209
+ }) || null;
5210
+ }
5211
+
5212
+ function readEndgameAgentAction(root, owner, { tasks = [] } = {}) {
4862
5213
  const todoPath = path.join(root || process.cwd(), 'atris', 'TODO.md');
4863
5214
  if (!fs.existsSync(todoPath)) return null;
4864
5215
  const content = fs.readFileSync(todoPath, 'utf8');
@@ -4869,6 +5220,8 @@ function readEndgameAgentAction(root, owner) {
4869
5220
  if (!slug && !horizon) return null;
4870
5221
  const member = String(owner || DEFAULT_OWNER);
4871
5222
  const taskSeed = buildEndgameTaskSeed({ slug, horizon, owner: member });
5223
+ const existing = liveTaskWithTitle(tasks, taskSeed.title);
5224
+ if (existing) return null;
4872
5225
  return {
4873
5226
  kind: 'create_bounded_endgame_task',
4874
5227
  endgame_slug: slug || null,
@@ -4992,7 +5345,7 @@ function cmdNext(args) {
4992
5345
  ? continueWorkCommandForTask(reviewTask, { owner })
4993
5346
  : null;
4994
5347
  const nextAgentAction = handoff.next_action === 'human_accept_waiting'
4995
- ? readEndgameAgentAction(taskDb.workspaceRoot(), owner)
5348
+ ? readEndgameAgentAction(taskDb.workspaceRoot(), owner, { tasks: projection.tasks || [] })
4996
5349
  : null;
4997
5350
  if (hasFlag(args, '--create-next')) {
4998
5351
  if (!nextAgentAction || !nextAgentAction.task_seed) {
@@ -5042,12 +5395,12 @@ function cmdNext(args) {
5042
5395
  }
5043
5396
  console.log('No open tasks.');
5044
5397
  console.log(handoff.next_action === 'agent_review_again'
5045
- ? `${taskRef(reviewTask)} needs one more agent check before landing.`
5046
- : `${taskRef(reviewTask)} is ready to land.`);
5398
+ ? `${taskRef(reviewTask)} needs one more agent check before approval.`
5399
+ : `${taskRef(reviewTask)} is ready for approval.`);
5047
5400
  console.log(handoff.next_action === 'continue_work'
5048
- ? 'Continue work elsewhere; XP lands after the human lands this task.'
5401
+ ? 'Continue work elsewhere; XP is awarded only after the human approves this task.'
5049
5402
  : handoff.next_action === 'human_accept_waiting'
5050
- ? (nextAgentAction ? nextAgentAction.message : 'No next agent task is attached; this task is ready to land when the human decides.')
5403
+ ? (nextAgentAction ? nextAgentAction.message : 'No next agent task is attached; this task is ready for human approval.')
5051
5404
  : 'Review this task again before continuing.');
5052
5405
  if (nextAgentAction) console.log(`Command: ${nextAgentAction.command}`);
5053
5406
  if (nextAgentAction && nextAgentAction.task_seed) {
@@ -5459,9 +5812,9 @@ function cmdShow(args) {
5459
5812
  const owner = task.claimed_by ? ` / ${task.claimed_by}` : '';
5460
5813
  const tag = task.tag ? ` #${task.tag}` : '';
5461
5814
  const statusLabel = task.status === 'review'
5462
- ? 'READY TO LAND'
5815
+ ? 'READY FOR APPROVAL'
5463
5816
  : task.status === 'done'
5464
- ? 'LANDED'
5817
+ ? 'DONE'
5465
5818
  : task.status.toUpperCase();
5466
5819
  console.log(`${statusLabel} ${taskRef(task)} v${task.current_version}${owner}${tag}`);
5467
5820
  console.log(task.title);
@@ -5932,13 +6285,80 @@ function buildAcceptHumanResult({ task, proof, nextTask, publicSync }) {
5932
6285
  return { changed, checked, try_next: tryNext };
5933
6286
  }
5934
6287
 
5935
- function renderAcceptLanding({ task, proof, nextTask, publicSync }) {
5936
- return renderPublicResult(buildAcceptHumanResult({
6288
+ function refreshBrainScorecardsAfterAccept(root = process.cwd()) {
6289
+ try {
6290
+ const { recordTaskEpisodeScorecards } = require('../commands/brain');
6291
+ return { ok: true, ...recordTaskEpisodeScorecards({ root }) };
6292
+ } catch (error) {
6293
+ return {
6294
+ ok: false,
6295
+ error: error && error.message ? error.message : String(error),
6296
+ };
6297
+ }
6298
+ }
6299
+
6300
+ function nextMissionRouteAfterAccept(root = process.cwd()) {
6301
+ try {
6302
+ const { selectCodexGoalMission } = require('../commands/mission');
6303
+ const selected = selectCodexGoalMission(root);
6304
+ const mission = selected && selected.mission;
6305
+ if (!mission) return { ok: true, objective: null, route: 'next mission: none' };
6306
+ return {
6307
+ ok: true,
6308
+ mission_id: mission.id,
6309
+ objective: mission.objective,
6310
+ reason: selected.reason,
6311
+ route: `next mission: ${cleanPublicText(mission.objective, 160)}`,
6312
+ command: mission.next_action || `atris mission run ${mission.id}`,
6313
+ };
6314
+ } catch (error) {
6315
+ return {
6316
+ ok: false,
6317
+ route: 'next mission: needs refresh',
6318
+ error: error && error.message ? error.message : String(error),
6319
+ };
6320
+ }
6321
+ }
6322
+
6323
+ function xpLandingText(xpProjection) {
6324
+ if (!xpProjection) return 'XP updated';
6325
+ if (xpProjection.ok === false) return 'XP refresh failed';
6326
+ const candidates = [
6327
+ xpProjection.total_agent_xp,
6328
+ xpProjection.total_xp,
6329
+ xpProjection.summary && xpProjection.summary.total_agent_xp,
6330
+ xpProjection.totals && xpProjection.totals.total_agent_xp,
6331
+ ];
6332
+ const total = candidates.map(Number).find((value) => Number.isFinite(value));
6333
+ return Number.isFinite(total) ? `XP updated (${total} total)` : 'XP updated';
6334
+ }
6335
+
6336
+ function brainScorecardLandingText(brainScorecards) {
6337
+ if (!brainScorecards) return 'brain scorecards checked';
6338
+ if (brainScorecards.ok === false) return 'brain scorecard refresh failed';
6339
+ const written = Number(brainScorecards.written);
6340
+ return Number.isFinite(written) ? `brain scorecards +${written}` : 'brain scorecards checked';
6341
+ }
6342
+
6343
+ function buildVisibleAcceptReceipt(result, { xpProjection, brainScorecards, nextMissionRoute } = {}) {
6344
+ const updates = [
6345
+ xpLandingText(xpProjection),
6346
+ brainScorecardLandingText(brainScorecards),
6347
+ ].filter(Boolean);
6348
+ const checked = updates.length ? `${result.checked}; ${updates.join('; ')}` : result.checked;
6349
+ const route = cleanPublicText(nextMissionRoute && nextMissionRoute.route, 180);
6350
+ const tryNext = route ? `${result.try_next}; ${route}` : result.try_next;
6351
+ return { ...result, checked, try_next: tryNext };
6352
+ }
6353
+
6354
+ function renderAcceptLanding({ task, proof, nextTask, publicSync, xpProjection, brainScorecards, nextMissionRoute }) {
6355
+ const result = buildAcceptHumanResult({
5937
6356
  task,
5938
6357
  proof,
5939
6358
  nextTask,
5940
6359
  publicSync,
5941
- }));
6360
+ });
6361
+ return renderPublicResult(buildVisibleAcceptReceipt(result, { xpProjection, brainScorecards, nextMissionRoute }));
5942
6362
  }
5943
6363
 
5944
6364
  async function publishAcceptAgentXp(args, actor) {
@@ -6322,7 +6742,7 @@ function taskPageNextAction(task, current, actions, { hasExistingReviewFollowUp
6322
6742
  if (handoff && handoff.next_action === 'human_accept_waiting') {
6323
6743
  return {
6324
6744
  key: 'human_accept_waiting',
6325
- label: 'Ready to land',
6745
+ label: 'Ready for approval',
6326
6746
  command: null,
6327
6747
  api: null,
6328
6748
  human_accept_command: actions.human_accept_command || null,
@@ -6444,7 +6864,7 @@ function appendTaskReviewChat(taskDb, db, taskId, { reviewer = 'codex-review', d
6444
6864
  throw taskReviewChatError(`not_reviewable_${task.status}`, `review chat requires a task in Review; current status is ${task.status}`, { status: 409, exitCode: 1 });
6445
6865
  }
6446
6866
  if (!taskAllowsReviewChat(task, { allowCertified: true })) {
6447
- throw taskReviewChatError('agent_certified_continue_work', 'review chat is closed after agent certification; continue other work or land/send back this task', { status: 409, exitCode: 1 });
6867
+ throw taskReviewChatError('agent_certified_continue_work', 'review chat is closed after agent certification; continue other work or wait for human approval/rework on this task', { status: 409, exitCode: 1 });
6448
6868
  }
6449
6869
  const contract = taskReviewChatContract(task, { reviewer: actor, allowCertified: true });
6450
6870
  let event = null;
@@ -6601,8 +7021,8 @@ function readyHandoffForStep(task, proof, lesson, nextTask, agentCertified) {
6601
7021
  career_xp_status: 'pending_human_accept',
6602
7022
  next_action: agentCertified ? certifiedReviewNextAction(nextTask) : 'agent_review_again',
6603
7023
  rule: agentCertified
6604
- ? 'Double-check complete; ready to keep moving. XP lands only after the human lands the task.'
6605
- : 'Proof is ready; one more agent check before landing. XP waits for the human.',
7024
+ ? 'Double-check complete; ready to keep moving. XP is awarded only after the human approves the task.'
7025
+ : 'Proof is ready; one more agent check before human approval. XP waits for the human.',
6606
7026
  };
6607
7027
  if (reviewChat) {
6608
7028
  handoff.review_chat_command = reviewChat.command;
@@ -6623,7 +7043,7 @@ function runTaskStep(taskDb, db, taskId, options = {}) {
6623
7043
  const reason = initialHandoffState.next_action === 'continue_work'
6624
7044
  ? 'agent_certified_continue_work'
6625
7045
  : 'agent_certified_waiting_human';
6626
- throw taskStepError(reason, 'atris task step: ready-to-land rows have no safe agent step; continue other work or land/send back this task', { status: 409, exitCode: 1, page: initialPage });
7046
+ throw taskStepError(reason, 'atris task step: approval-ready rows have no safe agent step; continue other work or wait for human approval/rework on this task', { status: 409, exitCode: 1, page: initialPage });
6627
7047
  }
6628
7048
  let chat = null;
6629
7049
  const message = String(options.message || '').trim();
@@ -6725,7 +7145,7 @@ function runTaskStep(taskDb, db, taskId, options = {}) {
6725
7145
  const reason = handoffState.next_action === 'continue_work'
6726
7146
  ? 'agent_certified_continue_work'
6727
7147
  : 'agent_certified_waiting_human';
6728
- throw taskStepError(reason, 'atris task step: ready-to-land rows have no safe agent step; continue other work or land/send back this task', { status: 409, exitCode: 1, page: actionPage });
7148
+ throw taskStepError(reason, 'atris task step: approval-ready rows have no safe agent step; continue other work or wait for human approval/rework on this task', { status: 409, exitCode: 1, page: actionPage });
6729
7149
  }
6730
7150
  const reviewed = appendTaskReviewChat(taskDb, db, taskId, { reviewer, dryRun: Boolean(options.dryRun) });
6731
7151
  stepAction = 'review_chat';
@@ -6833,7 +7253,7 @@ function runCurrentTaskStep(taskDb, db, { owner = DEFAULT_OWNER, reviewer = 'cod
6833
7253
  if (nextActionKey === 'human_accept_waiting') {
6834
7254
  const error = taskStepError(
6835
7255
  'agent_certified_waiting_human',
6836
- 'atris task current-step: selected row is ready to land; no agent mutation is safe',
7256
+ 'atris task current-step: selected row is ready for human approval; no agent mutation is safe',
6837
7257
  {
6838
7258
  status: 409,
6839
7259
  exitCode: 1,
@@ -7296,8 +7716,8 @@ function cmdReady(args) {
7296
7716
  career_xp_status: 'pending_human_accept',
7297
7717
  next_action: agentCertified ? certifiedReviewNextAction(nextTaskInput.nextTask) : 'agent_review_again',
7298
7718
  rule: agentCertified
7299
- ? 'Double-check complete; ready to keep moving. XP lands only after the human lands the task.'
7300
- : 'Proof is ready; one more agent check before landing. XP waits for the human.',
7719
+ ? 'Double-check complete; ready to keep moving. XP is awarded only after the human approves the task.'
7720
+ : 'Proof is ready; one more agent check before human approval. XP waits for the human.',
7301
7721
  };
7302
7722
  if (reviewChat) {
7303
7723
  handoff.review_chat_command = reviewChat.command;
@@ -7327,7 +7747,7 @@ function cmdReady(args) {
7327
7747
  });
7328
7748
  return;
7329
7749
  }
7330
- console.log(`ready to land ${taskRef(compactTaskFromProjection(projection, taskId))} v${result.event.version}`);
7750
+ console.log(`ready for approval ${taskRef(compactTaskFromProjection(projection, taskId))} v${result.event.version}`);
7331
7751
  if (resultTrace) console.log('Result trace recorded.');
7332
7752
  console.log(handoff.rule);
7333
7753
  for (const hint of policyHints) {
@@ -7412,9 +7832,12 @@ async function cmdAccept(args) {
7412
7832
  });
7413
7833
  const xpProjection = refreshCareerXpAfterReview(reviewed);
7414
7834
  const { projection, outPath } = writeDefaultProjection(taskDb, db);
7835
+ const workspaceRoot = projection.workspace_root || process.cwd();
7836
+ const brainScorecards = refreshBrainScorecardsAfterAccept(workspaceRoot);
7837
+ const nextMissionRoute = nextMissionRouteAfterAccept(workspaceRoot);
7415
7838
  // Inform the gate, never block it: show what the receipts named in the proof
7416
7839
  // actually say so the accepting human isn't trusting prose.
7417
- const evidence = extractReceiptEvidence(proof, projection.workspace_root || process.cwd());
7840
+ const evidence = extractReceiptEvidence(proof, workspaceRoot);
7418
7841
  const publicSync = hasFlag(args, '--public') ? await publishAcceptAgentXp(args, actor) : null;
7419
7842
  if (wantsJson(args)) {
7420
7843
  printJson({
@@ -7427,6 +7850,8 @@ async function cmdAccept(args) {
7427
7850
  evidence,
7428
7851
  public_sync: publicSync,
7429
7852
  xp_projection: xpProjection,
7853
+ brain_scorecards: brainScorecards,
7854
+ next_mission_route: nextMissionRoute,
7430
7855
  projection_path: outPath,
7431
7856
  task: compactTaskFromProjection(projection, taskId),
7432
7857
  });
@@ -7438,6 +7863,9 @@ async function cmdAccept(args) {
7438
7863
  proof,
7439
7864
  nextTask,
7440
7865
  publicSync,
7866
+ xpProjection,
7867
+ brainScorecards,
7868
+ nextMissionRoute,
7441
7869
  }));
7442
7870
  if (publicSync && !publicSync.ok) process.exitCode = 1;
7443
7871
  }
@@ -7485,9 +7913,16 @@ function cmdAutoAcceptCertified(args) {
7485
7913
  const dryRun = hasFlag(args, '--dry-run');
7486
7914
  const strictVerify = hasFlag(args, '--strict-verify');
7487
7915
  const actorFlag = flag(args, '--as');
7488
- const actor = String(actorFlag || 'auto-accept-certified');
7489
7916
  const hasHumanActor = validHumanActorFlag(actorFlag);
7490
7917
  const confirmedHumanAccept = hasFlag(args, '--confirm-human-accept');
7918
+ // Standing owner authorization: when the autoland policy is on for this
7919
+ // workspace, live accepts run as the owner who flipped it, no per-run
7920
+ // confirmation needed. See lib/autoland.js and 'atris autoland'.
7921
+ const dryRunEarly = hasFlag(args, '--dry-run');
7922
+ const policyAuth = (!dryRunEarly && !confirmedHumanAccept)
7923
+ ? require('../lib/autoland').liveAcceptAuthorization()
7924
+ : { ok: false };
7925
+ const actor = String(actorFlag || (policyAuth.ok ? policyAuth.actor : 'auto-accept-certified'));
7491
7926
  const limitRaw = flag(args, '--limit');
7492
7927
  const max = limitRaw && limitRaw !== true ? Math.max(1, Number(limitRaw) || 12) : 12;
7493
7928
  const parsedReward = parseAcceptReward(flag(args, '--reward'));
@@ -7495,14 +7930,14 @@ function cmdAutoAcceptCertified(args) {
7495
7930
  console.error('atris task auto-accept-certified: reward must be a positive number');
7496
7931
  process.exit(2);
7497
7932
  }
7498
- if (!dryRun && !confirmedHumanAccept) {
7933
+ if (!dryRun && !confirmedHumanAccept && !policyAuth.ok) {
7499
7934
  failTask(
7500
7935
  'atris task auto-accept-certified',
7501
7936
  'human_accept_confirmation_required',
7502
- 'live auto-accept requires --confirm-human-accept --as <human>; use --dry-run to preview',
7937
+ 'live auto-accept requires --confirm-human-accept --as <human>, or the owner flips the standing policy with atris autoland on; use --dry-run to preview',
7503
7938
  );
7504
7939
  }
7505
- if (!dryRun && !hasHumanActor) {
7940
+ if (!dryRun && !hasHumanActor && !policyAuth.ok) {
7506
7941
  failTask(
7507
7942
  'atris task auto-accept-certified',
7508
7943
  'human_actor_required',
@@ -7707,7 +8142,7 @@ function cmdReview(args) {
7707
8142
  version: result.event.version,
7708
8143
  reward: result.episode.reward.value,
7709
8144
  episode: result.episode,
7710
- xp_projection: xpProjection,
8145
+ xp_projection: compactCareerXpProjection(xpProjection),
7711
8146
  next_task_id: nextCreated ? nextCreated.id : null,
7712
8147
  ...(nextTaskInput.ignored ? { review_next_task_ignored: nextTaskInput.ignored } : {}),
7713
8148
  projection_path: outPath,
@@ -8080,6 +8515,40 @@ function markdownRowsForRender(taskDb, existingTodoPath, rows, refRows) {
8080
8515
  return out;
8081
8516
  }
8082
8517
 
8518
+ function taskRenderStatusCounts(rows) {
8519
+ const counts = {
8520
+ backlog: 0,
8521
+ in_progress: 0,
8522
+ review: 0,
8523
+ blocked: 0,
8524
+ done: 0,
8525
+ total: 0,
8526
+ };
8527
+ for (const row of Array.isArray(rows) ? rows : []) {
8528
+ counts.total += 1;
8529
+ if (row.status === 'open') counts.backlog += 1;
8530
+ else if (row.status === 'claimed') counts.in_progress += 1;
8531
+ else if (row.status === 'review') counts.review += 1;
8532
+ else if (row.status === 'failed') counts.blocked += 1;
8533
+ else if (row.status === 'done') counts.done += 1;
8534
+ }
8535
+ return counts;
8536
+ }
8537
+
8538
+ function taskRenderCountLabel(count) {
8539
+ return count === 0 ? 'empty' : String(count);
8540
+ }
8541
+
8542
+ function taskRenderSummaryLine(counts) {
8543
+ return [
8544
+ `Backlog: ${taskRenderCountLabel(counts.backlog)}`,
8545
+ `In Progress: ${taskRenderCountLabel(counts.in_progress)}`,
8546
+ `Review: ${taskRenderCountLabel(counts.review)}`,
8547
+ `Blocked: ${taskRenderCountLabel(counts.blocked)}`,
8548
+ `Done saved: ${taskRenderCountLabel(counts.done)}`,
8549
+ ].join('; ');
8550
+ }
8551
+
8083
8552
  function cmdRender(args) {
8084
8553
  const out = flag(args, '--out') || path.join('atris', 'TODO.md');
8085
8554
  const all = hasFlag(args, '--all');
@@ -8103,6 +8572,7 @@ function cmdRender(args) {
8103
8572
  if (endgameSection) preservedSections.push(endgameSection);
8104
8573
  const markdownRows = markdownRowsForRender(taskDb, outPath, rows, refRows);
8105
8574
  const rowsToRender = [...rows, ...markdownRows];
8575
+ const statusCounts = taskRenderStatusCounts(rowsToRender);
8106
8576
  const markdown = taskDb.renderTodoMarkdown(rowsToRender, { doneLimit, failedLimit, refRows, preservedSections });
8107
8577
  fs.mkdirSync(path.dirname(outPath), { recursive: true });
8108
8578
  fs.writeFileSync(outPath, markdown, 'utf8');
@@ -8111,11 +8581,14 @@ function cmdRender(args) {
8111
8581
  ok: true,
8112
8582
  action: 'rendered',
8113
8583
  count: rowsToRender.length,
8584
+ status_counts: statusCounts,
8585
+ backlog_empty: statusCounts.backlog === 0,
8114
8586
  path: outPath,
8115
8587
  });
8116
8588
  return;
8117
8589
  }
8118
- console.log(`rendered ${rowsToRender.length} task${rowsToRender.length === 1 ? '' : 's'} -> ${outPath}`);
8590
+ console.log(`rendered TODO.md -> ${outPath}`);
8591
+ console.log(taskRenderSummaryLine(statusCounts));
8119
8592
  }
8120
8593
 
8121
8594
  function cmdSync(args) {