atris 3.30.1 → 3.30.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 (67) hide show
  1. package/AGENTS.md +6 -0
  2. package/atris/AGENTS.md +11 -0
  3. package/atris/CLAUDE.md +5 -0
  4. package/atris/atris.md +19 -4
  5. package/atris/policies/atris-design.md +71 -0
  6. package/atris/policies/design-seed.md +187 -0
  7. package/atris/skills/atris/SKILL.md +26 -3
  8. package/atris/skills/design/SKILL.md +3 -2
  9. package/atris/skills/loop/SKILL.md +5 -3
  10. package/atris/team/_template/MEMBER.md +19 -0
  11. package/atris.md +63 -23
  12. package/ax +1434 -1698
  13. package/bin/atris.js +5 -1
  14. package/commands/agent-spawn.js +2 -2
  15. package/commands/brain.js +92 -7
  16. package/commands/brainstorm.js +62 -22
  17. package/commands/business-sync.js +92 -8
  18. package/commands/business.js +13 -7
  19. package/commands/chat-scan.js +102 -0
  20. package/commands/computer.js +758 -15
  21. package/commands/deck.js +505 -105
  22. package/commands/init.js +6 -1
  23. package/commands/launchpad.js +638 -0
  24. package/commands/log-sync.js +44 -13
  25. package/commands/loop-front.js +290 -0
  26. package/commands/member.js +124 -3
  27. package/commands/mission.js +653 -30
  28. package/commands/pull.js +37 -28
  29. package/commands/pulse.js +11 -8
  30. package/commands/run.js +79 -39
  31. package/commands/sync.js +36 -17
  32. package/commands/task.js +342 -66
  33. package/commands/xp.js +3 -0
  34. package/commands/youtube.js +221 -7
  35. package/decks/README.md +89 -0
  36. package/decks/archetype-catalog.json +180 -0
  37. package/decks/atris-antislop-pitch.json +48 -0
  38. package/decks/atris-archetypes-v2.json +83 -0
  39. package/decks/atris-one-loop-pitch.json +80 -0
  40. package/decks/atris-seed-pitch-v3.json +118 -0
  41. package/decks/atris-seed-pitch-v4-skeleton.json +106 -0
  42. package/decks/atris-seed-pitch-v5.json +109 -0
  43. package/decks/atris-seed-pitch-v6.json +137 -0
  44. package/decks/atris-seed-pitch-v7.json +133 -0
  45. package/decks/atris-single-shot-proof.json +74 -0
  46. package/decks/mark-pincus-narrative.json +102 -0
  47. package/decks/mark-pincus-sourcery.json +94 -0
  48. package/decks/yash-applied-compute-detailed.json +150 -0
  49. package/decks/yash-applied-compute-generalist.json +82 -0
  50. package/decks/yash-applied-compute-narrative.json +54 -0
  51. package/lib/ax-prefs.js +5 -12
  52. package/lib/chat-log-scan.js +377 -0
  53. package/lib/context-gatherer.js +35 -1
  54. package/lib/deck-compose.js +145 -0
  55. package/lib/deck-history.js +64 -0
  56. package/lib/deck-layout.js +169 -0
  57. package/lib/deck-review.js +431 -0
  58. package/lib/deck-schema.js +154 -0
  59. package/lib/file-ops.js +2 -2
  60. package/lib/functional-owner.js +189 -0
  61. package/lib/slides-deck.js +512 -58
  62. package/lib/task-db.js +109 -2
  63. package/package.json +2 -1
  64. package/templates/business-starter/team/START_HERE.md +12 -8
  65. package/utils/auth.js +4 -0
  66. package/utils/config.js +4 -0
  67. package/atris/atrisDev.md +0 -717
package/commands/task.js CHANGED
@@ -11,6 +11,10 @@ const { taskProofState, buildVerifiedProof } = require('../lib/task-proof');
11
11
  const { evaluateAutoAccept, parseVerifyCommand } = require('../lib/auto-accept-certified');
12
12
  const { extractReceiptEvidence } = require('../lib/receipt-evidence');
13
13
  const escapeRegExp = require('../lib/escape-regexp');
14
+ const {
15
+ normalizeOwnerSlug,
16
+ resolveFunctionalOwner: resolveFunctionalTaskOwner,
17
+ } = require('../lib/functional-owner');
14
18
 
15
19
  const DEFAULT_OWNER = process.env.ATRIS_AGENT_ID
16
20
  || process.env.USER
@@ -102,6 +106,8 @@ atris task - durable local task state (SQLite, gitignored)
102
106
  Show the plain Plan before work starts
103
107
  atris task ready <id> --proof "..." [--changed "..." --checked "..." --saved "..." --try "..."]
104
108
  Agent proof ready; records Result if needed
109
+ atris task ready <id> --proof "..." [--happened "..." --checked "..." --tested "..." --decision "..."]
110
+ Agent proof ready; writes the human result receipt
105
111
  atris task result <id> --changed "..." --checked "..." [--saved "..."] [--try "..."]
106
112
  Show the plain Result and store trace on the task
107
113
  atris task review-chat <id> [--as <owner>] Start a task-owned /codex verification chat
@@ -112,7 +118,7 @@ atris task - durable local task state (SQLite, gitignored)
112
118
  atris task revise <id> --note "..." Send reviewed work back to Do
113
119
 
114
120
  atris task add "<title>" [--tag <tag>] [--goal-id <id>] Create a task
115
- atris task delegate "<title>" --to <id> [--goal-id <id>] Create an assigned task
121
+ atris task delegate "<title>" [--to <member>] [--executed-by <engine>] [--goal-id <id>] Create assigned work
116
122
  atris task plan <id> --goal "..." --exit "..." --proof-needed "..."
117
123
  Record a task-owned Plan stage
118
124
  atris task do <id> --as <owner> --first-move "..."
@@ -145,7 +151,7 @@ atris task - durable local task state (SQLite, gitignored)
145
151
  atris task review <id> --reward <n> [--verify "<cmd>"]
146
152
  Write review event + RSI episode
147
153
  atris task reviews [--limit <n>] Show certified Review items for human accept/revise
148
- atris task reviews --group-by <tag|owner|source> Cluster the certified wall for fast triage
154
+ atris task reviews --group-by <tag|owner|source> Cluster ready-to-land work for fast triage
149
155
  atris task accept-group <key>=<value> --spot-check K --confirm-human-accept --as <you> --verified <ids>
150
156
  Accept a whole cluster; career XP only on the K you verified
151
157
  atris task status [--json] [--history] Compact live status for web/Swarlo
@@ -309,6 +315,15 @@ function textFlag(args, names) {
309
315
  return '';
310
316
  }
311
317
 
318
+ function landingFlags(args) {
319
+ return {
320
+ happened: textFlag(args, ['--happened', '--what-happened']),
321
+ checked: textFlag(args, ['--checked', '--how-checked']),
322
+ tested: textFlag(args, ['--tested', '--what-tested']),
323
+ decision: textFlag(args, ['--decision', '--acceptance']),
324
+ };
325
+ }
326
+
312
327
  function numericFlag(args, name) {
313
328
  const value = flag(args, name);
314
329
  if (value === null || value === true || value === undefined) return null;
@@ -645,6 +660,10 @@ function reviewSummary(task, payload = {}) {
645
660
 
646
661
  const title = String(task.title || 'this task').replace(/\s+/g, ' ').trim();
647
662
  const plainTitle = title ? title.charAt(0).toLowerCase() + title.slice(1) : 'this task';
663
+ const approvalStatus = payload.approval_status || metadata.approval_status || null;
664
+ if (approvalStatus === 'revise') {
665
+ return `Rework requested for ${plainTitle}.`;
666
+ }
648
667
  const careerText = [
649
668
  task.tag,
650
669
  metadata.goal_id,
@@ -659,20 +678,148 @@ function reviewSummary(task, payload = {}) {
659
678
  || careerText.includes('agent xp')
660
679
  ) {
661
680
  if (task.status === 'done') {
662
- return `This is accepted AgentXP work: ${plainTitle} is done and has a proof receipt.`;
681
+ return `Landed AgentXP result for ${plainTitle}.`;
663
682
  }
664
683
  if (task.status === 'review') {
665
- return `This is AgentXP review: ${plainTitle} is agent-complete; accept only if the proof is real.`;
684
+ return `${plainTitle} is ready to land; land only if the proof is real.`;
666
685
  }
667
- return `This explains what accepting ${plainTitle} would make real for AgentXP.`;
686
+ return `This explains what landing ${plainTitle} would make real for AgentXP.`;
668
687
  }
669
688
  if (task.status === 'done') {
670
- return `This is the accepted outcome: ${plainTitle} is done and counted as real work.`;
689
+ return `Landed result for ${plainTitle}.`;
671
690
  }
672
691
  if (task.status === 'review') {
673
- return `This is the human checkpoint: ${plainTitle} is agent-complete and needs acceptance before it counts as done.`;
674
- }
675
- return `This explains what accepting ${plainTitle} would make real.`;
692
+ return `${plainTitle} is ready to land; land it or send it back.`;
693
+ }
694
+ return `This explains what landing ${plainTitle} would make real.`;
695
+ }
696
+
697
+ function titleToResultText(title) {
698
+ const text = String(title || 'this task').replace(/\s+/g, ' ').trim();
699
+ if (!text) return 'This task is ready to review.';
700
+ const [first, ...restParts] = text.split(' ');
701
+ const rest = restParts.join(' ');
702
+ const past = {
703
+ add: 'Added',
704
+ approve: 'Prepared approval for',
705
+ build: 'Built',
706
+ clean: 'Cleaned',
707
+ create: 'Created',
708
+ design: 'Designed',
709
+ document: 'Documented',
710
+ fix: 'Fixed',
711
+ make: 'Made',
712
+ replace: 'Replaced',
713
+ route: 'Routed',
714
+ run: 'Ran',
715
+ ship: 'Shipped',
716
+ update: 'Updated',
717
+ validate: 'Validated',
718
+ wire: 'Wired',
719
+ }[String(first || '').toLowerCase()];
720
+ if (past && rest) return `${past} ${rest}.`;
721
+ return `${text} is ready to review.`;
722
+ }
723
+
724
+ function titleToReasonText(task, proof = '') {
725
+ const title = String(task?.title || '').replace(/\s+/g, ' ').trim();
726
+ const text = `${title} ${proof || ''}`.toLowerCase();
727
+ if (/\b(priv(?:ate|acy)|secret|payload|leak|redact)\b/.test(text)) {
728
+ return 'It keeps private data out of the fast human decision screen.';
729
+ }
730
+ if (/\bapprove\b/.test(text) && /\b(command|exact|preview|ux)\b/.test(text)) {
731
+ return 'It lets the operator see the next command without hunting.';
732
+ }
733
+ if (/\b(stale|expire|expired)\b/.test(text) && /\bapproval/.test(text)) {
734
+ return 'It stops old approvals from running after their context has gone stale.';
735
+ }
736
+ if (/\bapproval|approve|permission\b/.test(text)) {
737
+ return 'It keeps real-world side effects behind a clear human decision.';
738
+ }
739
+ if (/\btest|self-test|harness|verifier|proof\b/.test(text)) {
740
+ return 'It makes the check repeatable before the work lands.';
741
+ }
742
+ return 'It closes the user-visible gap named by the task.';
743
+ }
744
+
745
+ function proofToHumanCheck(proof) {
746
+ const text = String(proof || '').replace(/\s+/g, ' ').trim();
747
+ if (!text) return 'Proof is attached below.';
748
+ const lower = text.toLowerCase();
749
+ 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
+ return 'I opened the real task screen and checked the receipt renders before raw proof.';
751
+ }
752
+ if (/git\s+diff\s+--check/.test(lower) && /\bpass(?:ed)?\b/.test(lower)) {
753
+ if (/node\s+--test/.test(lower)) return 'I ran the behavior check and the diff cleanliness check.';
754
+ return 'I ran the diff cleanliness check.';
755
+ }
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.';
760
+ if (/\breview(?:ed)?\b/.test(lower)) return 'Review proof is attached below.';
761
+ return 'Proof is attached below.';
762
+ }
763
+
764
+ 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(', ')}.`;
769
+ return String(proof || '').trim() ? 'I attached the proof below.' : 'No verifier command recorded yet.';
770
+ }
771
+
772
+ function landingPayloadValue(payload, metadata, key) {
773
+ const landing = payload && payload.landing && typeof payload.landing === 'object' ? payload.landing : {};
774
+ return landing[key]
775
+ || payload && payload[`landing_${key}`]
776
+ || metadata[`landing_${key}`]
777
+ || null;
778
+ }
779
+
780
+ function taskReviewLanding(task, review = {}, payload = {}) {
781
+ const metadata = task.metadata || {};
782
+ const proof = review.proof || payload.proof || metadata.latest_agent_proof || '';
783
+ const agentCertified = review.agent_certified === true || metadata.agent_certified === true;
784
+ const approvalStatus = review.approval_status || metadata.approval_status || null;
785
+ const explicitHappened = landingPayloadValue(payload, metadata, 'happened')
786
+ || payload.changed || metadata.result_changed || metadata.human_changed || metadata.changed;
787
+ const explicitChecked = landingPayloadValue(payload, metadata, 'checked')
788
+ || payload.checked || metadata.result_checked || metadata.human_checked || metadata.checked;
789
+ const explicitTested = landingPayloadValue(payload, metadata, 'tested');
790
+ const explicitDecision = landingPayloadValue(payload, metadata, 'decision');
791
+ const explicitReason = landingPayloadValue(payload, metadata, 'reason')
792
+ || landingPayloadValue(payload, metadata, 'why')
793
+ || payload.reason || payload.why || metadata.result_reason || metadata.review_reason || metadata.why_it_matters;
794
+ return {
795
+ happened: clipStatusText(explicitHappened || titleToResultText(task.title), 220),
796
+ reason: clipStatusText(explicitReason || titleToReasonText(task, proof), 220),
797
+ checked: clipStatusText(explicitChecked || proofToHumanCheck(proof), 220),
798
+ tested: clipStatusText(explicitTested || taskReviewLandingTested(proof), 260),
799
+ decision: clipStatusText(explicitDecision || (task.status === 'done'
800
+ ? 'Landed. No action needed unless this regresses.'
801
+ : approvalStatus === 'revise'
802
+ ? 'Rework requested. Fix the note, then send a new receipt.'
803
+ : 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),
806
+ };
807
+ }
808
+
809
+ function taskReviewResult(task, review = {}, payload = {}) {
810
+ const metadata = task.metadata || {};
811
+ const ref = taskRef(task);
812
+ const landing = taskReviewLanding(task, review, payload);
813
+ const explicitSaved = payload.saved || metadata.result_saved || metadata.human_saved || metadata.saved;
814
+ return {
815
+ changed: landing.happened,
816
+ reason: landing.reason,
817
+ checked: landing.checked,
818
+ saved: clipStatusText(explicitSaved || (task.status === 'done'
819
+ ? `Landed as ${ref}.`
820
+ : `Ready to land as ${ref}.`), 180),
821
+ accept: landing.decision,
822
+ };
676
823
  }
677
824
 
678
825
  function taskReviewSummary(task) {
@@ -681,7 +828,7 @@ function taskReviewSummary(task) {
681
828
  const metadata = task.metadata || {};
682
829
  if (!reviewed && !metadata.approval_status && !metadata.agent_review_pass_count && !metadata.human_revision_count && !metadata.agent_certified) return null;
683
830
  if (reviewed && reviewed.event_type === 'revision_requested') {
684
- return reviewSummaryWithVerificationChat(task, {
831
+ const review = {
685
832
  summary: reviewSummary(task, payload),
686
833
  reward: null,
687
834
  proof: null,
@@ -693,7 +840,10 @@ function taskReviewSummary(task) {
693
840
  agent_certification_policy: null,
694
841
  human_revision_count: metadata.human_revision_count || payload.revision_count || null,
695
842
  human_revision_note: metadata.human_revision_note || payload.note || null,
696
- });
843
+ };
844
+ review.landing = taskReviewLanding(task, review, payload);
845
+ review.result = taskReviewResult(task, review, payload);
846
+ return reviewSummaryWithVerificationChat(task, review);
697
847
  }
698
848
  const reviewPassCount = Number(metadata.agent_review_pass_count || payload.review_pass_count || 0);
699
849
  const agentCertified = metadata.agent_certified === true
@@ -713,7 +863,7 @@ function taskReviewSummary(task) {
713
863
  }
714
864
  return payload[key] || metadata[metadataKey] || null;
715
865
  };
716
- return reviewSummaryWithVerificationChat(task, {
866
+ const review = {
717
867
  summary: reviewSummary(task, payload),
718
868
  reward: reviewed && reviewed.event_type === 'reviewed' && payload.reward !== undefined ? payload.reward : null,
719
869
  proof: readyField('proof', 'latest_agent_proof'),
@@ -726,7 +876,10 @@ function taskReviewSummary(task) {
726
876
  || payload.agent_certification_policy
727
877
  || (agentCertified ? `${AGENT_CERTIFICATION_REVIEW_PASSES}_agent_review_passes` : null),
728
878
  human_revision_count: metadata.human_revision_count || null,
729
- });
879
+ };
880
+ review.landing = taskReviewLanding(task, review, payload);
881
+ review.result = taskReviewResult(task, review, payload);
882
+ return reviewSummaryWithVerificationChat(task, review);
730
883
  }
731
884
 
732
885
  function reviewSummaryWithVerificationChat(task, review) {
@@ -951,6 +1104,15 @@ function taskDescriptionForCloud(task) {
951
1104
  lines.push(`- ${message.actor || 'unknown'}: ${message.content}`);
952
1105
  }
953
1106
  }
1107
+ const landing = reviewLandingForDisplay(task.review);
1108
+ if (landing) {
1109
+ lines.push('', 'Result:');
1110
+ if (landing.happened) lines.push(`What happened: ${landing.happened}`);
1111
+ if (landing.reason) lines.push(`Why it matters: ${landing.reason}`);
1112
+ if (landing.checked) lines.push(`How checked: ${landing.checked}`);
1113
+ if (landing.tested) lines.push(`Tested: ${landing.tested}`);
1114
+ if (landing.decision) lines.push(`Decision: ${landing.decision}`);
1115
+ }
954
1116
  const reviewed = (task.events || []).slice().reverse().find(e => e.event_type === 'reviewed');
955
1117
  if (reviewed && reviewed.payload) {
956
1118
  if (reviewed.payload.proof) lines.push('', `Proof: ${reviewed.payload.proof}`);
@@ -964,6 +1126,32 @@ function taskDescriptionForCloud(task) {
964
1126
  return lines.join('\n').slice(0, 5000);
965
1127
  }
966
1128
 
1129
+ function reviewLandingForDisplay(review) {
1130
+ if (!review) return null;
1131
+ if (review.landing) return review.landing;
1132
+ if (!review.result) return null;
1133
+ return {
1134
+ happened: review.result.changed || null,
1135
+ reason: review.result.reason || null,
1136
+ checked: review.result.checked || null,
1137
+ tested: review.proof ? taskReviewLandingTested(review.proof) : null,
1138
+ decision: review.result.accept || null,
1139
+ };
1140
+ }
1141
+
1142
+ function printReviewLanding(review) {
1143
+ const landing = reviewLandingForDisplay(review);
1144
+ if (!landing) return false;
1145
+ console.log('Result:');
1146
+ if (landing.happened) console.log(` What happened: ${landing.happened}`);
1147
+ if (landing.reason) console.log(` Why it matters: ${landing.reason}`);
1148
+ if (landing.checked) console.log(` How I checked: ${landing.checked}`);
1149
+ if (landing.tested) console.log(` What I tested: ${landing.tested}`);
1150
+ if (review.result && review.result.saved) console.log(` Saved: ${review.result.saved}`);
1151
+ if (landing.decision) console.log(` Decision: ${landing.decision}`);
1152
+ return true;
1153
+ }
1154
+
967
1155
  function cloudPayloadForTask(task, businessId) {
968
1156
  const metadata = task.metadata || {};
969
1157
  const claimedAtEvent = (task.events || []).find(e => e.event_type === 'claimed');
@@ -1381,6 +1569,8 @@ function compactTaskForStatus(task) {
1381
1569
  const review = {};
1382
1570
  if (typeof task.review.reward === 'number') review.reward = task.review.reward;
1383
1571
  else if (task.review.reward === null) review.reward = null;
1572
+ if (task.review.landing) review.landing = task.review.landing;
1573
+ if (task.review.result) review.result = task.review.result;
1384
1574
  if (task.review.summary) review.summary = clipStatusText(task.review.summary, 240);
1385
1575
  if (task.review.proof) review.proof = clipStatusText(task.review.proof, 180);
1386
1576
  if (task.review.lesson) review.lesson = clipStatusText(task.review.lesson, 180);
@@ -1404,7 +1594,7 @@ function compactTaskForStatus(task) {
1404
1594
  if (Object.keys(lineage).length) out.lineage = lineage;
1405
1595
  }
1406
1596
  const compactMetadata = {};
1407
- for (const key of ['todo_id', 'stage', 'verify', 'delegate_via', 'goal_id', 'task_goal', 'goal_objective', 'approval_status', 'agent_review_pass_count', 'agent_certified', 'agent_certification_policy', 'human_revision_count', 'human_revision_note']) {
1597
+ for (const key of ['todo_id', 'stage', 'verify', 'delegate_via', 'owner_resolution', 'requested_owner', 'executed_by', 'proposed_member', 'proposed_member_command', 'goal_id', 'task_goal', 'goal_objective', 'approval_status', 'agent_review_pass_count', 'agent_certified', 'agent_certification_policy', 'human_revision_count', 'human_revision_note']) {
1408
1598
  if (metadata[key]) compactMetadata[key] = key === 'verify' ? clipStatusText(metadata[key], 180) : metadata[key];
1409
1599
  }
1410
1600
  if (Object.keys(compactMetadata).length) out.metadata = compactMetadata;
@@ -1598,9 +1788,20 @@ function sortTasksNewestFirst(tasks) {
1598
1788
  });
1599
1789
  }
1600
1790
 
1791
+ function sortTasksOldestFirst(tasks) {
1792
+ return [...tasks].sort((a, b) => {
1793
+ const byUpdated = Number(a.updated_at || 0) - Number(b.updated_at || 0);
1794
+ if (byUpdated) return byUpdated;
1795
+ return String(a.title || '').localeCompare(String(b.title || ''));
1796
+ });
1797
+ }
1798
+
1601
1799
  function taskQueueItem(task, { reviewer = 'codex-review', hasExistingReviewFollowUp = null } = {}) {
1602
1800
  const page = taskPageContract(task, { reviewer, hasExistingReviewFollowUp });
1603
1801
  const item = compactTaskForStatus(task) || {};
1802
+ item.ref = taskRef(task);
1803
+ item.display_id = item.display_id || task.display_id || null;
1804
+ item.legacy_ref = item.legacy_ref || task.legacy_ref || taskRef(task.id);
1604
1805
  if (item.review && item.review.verification_chat) {
1605
1806
  item.review = { ...item.review };
1606
1807
  delete item.review.verification_chat;
@@ -2682,7 +2883,8 @@ function taskQueueContract(projection, { reviewer = 'codex-review', limit = 8, s
2682
2883
  }
2683
2884
 
2684
2885
  function selectTaskForCurrent(projection, { owner = DEFAULT_OWNER, scope = {}, hasExistingReviewFollowUp = null } = {}) {
2685
- const tasks = filterTasksByScope(sortTasksNewestFirst(projection.tasks || []), scope, { hasExistingReviewFollowUp });
2886
+ const normalizedScope = normalizeTaskQueueScope(scope);
2887
+ const tasks = filterTasksByScope(sortTasksNewestFirst(projection.tasks || []), normalizedScope, { hasExistingReviewFollowUp });
2686
2888
  const columns = {
2687
2889
  backlog: [],
2688
2890
  plan: [],
@@ -2703,7 +2905,8 @@ function selectTaskForCurrent(projection, { owner = DEFAULT_OWNER, scope = {}, h
2703
2905
  if (reviewNeedsAgent) return { task: reviewNeedsAgent, reason: 'review_needs_agent_verification' };
2704
2906
  const reviewProofBoundaryBlocked = columns.review.find(task => reviewHandoffForTask(task, { suppressExistingFollowUp: true, hasExistingReviewFollowUp })?.next_action === PROOF_BOUNDARY_BLOCKED_ACTION);
2705
2907
  if (reviewProofBoundaryBlocked) return { task: reviewProofBoundaryBlocked, reason: 'review_proof_boundary_blocked' };
2706
- const planReady = columns.plan[0];
2908
+ const planQueue = normalizedScope.goal_id ? sortTasksOldestFirst(columns.plan) : columns.plan;
2909
+ const planReady = planQueue[0];
2707
2910
  if (planReady) return { task: planReady, reason: 'plan_ready' };
2708
2911
  const backlogIdea = columns.backlog[0];
2709
2912
  if (backlogIdea) return { task: backlogIdea, reason: 'backlog_idea' };
@@ -3821,10 +4024,14 @@ function reviewQueueItem(task, root = process.cwd(), evidence = undefined) {
3821
4024
  tag: task.tag || null,
3822
4025
  updated_at: task.updated_at || null,
3823
4026
  review_pass_count: task.review?.agent_review_pass_count || null,
4027
+ landing: reviewLandingForDisplay(task.review),
4028
+ result: task.review?.result || null,
3824
4029
  proof: taskReviewClip(task.review?.proof, 500) || null,
3825
4030
  next_action: handoff?.next_action || null,
3826
4031
  accept_command: acceptCommand,
4032
+ land_command: acceptCommand,
3827
4033
  revise_command: `atris task revise ${ref} --note "<what must change>"`,
4034
+ send_back_command: `atris task revise ${ref} --note "<what must change>"`,
3828
4035
  };
3829
4036
  const resolvedEvidence = evidence === undefined ? extractReceiptEvidence(task.review?.proof, root) : evidence;
3830
4037
  if (resolvedEvidence) item.evidence = resolvedEvidence;
@@ -3968,13 +4175,13 @@ function cmdReviews(args) {
3968
4175
  printJson({ ok: true, action: 'review_groups', projection_path: outPath, groups });
3969
4176
  return;
3970
4177
  }
3971
- console.log(`CERTIFIED REVIEW — grouped by ${key}`);
3972
- console.log(`${groups.total_certified} certified across ${groups.group_count} ${key} group(s)`);
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)`);
3973
4180
  groups.groups.forEach((g, index) => {
3974
4181
  console.log('');
3975
4182
  console.log(`${index + 1}. ${g.value} — ${g.count} task${g.count === 1 ? '' : 's'}`);
3976
4183
  g.sample_titles.forEach(title => console.log(` • ${title}`));
3977
- console.log(` review then accept the cluster: ${g.accept_group_command} --confirm-human-accept --as <you>`);
4184
+ console.log(` land this group: ${g.accept_group_command} --confirm-human-accept --as <you>`);
3978
4185
  });
3979
4186
  return;
3980
4187
  }
@@ -3988,10 +4195,10 @@ function cmdReviews(args) {
3988
4195
  });
3989
4196
  return;
3990
4197
  }
3991
- console.log('CERTIFIED REVIEW QUEUE');
3992
- console.log(`${queue.counts.certified} certified / ${queue.counts.blocking} need agent review / ${queue.counts.review} total review`);
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`);
3993
4200
  if (!queue.items.length) {
3994
- console.log('No certified review items.');
4201
+ console.log('Nothing is ready to land.');
3995
4202
  return;
3996
4203
  }
3997
4204
  queue.items.forEach((item, index) => {
@@ -4000,7 +4207,16 @@ function cmdReviews(args) {
4000
4207
  const badge = item.evidence?.all_passing ? ' [evidence:passing]' : '';
4001
4208
  console.log('');
4002
4209
  console.log(`${index + 1}. ${item.display_id || taskRef(item.id)}${tag}${passes}: ${item.title}${badge}`);
4003
- if (item.proof) console.log(` proof: ${item.proof}`);
4210
+ if (item.landing) {
4211
+ console.log(' Result:');
4212
+ if (item.landing.happened) console.log(` What happened: ${item.landing.happened}`);
4213
+ if (item.landing.reason) console.log(` Why it matters: ${item.landing.reason}`);
4214
+ if (item.landing.checked) console.log(` How I checked: ${item.landing.checked}`);
4215
+ if (item.landing.tested) console.log(` What I tested: ${item.landing.tested}`);
4216
+ if (item.result?.saved) console.log(` Saved: ${item.result.saved}`);
4217
+ if (item.landing.decision) console.log(` Decision: ${item.landing.decision}`);
4218
+ }
4219
+ if (item.proof) console.log(` details: ${item.proof}`);
4004
4220
  if (item.evidence) {
4005
4221
  item.evidence.receipts.forEach((receipt) => {
4006
4222
  const verdict = receipt.verifier_passed === true ? ' verifier:passed'
@@ -4011,9 +4227,9 @@ function cmdReviews(args) {
4011
4227
  }
4012
4228
  if (item.review_chat_command) console.log(` /codex: ${item.review_chat_command}`);
4013
4229
  if (item.continue_work_command) console.log(` continue: ${item.continue_work_command}`);
4014
- if (item.accept_command) console.log(` accept: ${item.accept_command}`);
4015
- else if (item.blocked_accept_reason) console.log(` accept: blocked (${item.blocked_accept_reason})`);
4016
- console.log(` revise: ${item.revise_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}`);
4017
4233
  });
4018
4234
  if (queue.counts.shown < queue.counts.certified) {
4019
4235
  console.log('');
@@ -4380,13 +4596,9 @@ function delegateHandoff(task, owner, via, tag) {
4380
4596
  function cmdDelegate(args) {
4381
4597
  const pos = positional(args);
4382
4598
  const title = pos.join(' ').trim();
4383
- const owner = flag(args, '--to') || flag(args, '--as');
4384
4599
  if (!title) {
4385
4600
  failTask('atris task delegate', 'missing_title', 'title required');
4386
4601
  }
4387
- if (!owner || owner === true) {
4388
- failTask('atris task delegate', 'missing_owner', '--to <owner> required');
4389
- }
4390
4602
  const viaFlag = flag(args, '--via');
4391
4603
  const via = viaFlag === 'swarlo' ? 'swarlo' : 'local';
4392
4604
  const tag = flag(args, '--tag');
@@ -4394,15 +4606,36 @@ function cmdDelegate(args) {
4394
4606
  const goalId = flag(args, '--goal-id');
4395
4607
  const goalObjective = flag(args, '--goal-objective') || flag(args, '--goal');
4396
4608
  const claimNow = hasFlag(args, '--claim');
4609
+ const requestedOwner = flag(args, '--to') || flag(args, '--as');
4610
+ const explicitExecutedBy = flag(args, '--executed-by');
4397
4611
  const taskDb = getTaskDb();
4398
4612
  const db = taskDb.open();
4399
4613
  const ws = taskDb.workspaceRoot();
4614
+ const ownerResolution = resolveFunctionalTaskOwner({
4615
+ requestedOwner: requestedOwner && requestedOwner !== true ? requestedOwner : null,
4616
+ title,
4617
+ tag,
4618
+ note,
4619
+ goal: goalObjective && goalObjective !== true ? goalObjective : '',
4620
+ root: ws,
4621
+ });
4622
+ const owner = ownerResolution.owner;
4623
+ const executedBy = explicitExecutedBy && explicitExecutedBy !== true
4624
+ ? normalizeOwnerSlug(explicitExecutedBy)
4625
+ : ownerResolution.executed_by;
4400
4626
  const metadata = {
4401
- assigned_to: String(owner),
4627
+ assigned_to: owner,
4402
4628
  delegate_via: via,
4403
4629
  swarlo_channel: via === 'swarlo' ? String(tag || 'tasks') : null,
4404
4630
  created_for_day: new Date().toISOString().slice(0, 10),
4631
+ owner_resolution: ownerResolution.reason,
4405
4632
  };
4633
+ if (ownerResolution.requested_owner) metadata.requested_owner = ownerResolution.requested_owner;
4634
+ if (executedBy) metadata.executed_by = executedBy;
4635
+ if (ownerResolution.proposed_member) {
4636
+ metadata.proposed_member = ownerResolution.proposed_member;
4637
+ metadata.proposed_member_command = `atris member create ${ownerResolution.proposed_member} --role="${ownerResolution.proposed_member.replace(/-/g, ' ')}"`;
4638
+ }
4406
4639
  if (goalId && goalId !== true) metadata.goal_id = String(goalId);
4407
4640
  if (goalObjective && goalObjective !== true) {
4408
4641
  metadata.task_goal = String(goalObjective);
@@ -4413,7 +4646,7 @@ function cmdDelegate(args) {
4413
4646
  tag: typeof tag === 'string' ? tag : null,
4414
4647
  workspaceRoot: ws,
4415
4648
  status: claimNow ? 'claimed' : 'open',
4416
- claimedBy: claimNow ? String(owner) : null,
4649
+ claimedBy: claimNow ? owner : null,
4417
4650
  metadata,
4418
4651
  });
4419
4652
  if (typeof note === 'string' && note.trim()) {
@@ -4421,14 +4654,16 @@ function cmdDelegate(args) {
4421
4654
  }
4422
4655
  const { projection, outPath } = writeDefaultProjection(taskDb, db);
4423
4656
  const task = compactTaskFromProjection(projection, result.id);
4424
- const handoff = delegateHandoff(task, String(owner), via, typeof tag === 'string' ? tag : null);
4657
+ const handoff = delegateHandoff(task, owner, via, typeof tag === 'string' ? tag : null);
4425
4658
  if (wantsJson(args)) {
4426
4659
  printJson({
4427
4660
  ok: true,
4428
4661
  action: 'delegated',
4429
4662
  task_id: result.id,
4430
4663
  inserted: result.inserted !== false,
4431
- owner: String(owner),
4664
+ owner,
4665
+ owner_resolution: ownerResolution,
4666
+ executed_by: executedBy || null,
4432
4667
  via,
4433
4668
  handoff,
4434
4669
  projection_path: outPath,
@@ -4438,6 +4673,8 @@ function cmdDelegate(args) {
4438
4673
  }
4439
4674
  const tagText = tag && tag !== true ? ` #${tag}` : '';
4440
4675
  console.log(`delegated ${taskRef(task)} -> ${owner}${tagText} via=${via}`);
4676
+ if (executedBy) console.log(`executed_by: ${executedBy}`);
4677
+ if (ownerResolution.proposed_member) console.log(`member: ${metadata.proposed_member_command}`);
4441
4678
  console.log(`claim: ${handoff.command}`);
4442
4679
  if (handoff.swarlo) console.log(`swarlo: ${handoff.swarlo.channel}/${handoff.swarlo.action}`);
4443
4680
  }
@@ -4528,7 +4765,7 @@ function cmdDay(args) {
4528
4765
  console.log(`stale ${staleFailed.length} failed >7d hidden — atris task list --status failed`);
4529
4766
  }
4530
4767
  console.log('');
4531
- console.log('add: atris task delegate "..." --to codex --tag tasks');
4768
+ console.log('add: atris task delegate "..." --to task-planner --tag tasks');
4532
4769
  }
4533
4770
 
4534
4771
  function cmdHome(args) {
@@ -4784,7 +5021,7 @@ function cmdNext(args) {
4784
5021
  }
4785
5022
  console.log(`created ${taskRef(createdTask)} @${owner}`);
4786
5023
  console.log(createdTask.title);
4787
- console.log(`Noted v${created.note_version}. Human accept remains pending on ${taskRef(reviewTask)}.`);
5024
+ console.log(`Noted v${created.note_version}. Landing remains pending on ${taskRef(reviewTask)}.`);
4788
5025
  console.log(`Verify: ${nextAgentAction.task_seed.verifier}`);
4789
5026
  return;
4790
5027
  }
@@ -4805,12 +5042,12 @@ function cmdNext(args) {
4805
5042
  }
4806
5043
  console.log('No open tasks.');
4807
5044
  console.log(handoff.next_action === 'agent_review_again'
4808
- ? `${taskRef(reviewTask)} needs one more agent review before continuation.`
4809
- : `${taskRef(reviewTask)} is agent-certified and waiting for human accept.`);
5045
+ ? `${taskRef(reviewTask)} needs one more agent check before landing.`
5046
+ : `${taskRef(reviewTask)} is ready to land.`);
4810
5047
  console.log(handoff.next_action === 'continue_work'
4811
- ? 'Continue work elsewhere; AgentXP waits for human accept.'
5048
+ ? 'Continue work elsewhere; XP lands after the human lands this task.'
4812
5049
  : handoff.next_action === 'human_accept_waiting'
4813
- ? (nextAgentAction ? nextAgentAction.message : 'No concrete next agent task is attached; AgentXP waits for human accept.')
5050
+ ? (nextAgentAction ? nextAgentAction.message : 'No next agent task is attached; this task is ready to land when the human decides.')
4814
5051
  : 'Review this task again before continuing.');
4815
5052
  if (nextAgentAction) console.log(`Command: ${nextAgentAction.command}`);
4816
5053
  if (nextAgentAction && nextAgentAction.task_seed) {
@@ -5221,17 +5458,30 @@ function cmdShow(args) {
5221
5458
  }
5222
5459
  const owner = task.claimed_by ? ` / ${task.claimed_by}` : '';
5223
5460
  const tag = task.tag ? ` #${task.tag}` : '';
5224
- console.log(`${task.status.toUpperCase()} ${taskRef(task)} v${task.current_version}${owner}${tag}`);
5461
+ const statusLabel = task.status === 'review'
5462
+ ? 'READY TO LAND'
5463
+ : task.status === 'done'
5464
+ ? 'LANDED'
5465
+ : task.status.toUpperCase();
5466
+ console.log(`${statusLabel} ${taskRef(task)} v${task.current_version}${owner}${tag}`);
5225
5467
  console.log(task.title);
5226
5468
  if (task.review) {
5227
5469
  console.log('');
5228
- if (task.review.summary) console.log(`Summary: ${task.review.summary}`);
5229
- if (task.review.proof) console.log(`Proof: ${task.review.proof}`);
5470
+ printReviewLanding(task.review);
5471
+ if (task.review.summary) console.log(`Short version: ${task.review.summary}`);
5472
+ if (task.review.proof) console.log(`Details: ${task.review.proof}`);
5230
5473
  if (task.review.lesson) console.log(`Lesson: ${task.review.lesson}`);
5231
5474
  if (task.review.next_task) console.log(`Next: ${task.review.next_task}`);
5232
- if (task.review.approval_status) console.log(`Approval: ${task.review.approval_status}`);
5233
- if (task.review.verification_chat) console.log(`Review chat: ${task.review.verification_chat.command}`);
5234
- if (task.review.agent_certified) console.log(`Agent certified: yes (${task.review.agent_review_pass_count || AGENT_CERTIFICATION_REVIEW_PASSES} reviews)`);
5475
+ if (task.review.approval_status) {
5476
+ const landingStatus = task.review.approval_status === 'pending'
5477
+ ? 'waiting on human'
5478
+ : task.review.approval_status === 'revise'
5479
+ ? 'sent back'
5480
+ : task.review.approval_status;
5481
+ console.log(`Landing: ${landingStatus}`);
5482
+ }
5483
+ if (task.review.verification_chat) console.log(`Check command: ${task.review.verification_chat.command}`);
5484
+ if (task.review.agent_certified) console.log(`Checked: yes (${task.review.agent_review_pass_count || AGENT_CERTIFICATION_REVIEW_PASSES} agent checks)`);
5235
5485
  }
5236
5486
  if (task.messages.length) {
5237
5487
  console.log('');
@@ -5273,10 +5523,11 @@ function cmdPage(args) {
5273
5523
  console.log(`TASK PAGE ${taskRef(task)}`);
5274
5524
  console.log(`Goal: ${page.goal.text || '(none)'}`);
5275
5525
  console.log(`Stage: ${page.stage.current}`);
5526
+ printReviewLanding(page.review);
5276
5527
  console.log(`Next: ${page.stage.next_action.command || page.stage.next_action.label}`);
5277
5528
  console.log(`Chat: ${page.chat.command}`);
5278
- if (page.review.verification_chat) console.log(`Review chat: ${page.review.verification_chat.command}`);
5279
- if (page.review.human_accept.enabled) console.log(`Human accept: ${page.review.human_accept.command}`);
5529
+ if (page.review.verification_chat) console.log(`Check command: ${page.review.verification_chat.command}`);
5530
+ if (page.review.human_accept.enabled) console.log(`Land: ${page.review.human_accept.command}`);
5280
5531
  }
5281
5532
 
5282
5533
  function cmdReviewChat(args) {
@@ -6013,7 +6264,7 @@ function taskPageActions(task, { reviewer = 'codex-review', hasExistingReviewFol
6013
6264
  note_command: `atris task note ${ref} "<context>" --as ${owner}`,
6014
6265
  plan_command: `atris task plan ${ref} --goal ${taskCommandQuote(goal)} --exit "<exit condition>" --proof-needed "<verification command>" --first-move "<first move>"`,
6015
6266
  do_command: `atris task do ${ref} --as ${owner} --first-move "<first move>"`,
6016
- ready_command: `atris task ready ${ref} --as ${owner} --proof "<specific proof command/result>"`,
6267
+ ready_command: `atris task ready ${ref} --as ${owner} --proof "<specific proof command/result>" --happened "<what happened>" --checked "<how you know>" --tested "<what you ran or inspected>" --decision "<accept/rework guidance>"`,
6017
6268
  review_command: `atris task review ${ref} --reward 0 --as ${actor} --proof "<specific proof command/result>" --verify "<safe verifier command>"`,
6018
6269
  };
6019
6270
  if (task && task.status === 'review') {
@@ -6071,7 +6322,7 @@ function taskPageNextAction(task, current, actions, { hasExistingReviewFollowUp
6071
6322
  if (handoff && handoff.next_action === 'human_accept_waiting') {
6072
6323
  return {
6073
6324
  key: 'human_accept_waiting',
6074
- label: 'Waiting for human accept',
6325
+ label: 'Ready to land',
6075
6326
  command: null,
6076
6327
  api: null,
6077
6328
  human_accept_command: actions.human_accept_command || null,
@@ -6092,6 +6343,7 @@ function taskPageContract(task, { reviewer = 'codex-review', hasExistingReviewFo
6092
6343
  const metadata = task && task.metadata || {};
6093
6344
  const current = taskPageCurrentStage(task);
6094
6345
  const actions = taskPageActions(task, { reviewer, hasExistingReviewFollowUp });
6346
+ const review = task && (task.review || taskReviewSummary(task));
6095
6347
  const recentMessages = (task && Array.isArray(task.messages) ? task.messages : []).slice(-10).map(message => ({
6096
6348
  version: message.version || null,
6097
6349
  actor: message.actor || null,
@@ -6135,9 +6387,13 @@ function taskPageContract(task, { reviewer = 'codex-review', hasExistingReviewFo
6135
6387
  },
6136
6388
  actions,
6137
6389
  review: {
6138
- approval_status: task.review && task.review.approval_status || metadata.approval_status || null,
6139
- agent_review_pass_count: task.review && task.review.agent_review_pass_count || metadata.agent_review_pass_count || null,
6140
- agent_certified: Boolean(task.review && task.review.agent_certified || metadata.agent_certified),
6390
+ landing: review && review.landing || null,
6391
+ result: review && review.result || null,
6392
+ summary: review && review.summary || null,
6393
+ proof: review && review.proof || null,
6394
+ approval_status: review && review.approval_status || metadata.approval_status || null,
6395
+ agent_review_pass_count: review && review.agent_review_pass_count || metadata.agent_review_pass_count || null,
6396
+ agent_certified: Boolean(review && review.agent_certified || metadata.agent_certified),
6141
6397
  verification_chat: reviewChat,
6142
6398
  handoff: reviewHandoff,
6143
6399
  human_accept: {
@@ -6188,7 +6444,7 @@ function appendTaskReviewChat(taskDb, db, taskId, { reviewer = 'codex-review', d
6188
6444
  throw taskReviewChatError(`not_reviewable_${task.status}`, `review chat requires a task in Review; current status is ${task.status}`, { status: 409, exitCode: 1 });
6189
6445
  }
6190
6446
  if (!taskAllowsReviewChat(task, { allowCertified: true })) {
6191
- throw taskReviewChatError('agent_certified_continue_work', 'review chat is closed after agent certification; continue other work or wait for human accept', { status: 409, exitCode: 1 });
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 });
6192
6448
  }
6193
6449
  const contract = taskReviewChatContract(task, { reviewer: actor, allowCertified: true });
6194
6450
  let event = null;
@@ -6345,8 +6601,8 @@ function readyHandoffForStep(task, proof, lesson, nextTask, agentCertified) {
6345
6601
  career_xp_status: 'pending_human_accept',
6346
6602
  next_action: agentCertified ? certifiedReviewNextAction(nextTask) : 'agent_review_again',
6347
6603
  rule: agentCertified
6348
- ? 'Agent double-check complete; continue work. AgentXP waits for human accept.'
6349
- : 'Proof is in Review; one more agent review pass certifies continuation. AgentXP waits for human accept.',
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.',
6350
6606
  };
6351
6607
  if (reviewChat) {
6352
6608
  handoff.review_chat_command = reviewChat.command;
@@ -6367,7 +6623,7 @@ function runTaskStep(taskDb, db, taskId, options = {}) {
6367
6623
  const reason = initialHandoffState.next_action === 'continue_work'
6368
6624
  ? 'agent_certified_continue_work'
6369
6625
  : 'agent_certified_waiting_human';
6370
- throw taskStepError(reason, 'atris task step: agent-certified Review rows have no safe agent step; continue other work or wait for human accept', { status: 409, exitCode: 1, page: initialPage });
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 });
6371
6627
  }
6372
6628
  let chat = null;
6373
6629
  const message = String(options.message || '').trim();
@@ -6469,7 +6725,7 @@ function runTaskStep(taskDb, db, taskId, options = {}) {
6469
6725
  const reason = handoffState.next_action === 'continue_work'
6470
6726
  ? 'agent_certified_continue_work'
6471
6727
  : 'agent_certified_waiting_human';
6472
- throw taskStepError(reason, 'atris task step: agent-certified Review rows have no safe agent step; continue other work or wait for human accept', { status: 409, exitCode: 1, page: actionPage });
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 });
6473
6729
  }
6474
6730
  const reviewed = appendTaskReviewChat(taskDb, db, taskId, { reviewer, dryRun: Boolean(options.dryRun) });
6475
6731
  stepAction = 'review_chat';
@@ -6577,7 +6833,7 @@ function runCurrentTaskStep(taskDb, db, { owner = DEFAULT_OWNER, reviewer = 'cod
6577
6833
  if (nextActionKey === 'human_accept_waiting') {
6578
6834
  const error = taskStepError(
6579
6835
  'agent_certified_waiting_human',
6580
- 'atris task current-step: selected Review row is agent-certified and waiting for human accept; no agent mutation is safe',
6836
+ 'atris task current-step: selected row is ready to land; no agent mutation is safe',
6581
6837
  {
6582
6838
  status: 409,
6583
6839
  exitCode: 1,
@@ -6989,6 +7245,7 @@ function cmdReady(args) {
6989
7245
  requireMeaningfulTaskProof('atris task ready', proof);
6990
7246
  const lesson = flag(args, '--lesson') || '';
6991
7247
  const nextTaskInput = normalizeReviewNextTaskInput(typeof flag(args, '--next') === 'string' ? flag(args, '--next') : '');
7248
+ const landing = landingFlags(args);
6992
7249
  const actor = String(flag(args, '--as') || DEFAULT_OWNER);
6993
7250
  const resultFields = {
6994
7251
  changed: textFlag(args, ['--changed', '--result', '--done']),
@@ -7017,6 +7274,7 @@ function cmdReady(args) {
7017
7274
  lesson: typeof lesson === 'string' ? lesson : '',
7018
7275
  nextTask: nextTaskInput.nextTask,
7019
7276
  resultTrace: resultTrace && resultTrace.trace,
7277
+ landing,
7020
7278
  });
7021
7279
  if (!result.ready) {
7022
7280
  console.error(`ready failed: ${result.reason}`);
@@ -7038,8 +7296,8 @@ function cmdReady(args) {
7038
7296
  career_xp_status: 'pending_human_accept',
7039
7297
  next_action: agentCertified ? certifiedReviewNextAction(nextTaskInput.nextTask) : 'agent_review_again',
7040
7298
  rule: agentCertified
7041
- ? 'Agent double-check complete; continue work. AgentXP waits for human accept.'
7042
- : 'Proof is in Review; one more agent review pass certifies continuation. AgentXP waits for human accept.',
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.',
7043
7301
  };
7044
7302
  if (reviewChat) {
7045
7303
  handoff.review_chat_command = reviewChat.command;
@@ -7069,7 +7327,7 @@ function cmdReady(args) {
7069
7327
  });
7070
7328
  return;
7071
7329
  }
7072
- console.log(`ready ${taskRef(compactTaskFromProjection(projection, taskId))} v${result.event.version} pending approval`);
7330
+ console.log(`ready to land ${taskRef(compactTaskFromProjection(projection, taskId))} v${result.event.version}`);
7073
7331
  if (resultTrace) console.log('Result trace recorded.');
7074
7332
  console.log(handoff.rule);
7075
7333
  for (const hint of policyHints) {
@@ -7361,6 +7619,7 @@ function cmdRevise(args) {
7361
7619
  version: result.event.version,
7362
7620
  approval_status: 'revise',
7363
7621
  revision_count: result.event.payload.revision_count,
7622
+ episode: result.episode || null,
7364
7623
  projection_path: outPath,
7365
7624
  task: compactTaskFromProjection(projection, taskId),
7366
7625
  });
@@ -8288,7 +8547,10 @@ function taskBoardHtml() {
8288
8547
  '<div class="meta"><span class="pill">' + task.status + '</span><span class="pill">' + (task.claimed_by || 'unowned') + '</span><span class="pill">v' + task.current_version + '</span></div>',
8289
8548
  '<div class="fact"><b>Goal</b><div id="taskGoal"></div></div>',
8290
8549
  '<div class="fact"><b>Lineage</b><div id="taskLineage"></div></div>',
8291
- '<div class="fact"><b>Summary</b><div id="taskSummary"></div></div>',
8550
+ '<div class="fact"><b>Result</b><div id="taskHappened"></div></div>',
8551
+ '<div class="fact"><b>How I checked</b><div id="taskChecked"></div></div>',
8552
+ '<div class="fact"><b>What I tested</b><div id="taskTested"></div></div>',
8553
+ '<div class="fact"><b>Decision</b><div id="taskDecision"></div></div>',
8292
8554
  '<div class="fact"><b>Proof / lesson</b><div id="taskProof"></div></div>',
8293
8555
  '<div class="thread">' + (messages || '<div class="empty">No thread yet.</div>') + '</div>',
8294
8556
  '<label>Add context</label><textarea id="note" placeholder="Decision, blocker, context, update..."></textarea>',
@@ -8300,9 +8562,17 @@ function taskBoardHtml() {
8300
8562
  room.querySelector('h3').textContent = task.title;
8301
8563
  $('taskGoal').textContent = task.objective || 'No matching goal yet.';
8302
8564
  $('taskLineage').textContent = 'parent: ' + parent + ' / next: ' + children;
8303
- $('taskSummary').textContent = task.review && task.review.summary
8304
- ? task.review.summary
8305
- : 'No review summary yet.';
8565
+ const result = task.review && task.review.result || {};
8566
+ const landing = task.review && task.review.landing || {
8567
+ happened: result.changed,
8568
+ checked: result.checked,
8569
+ tested: task.review && task.review.proof ? 'Proof is attached below.' : '',
8570
+ decision: result.accept,
8571
+ };
8572
+ $('taskHappened').textContent = landing.happened || (task.review && task.review.summary) || 'No review result yet.';
8573
+ $('taskChecked').textContent = landing.checked || 'No check yet.';
8574
+ $('taskTested').textContent = landing.tested || 'No test recorded yet.';
8575
+ $('taskDecision').textContent = landing.decision || 'No accept action yet.';
8306
8576
  $('taskProof').textContent = task.review && (task.review.proof || task.review.lesson)
8307
8577
  ? ((task.review.proof || 'no proof') + ' / ' + (task.review.lesson || 'no lesson'))
8308
8578
  : 'No proof yet.';
@@ -8804,6 +9074,12 @@ async function handleTaskApi(req, res, taskDb, db) {
8804
9074
  lesson: String(body.lesson || ''),
8805
9075
  nextTask: nextTaskInput.nextTask,
8806
9076
  resultTrace: resultTrace && resultTrace.trace,
9077
+ landing: body.landing || {
9078
+ happened: body.happened,
9079
+ checked: body.checked,
9080
+ tested: body.tested,
9081
+ decision: body.decision,
9082
+ },
8807
9083
  });
8808
9084
  if (!result.ready) return sendJson(res, 409, { ok: false, reason: result.reason });
8809
9085
  const { projection, outPath } = writeDefaultProjection(taskDb, db);