atris 3.30.0 → 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 (69) 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 +1072 -89
  33. package/commands/workflow.js +6 -6
  34. package/commands/xp.js +13 -1
  35. package/commands/youtube.js +221 -7
  36. package/decks/README.md +89 -0
  37. package/decks/archetype-catalog.json +180 -0
  38. package/decks/atris-antislop-pitch.json +48 -0
  39. package/decks/atris-archetypes-v2.json +83 -0
  40. package/decks/atris-one-loop-pitch.json +80 -0
  41. package/decks/atris-seed-pitch-v3.json +118 -0
  42. package/decks/atris-seed-pitch-v4-skeleton.json +106 -0
  43. package/decks/atris-seed-pitch-v5.json +109 -0
  44. package/decks/atris-seed-pitch-v6.json +137 -0
  45. package/decks/atris-seed-pitch-v7.json +133 -0
  46. package/decks/atris-single-shot-proof.json +74 -0
  47. package/decks/mark-pincus-narrative.json +102 -0
  48. package/decks/mark-pincus-sourcery.json +94 -0
  49. package/decks/yash-applied-compute-detailed.json +150 -0
  50. package/decks/yash-applied-compute-generalist.json +82 -0
  51. package/decks/yash-applied-compute-narrative.json +54 -0
  52. package/lib/ax-prefs.js +5 -12
  53. package/lib/chat-log-scan.js +377 -0
  54. package/lib/context-gatherer.js +35 -1
  55. package/lib/deck-compose.js +145 -0
  56. package/lib/deck-history.js +64 -0
  57. package/lib/deck-layout.js +169 -0
  58. package/lib/deck-review.js +431 -0
  59. package/lib/deck-schema.js +154 -0
  60. package/lib/file-ops.js +3 -3
  61. package/lib/functional-owner.js +189 -0
  62. package/lib/journal.js +7 -73
  63. package/lib/slides-deck.js +512 -58
  64. package/lib/task-db.js +128 -11
  65. package/package.json +2 -1
  66. package/templates/business-starter/team/START_HERE.md +12 -8
  67. package/utils/auth.js +4 -0
  68. package/utils/config.js +4 -0
  69. package/atris/atrisDev.md +0 -717
package/commands/task.js CHANGED
@@ -11,12 +11,17 @@ 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
17
21
  || os.userInfo().username
18
22
  || 'unknown';
19
23
  const AGENT_CERTIFICATION_REVIEW_PASSES = 2;
24
+ const RESULT_SAVED_TEXT_LIMIT = 200;
20
25
  const REVIEW_LANE_LOOP_DEFAULT_MAX_STEPS = 3;
21
26
  const REVIEW_LANE_LOOP_MAX_STEPS = 10;
22
27
  const REVIEW_LANE_RUN_DEFAULT_MAX_RUNS = 3;
@@ -97,14 +102,23 @@ atris task - durable local task state (SQLite, gitignored)
97
102
  atris task chat <id> "<message>" [--goal "..."] Refine a task chat + working goal
98
103
  atris task ready <id> --proof "..." Agent proof ready; native goal can complete
99
104
  atris task ready <id> --verify "<cmd>" Run <cmd>; only ready if it exits 0 (executed proof)
105
+ atris task plan-preview "<purpose>" [--tag <tag>] [--owner <member>] [--task <id>]
106
+ Show the plain Plan before work starts
107
+ atris task ready <id> --proof "..." [--changed "..." --checked "..." --saved "..." --try "..."]
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
111
+ atris task result <id> --changed "..." --checked "..." [--saved "..."] [--try "..."]
112
+ Show the plain Result and store trace on the task
100
113
  atris task review-chat <id> [--as <owner>] Start a task-owned /codex verification chat
101
- atris task accept <id> [--proof "..."] Human accepts proof, marks done
114
+ atris task accept <id> [--proof "..."] [--public]
115
+ Human accepts proof, marks done; --public also publishes AgentXP
102
116
  atris task auto-accept-certified --dry-run [--strict-verify] [--limit <n>]
103
117
  Preview certified Review rows; live accept needs --confirm-human-accept --as <human>
104
118
  atris task revise <id> --note "..." Send reviewed work back to Do
105
119
 
106
120
  atris task add "<title>" [--tag <tag>] [--goal-id <id>] Create a task
107
- 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
108
122
  atris task plan <id> --goal "..." --exit "..." --proof-needed "..."
109
123
  Record a task-owned Plan stage
110
124
  atris task do <id> --as <owner> --first-move "..."
@@ -137,7 +151,7 @@ atris task - durable local task state (SQLite, gitignored)
137
151
  atris task review <id> --reward <n> [--verify "<cmd>"]
138
152
  Write review event + RSI episode
139
153
  atris task reviews [--limit <n>] Show certified Review items for human accept/revise
140
- 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
141
155
  atris task accept-group <key>=<value> --spot-check K --confirm-human-accept --as <you> --verified <ids>
142
156
  Accept a whole cluster; career XP only on the K you verified
143
157
  atris task status [--json] [--history] Compact live status for web/Swarlo
@@ -301,6 +315,15 @@ function textFlag(args, names) {
301
315
  return '';
302
316
  }
303
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
+
304
327
  function numericFlag(args, name) {
305
328
  const value = flag(args, name);
306
329
  if (value === null || value === true || value === undefined) return null;
@@ -637,6 +660,10 @@ function reviewSummary(task, payload = {}) {
637
660
 
638
661
  const title = String(task.title || 'this task').replace(/\s+/g, ' ').trim();
639
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
+ }
640
667
  const careerText = [
641
668
  task.tag,
642
669
  metadata.goal_id,
@@ -651,20 +678,148 @@ function reviewSummary(task, payload = {}) {
651
678
  || careerText.includes('agent xp')
652
679
  ) {
653
680
  if (task.status === 'done') {
654
- return `This is accepted AgentXP work: ${plainTitle} is done and has a proof receipt.`;
681
+ return `Landed AgentXP result for ${plainTitle}.`;
655
682
  }
656
683
  if (task.status === 'review') {
657
- 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.`;
658
685
  }
659
- return `This explains what accepting ${plainTitle} would make real for AgentXP.`;
686
+ return `This explains what landing ${plainTitle} would make real for AgentXP.`;
660
687
  }
661
688
  if (task.status === 'done') {
662
- return `This is the accepted outcome: ${plainTitle} is done and counted as real work.`;
689
+ return `Landed result for ${plainTitle}.`;
663
690
  }
664
691
  if (task.status === 'review') {
665
- return `This is the human checkpoint: ${plainTitle} is agent-complete and needs acceptance before it counts as done.`;
666
- }
667
- 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
+ };
668
823
  }
669
824
 
670
825
  function taskReviewSummary(task) {
@@ -673,7 +828,7 @@ function taskReviewSummary(task) {
673
828
  const metadata = task.metadata || {};
674
829
  if (!reviewed && !metadata.approval_status && !metadata.agent_review_pass_count && !metadata.human_revision_count && !metadata.agent_certified) return null;
675
830
  if (reviewed && reviewed.event_type === 'revision_requested') {
676
- return reviewSummaryWithVerificationChat(task, {
831
+ const review = {
677
832
  summary: reviewSummary(task, payload),
678
833
  reward: null,
679
834
  proof: null,
@@ -685,7 +840,10 @@ function taskReviewSummary(task) {
685
840
  agent_certification_policy: null,
686
841
  human_revision_count: metadata.human_revision_count || payload.revision_count || null,
687
842
  human_revision_note: metadata.human_revision_note || payload.note || null,
688
- });
843
+ };
844
+ review.landing = taskReviewLanding(task, review, payload);
845
+ review.result = taskReviewResult(task, review, payload);
846
+ return reviewSummaryWithVerificationChat(task, review);
689
847
  }
690
848
  const reviewPassCount = Number(metadata.agent_review_pass_count || payload.review_pass_count || 0);
691
849
  const agentCertified = metadata.agent_certified === true
@@ -705,7 +863,7 @@ function taskReviewSummary(task) {
705
863
  }
706
864
  return payload[key] || metadata[metadataKey] || null;
707
865
  };
708
- return reviewSummaryWithVerificationChat(task, {
866
+ const review = {
709
867
  summary: reviewSummary(task, payload),
710
868
  reward: reviewed && reviewed.event_type === 'reviewed' && payload.reward !== undefined ? payload.reward : null,
711
869
  proof: readyField('proof', 'latest_agent_proof'),
@@ -718,7 +876,10 @@ function taskReviewSummary(task) {
718
876
  || payload.agent_certification_policy
719
877
  || (agentCertified ? `${AGENT_CERTIFICATION_REVIEW_PASSES}_agent_review_passes` : null),
720
878
  human_revision_count: metadata.human_revision_count || null,
721
- });
879
+ };
880
+ review.landing = taskReviewLanding(task, review, payload);
881
+ review.result = taskReviewResult(task, review, payload);
882
+ return reviewSummaryWithVerificationChat(task, review);
722
883
  }
723
884
 
724
885
  function reviewSummaryWithVerificationChat(task, review) {
@@ -943,6 +1104,15 @@ function taskDescriptionForCloud(task) {
943
1104
  lines.push(`- ${message.actor || 'unknown'}: ${message.content}`);
944
1105
  }
945
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
+ }
946
1116
  const reviewed = (task.events || []).slice().reverse().find(e => e.event_type === 'reviewed');
947
1117
  if (reviewed && reviewed.payload) {
948
1118
  if (reviewed.payload.proof) lines.push('', `Proof: ${reviewed.payload.proof}`);
@@ -956,6 +1126,32 @@ function taskDescriptionForCloud(task) {
956
1126
  return lines.join('\n').slice(0, 5000);
957
1127
  }
958
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
+
959
1155
  function cloudPayloadForTask(task, businessId) {
960
1156
  const metadata = task.metadata || {};
961
1157
  const claimedAtEvent = (task.events || []).find(e => e.event_type === 'claimed');
@@ -1373,6 +1569,8 @@ function compactTaskForStatus(task) {
1373
1569
  const review = {};
1374
1570
  if (typeof task.review.reward === 'number') review.reward = task.review.reward;
1375
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;
1376
1574
  if (task.review.summary) review.summary = clipStatusText(task.review.summary, 240);
1377
1575
  if (task.review.proof) review.proof = clipStatusText(task.review.proof, 180);
1378
1576
  if (task.review.lesson) review.lesson = clipStatusText(task.review.lesson, 180);
@@ -1396,7 +1594,7 @@ function compactTaskForStatus(task) {
1396
1594
  if (Object.keys(lineage).length) out.lineage = lineage;
1397
1595
  }
1398
1596
  const compactMetadata = {};
1399
- 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']) {
1400
1598
  if (metadata[key]) compactMetadata[key] = key === 'verify' ? clipStatusText(metadata[key], 180) : metadata[key];
1401
1599
  }
1402
1600
  if (Object.keys(compactMetadata).length) out.metadata = compactMetadata;
@@ -1590,9 +1788,20 @@ function sortTasksNewestFirst(tasks) {
1590
1788
  });
1591
1789
  }
1592
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
+
1593
1799
  function taskQueueItem(task, { reviewer = 'codex-review', hasExistingReviewFollowUp = null } = {}) {
1594
1800
  const page = taskPageContract(task, { reviewer, hasExistingReviewFollowUp });
1595
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);
1596
1805
  if (item.review && item.review.verification_chat) {
1597
1806
  item.review = { ...item.review };
1598
1807
  delete item.review.verification_chat;
@@ -2674,7 +2883,8 @@ function taskQueueContract(projection, { reviewer = 'codex-review', limit = 8, s
2674
2883
  }
2675
2884
 
2676
2885
  function selectTaskForCurrent(projection, { owner = DEFAULT_OWNER, scope = {}, hasExistingReviewFollowUp = null } = {}) {
2677
- const tasks = filterTasksByScope(sortTasksNewestFirst(projection.tasks || []), scope, { hasExistingReviewFollowUp });
2886
+ const normalizedScope = normalizeTaskQueueScope(scope);
2887
+ const tasks = filterTasksByScope(sortTasksNewestFirst(projection.tasks || []), normalizedScope, { hasExistingReviewFollowUp });
2678
2888
  const columns = {
2679
2889
  backlog: [],
2680
2890
  plan: [],
@@ -2695,7 +2905,8 @@ function selectTaskForCurrent(projection, { owner = DEFAULT_OWNER, scope = {}, h
2695
2905
  if (reviewNeedsAgent) return { task: reviewNeedsAgent, reason: 'review_needs_agent_verification' };
2696
2906
  const reviewProofBoundaryBlocked = columns.review.find(task => reviewHandoffForTask(task, { suppressExistingFollowUp: true, hasExistingReviewFollowUp })?.next_action === PROOF_BOUNDARY_BLOCKED_ACTION);
2697
2907
  if (reviewProofBoundaryBlocked) return { task: reviewProofBoundaryBlocked, reason: 'review_proof_boundary_blocked' };
2698
- const planReady = columns.plan[0];
2908
+ const planQueue = normalizedScope.goal_id ? sortTasksOldestFirst(columns.plan) : columns.plan;
2909
+ const planReady = planQueue[0];
2699
2910
  if (planReady) return { task: planReady, reason: 'plan_ready' };
2700
2911
  const backlogIdea = columns.backlog[0];
2701
2912
  if (backlogIdea) return { task: backlogIdea, reason: 'backlog_idea' };
@@ -3813,10 +4024,14 @@ function reviewQueueItem(task, root = process.cwd(), evidence = undefined) {
3813
4024
  tag: task.tag || null,
3814
4025
  updated_at: task.updated_at || null,
3815
4026
  review_pass_count: task.review?.agent_review_pass_count || null,
4027
+ landing: reviewLandingForDisplay(task.review),
4028
+ result: task.review?.result || null,
3816
4029
  proof: taskReviewClip(task.review?.proof, 500) || null,
3817
4030
  next_action: handoff?.next_action || null,
3818
4031
  accept_command: acceptCommand,
4032
+ land_command: acceptCommand,
3819
4033
  revise_command: `atris task revise ${ref} --note "<what must change>"`,
4034
+ send_back_command: `atris task revise ${ref} --note "<what must change>"`,
3820
4035
  };
3821
4036
  const resolvedEvidence = evidence === undefined ? extractReceiptEvidence(task.review?.proof, root) : evidence;
3822
4037
  if (resolvedEvidence) item.evidence = resolvedEvidence;
@@ -3960,13 +4175,13 @@ function cmdReviews(args) {
3960
4175
  printJson({ ok: true, action: 'review_groups', projection_path: outPath, groups });
3961
4176
  return;
3962
4177
  }
3963
- console.log(`CERTIFIED REVIEW — grouped by ${key}`);
3964
- 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)`);
3965
4180
  groups.groups.forEach((g, index) => {
3966
4181
  console.log('');
3967
4182
  console.log(`${index + 1}. ${g.value} — ${g.count} task${g.count === 1 ? '' : 's'}`);
3968
4183
  g.sample_titles.forEach(title => console.log(` • ${title}`));
3969
- 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>`);
3970
4185
  });
3971
4186
  return;
3972
4187
  }
@@ -3980,10 +4195,10 @@ function cmdReviews(args) {
3980
4195
  });
3981
4196
  return;
3982
4197
  }
3983
- console.log('CERTIFIED REVIEW QUEUE');
3984
- 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`);
3985
4200
  if (!queue.items.length) {
3986
- console.log('No certified review items.');
4201
+ console.log('Nothing is ready to land.');
3987
4202
  return;
3988
4203
  }
3989
4204
  queue.items.forEach((item, index) => {
@@ -3992,7 +4207,16 @@ function cmdReviews(args) {
3992
4207
  const badge = item.evidence?.all_passing ? ' [evidence:passing]' : '';
3993
4208
  console.log('');
3994
4209
  console.log(`${index + 1}. ${item.display_id || taskRef(item.id)}${tag}${passes}: ${item.title}${badge}`);
3995
- 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}`);
3996
4220
  if (item.evidence) {
3997
4221
  item.evidence.receipts.forEach((receipt) => {
3998
4222
  const verdict = receipt.verifier_passed === true ? ' verifier:passed'
@@ -4003,9 +4227,9 @@ function cmdReviews(args) {
4003
4227
  }
4004
4228
  if (item.review_chat_command) console.log(` /codex: ${item.review_chat_command}`);
4005
4229
  if (item.continue_work_command) console.log(` continue: ${item.continue_work_command}`);
4006
- if (item.accept_command) console.log(` accept: ${item.accept_command}`);
4007
- else if (item.blocked_accept_reason) console.log(` accept: blocked (${item.blocked_accept_reason})`);
4008
- 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}`);
4009
4233
  });
4010
4234
  if (queue.counts.shown < queue.counts.certified) {
4011
4235
  console.log('');
@@ -4372,13 +4596,9 @@ function delegateHandoff(task, owner, via, tag) {
4372
4596
  function cmdDelegate(args) {
4373
4597
  const pos = positional(args);
4374
4598
  const title = pos.join(' ').trim();
4375
- const owner = flag(args, '--to') || flag(args, '--as');
4376
4599
  if (!title) {
4377
4600
  failTask('atris task delegate', 'missing_title', 'title required');
4378
4601
  }
4379
- if (!owner || owner === true) {
4380
- failTask('atris task delegate', 'missing_owner', '--to <owner> required');
4381
- }
4382
4602
  const viaFlag = flag(args, '--via');
4383
4603
  const via = viaFlag === 'swarlo' ? 'swarlo' : 'local';
4384
4604
  const tag = flag(args, '--tag');
@@ -4386,15 +4606,36 @@ function cmdDelegate(args) {
4386
4606
  const goalId = flag(args, '--goal-id');
4387
4607
  const goalObjective = flag(args, '--goal-objective') || flag(args, '--goal');
4388
4608
  const claimNow = hasFlag(args, '--claim');
4609
+ const requestedOwner = flag(args, '--to') || flag(args, '--as');
4610
+ const explicitExecutedBy = flag(args, '--executed-by');
4389
4611
  const taskDb = getTaskDb();
4390
4612
  const db = taskDb.open();
4391
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;
4392
4626
  const metadata = {
4393
- assigned_to: String(owner),
4627
+ assigned_to: owner,
4394
4628
  delegate_via: via,
4395
4629
  swarlo_channel: via === 'swarlo' ? String(tag || 'tasks') : null,
4396
4630
  created_for_day: new Date().toISOString().slice(0, 10),
4631
+ owner_resolution: ownerResolution.reason,
4397
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
+ }
4398
4639
  if (goalId && goalId !== true) metadata.goal_id = String(goalId);
4399
4640
  if (goalObjective && goalObjective !== true) {
4400
4641
  metadata.task_goal = String(goalObjective);
@@ -4405,7 +4646,7 @@ function cmdDelegate(args) {
4405
4646
  tag: typeof tag === 'string' ? tag : null,
4406
4647
  workspaceRoot: ws,
4407
4648
  status: claimNow ? 'claimed' : 'open',
4408
- claimedBy: claimNow ? String(owner) : null,
4649
+ claimedBy: claimNow ? owner : null,
4409
4650
  metadata,
4410
4651
  });
4411
4652
  if (typeof note === 'string' && note.trim()) {
@@ -4413,14 +4654,16 @@ function cmdDelegate(args) {
4413
4654
  }
4414
4655
  const { projection, outPath } = writeDefaultProjection(taskDb, db);
4415
4656
  const task = compactTaskFromProjection(projection, result.id);
4416
- const handoff = delegateHandoff(task, String(owner), via, typeof tag === 'string' ? tag : null);
4657
+ const handoff = delegateHandoff(task, owner, via, typeof tag === 'string' ? tag : null);
4417
4658
  if (wantsJson(args)) {
4418
4659
  printJson({
4419
4660
  ok: true,
4420
4661
  action: 'delegated',
4421
4662
  task_id: result.id,
4422
4663
  inserted: result.inserted !== false,
4423
- owner: String(owner),
4664
+ owner,
4665
+ owner_resolution: ownerResolution,
4666
+ executed_by: executedBy || null,
4424
4667
  via,
4425
4668
  handoff,
4426
4669
  projection_path: outPath,
@@ -4430,6 +4673,8 @@ function cmdDelegate(args) {
4430
4673
  }
4431
4674
  const tagText = tag && tag !== true ? ` #${tag}` : '';
4432
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}`);
4433
4678
  console.log(`claim: ${handoff.command}`);
4434
4679
  if (handoff.swarlo) console.log(`swarlo: ${handoff.swarlo.channel}/${handoff.swarlo.action}`);
4435
4680
  }
@@ -4520,7 +4765,7 @@ function cmdDay(args) {
4520
4765
  console.log(`stale ${staleFailed.length} failed >7d hidden — atris task list --status failed`);
4521
4766
  }
4522
4767
  console.log('');
4523
- console.log('add: atris task delegate "..." --to codex --tag tasks');
4768
+ console.log('add: atris task delegate "..." --to task-planner --tag tasks');
4524
4769
  }
4525
4770
 
4526
4771
  function cmdHome(args) {
@@ -4776,7 +5021,7 @@ function cmdNext(args) {
4776
5021
  }
4777
5022
  console.log(`created ${taskRef(createdTask)} @${owner}`);
4778
5023
  console.log(createdTask.title);
4779
- 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)}.`);
4780
5025
  console.log(`Verify: ${nextAgentAction.task_seed.verifier}`);
4781
5026
  return;
4782
5027
  }
@@ -4797,12 +5042,12 @@ function cmdNext(args) {
4797
5042
  }
4798
5043
  console.log('No open tasks.');
4799
5044
  console.log(handoff.next_action === 'agent_review_again'
4800
- ? `${taskRef(reviewTask)} needs one more agent review before continuation.`
4801
- : `${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.`);
4802
5047
  console.log(handoff.next_action === 'continue_work'
4803
- ? 'Continue work elsewhere; AgentXP waits for human accept.'
5048
+ ? 'Continue work elsewhere; XP lands after the human lands this task.'
4804
5049
  : handoff.next_action === 'human_accept_waiting'
4805
- ? (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.')
4806
5051
  : 'Review this task again before continuing.');
4807
5052
  if (nextAgentAction) console.log(`Command: ${nextAgentAction.command}`);
4808
5053
  if (nextAgentAction && nextAgentAction.task_seed) {
@@ -5021,7 +5266,8 @@ function cmdPlan(args) {
5021
5266
  const pos = positional(args);
5022
5267
  const id = pos[0];
5023
5268
  if (!id) failTask('atris task plan', 'missing_id', 'id required');
5024
- const actor = String(flag(args, '--as') || DEFAULT_OWNER);
5269
+ const actorFlag = flag(args, '--as');
5270
+ const actor = String(actorFlag || DEFAULT_OWNER);
5025
5271
  const goal = textFlag(args, ['--goal', '--objective']);
5026
5272
  const exit = textFlag(args, ['--exit', '--exit-condition']);
5027
5273
  const proofNeeded = textFlag(args, ['--proof-needed', '--proof', '--verify']);
@@ -5033,18 +5279,29 @@ function cmdPlan(args) {
5033
5279
  const taskDb = getTaskDb();
5034
5280
  const db = taskDb.open();
5035
5281
  const taskId = requireTaskId(taskDb, db, id, 'atris task plan');
5282
+ const task = taskDetail(taskDb, db, taskId);
5283
+ const automaticPlan = buildAutomaticPlanTrace(taskDb, task, {
5284
+ actor,
5285
+ actorExplicit: typeof actorFlag === 'string' && Boolean(actorFlag.trim()),
5286
+ owner,
5287
+ goal,
5288
+ summary,
5289
+ firstMove,
5290
+ exit,
5291
+ });
5036
5292
  const result = taskDb.stageTask(db, {
5037
5293
  id: taskId,
5038
5294
  actor,
5039
5295
  stage: 'plan',
5040
5296
  goal,
5041
5297
  summary,
5042
- owner,
5298
+ owner: automaticPlan.ownerForStage || owner,
5043
5299
  exit,
5044
5300
  proofNeeded,
5045
5301
  firstMove,
5046
5302
  nextButton,
5047
5303
  confidence,
5304
+ planTrace: automaticPlan.trace,
5048
5305
  });
5049
5306
  if (!result.staged) {
5050
5307
  failTask('atris task plan', result.reason || 'stage_failed', stageErrorDetail('atris task plan', result.reason, result), 1);
@@ -5056,6 +5313,11 @@ function cmdPlan(args) {
5056
5313
  action: 'planned',
5057
5314
  task_id: taskId,
5058
5315
  version: result.event.version,
5316
+ plan_trace: automaticPlan.trace ? {
5317
+ plan: automaticPlan.plan,
5318
+ owner_choice: automaticPlan.ownerChoice,
5319
+ trace: automaticPlan.trace,
5320
+ } : null,
5059
5321
  stage_packet: result.stage_packet,
5060
5322
  projection_path: outPath,
5061
5323
  task: compactTaskFromProjection(projection, taskId),
@@ -5196,17 +5458,30 @@ function cmdShow(args) {
5196
5458
  }
5197
5459
  const owner = task.claimed_by ? ` / ${task.claimed_by}` : '';
5198
5460
  const tag = task.tag ? ` #${task.tag}` : '';
5199
- 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}`);
5200
5467
  console.log(task.title);
5201
5468
  if (task.review) {
5202
5469
  console.log('');
5203
- if (task.review.summary) console.log(`Summary: ${task.review.summary}`);
5204
- 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}`);
5205
5473
  if (task.review.lesson) console.log(`Lesson: ${task.review.lesson}`);
5206
5474
  if (task.review.next_task) console.log(`Next: ${task.review.next_task}`);
5207
- if (task.review.approval_status) console.log(`Approval: ${task.review.approval_status}`);
5208
- if (task.review.verification_chat) console.log(`Review chat: ${task.review.verification_chat.command}`);
5209
- 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)`);
5210
5485
  }
5211
5486
  if (task.messages.length) {
5212
5487
  console.log('');
@@ -5248,10 +5523,11 @@ function cmdPage(args) {
5248
5523
  console.log(`TASK PAGE ${taskRef(task)}`);
5249
5524
  console.log(`Goal: ${page.goal.text || '(none)'}`);
5250
5525
  console.log(`Stage: ${page.stage.current}`);
5526
+ printReviewLanding(page.review);
5251
5527
  console.log(`Next: ${page.stage.next_action.command || page.stage.next_action.label}`);
5252
5528
  console.log(`Chat: ${page.chat.command}`);
5253
- if (page.review.verification_chat) console.log(`Review chat: ${page.review.verification_chat.command}`);
5254
- 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}`);
5255
5531
  }
5256
5532
 
5257
5533
  function cmdReviewChat(args) {
@@ -5320,6 +5596,611 @@ function taskCommandQuote(value) {
5320
5596
  return `"${text || '...'}"`;
5321
5597
  }
5322
5598
 
5599
+ function cleanPublicText(value, max = 500) {
5600
+ const text = String(value || '').replace(/\s+/g, ' ').trim();
5601
+ if (!text) return '';
5602
+ return text.length > max ? `${text.slice(0, Math.max(0, max - 3)).trim()}...` : text;
5603
+ }
5604
+
5605
+ function publicWords(value) {
5606
+ return (String(value || '').toLowerCase().match(/[a-z0-9]{3,}/g) || [])
5607
+ .map(word => word.endsWith('s') && word.length > 4 ? word.slice(0, -1) : word)
5608
+ .filter(word => !new Set([
5609
+ 'and',
5610
+ 'for',
5611
+ 'from',
5612
+ 'into',
5613
+ 'the',
5614
+ 'this',
5615
+ 'that',
5616
+ 'task',
5617
+ 'work',
5618
+ 'with',
5619
+ ]).has(word));
5620
+ }
5621
+
5622
+ function parseMemberFrontmatter(text) {
5623
+ const source = String(text || '');
5624
+ if (!source.startsWith('---')) return {};
5625
+ const end = source.indexOf('\n---', 3);
5626
+ if (end === -1) return {};
5627
+ const block = source.slice(3, end).split(/\r?\n/);
5628
+ const data = {};
5629
+ for (const raw of block) {
5630
+ const match = raw.match(/^([A-Za-z0-9_-]+):\s*(.*)$/);
5631
+ if (!match) continue;
5632
+ data[match[1]] = match[2].replace(/^["']|["']$/g, '').trim();
5633
+ }
5634
+ return data;
5635
+ }
5636
+
5637
+ function readTeamMembers(root = process.cwd()) {
5638
+ const teamDir = path.join(root, 'atris', 'team');
5639
+ if (!fs.existsSync(teamDir)) return [];
5640
+ return fs.readdirSync(teamDir, { withFileTypes: true })
5641
+ .filter(entry => entry.isDirectory())
5642
+ .map(entry => {
5643
+ const slug = entry.name;
5644
+ const memberPath = path.join(teamDir, slug, 'MEMBER.md');
5645
+ if (!fs.existsSync(memberPath)) return null;
5646
+ const text = fs.readFileSync(memberPath, 'utf8');
5647
+ const frontmatter = parseMemberFrontmatter(text);
5648
+ return {
5649
+ slug,
5650
+ role: cleanPublicText(frontmatter.role || slug.replace(/[-_]/g, ' '), 120),
5651
+ description: cleanPublicText(frontmatter.description || '', 240),
5652
+ path: memberPath,
5653
+ };
5654
+ })
5655
+ .filter(Boolean);
5656
+ }
5657
+
5658
+ const GENERIC_MEMBER_SLUGS = new Set([
5659
+ '_template',
5660
+ 'coordinator',
5661
+ 'executor',
5662
+ 'generalist',
5663
+ 'navigator',
5664
+ 'supervisor',
5665
+ ]);
5666
+
5667
+ function scoreTeamMember(member, words, tag) {
5668
+ const slug = String(member.slug || '').toLowerCase();
5669
+ const role = String(member.role || '').toLowerCase();
5670
+ const description = String(member.description || '').toLowerCase();
5671
+ const haystack = `${slug} ${role} ${description}`.replace(/[-_]/g, ' ');
5672
+ let score = 0;
5673
+ const cleanTag = String(tag || '').toLowerCase().replace(/[^a-z0-9]+/g, ' ').trim();
5674
+ if (cleanTag) {
5675
+ if (slug === cleanTag || slug.replace(/[-_]/g, ' ') === cleanTag) score += 12;
5676
+ else if (haystack.includes(cleanTag)) score += 5;
5677
+ }
5678
+ for (const word of words) {
5679
+ if (slug.includes(word)) score += 5;
5680
+ else if (role.includes(word)) score += 3;
5681
+ else if (description.includes(word)) score += 2;
5682
+ }
5683
+ if (GENERIC_MEMBER_SLUGS.has(slug)) score -= 3;
5684
+ return score;
5685
+ }
5686
+
5687
+ function plainMemberDescription(member) {
5688
+ const description = cleanPublicText(member && member.description, 220);
5689
+ if (!description) return '';
5690
+ const lowered = `${description.charAt(0).toLowerCase()}${description.slice(1)}`;
5691
+ return /[.!?]$/.test(lowered) ? lowered : `${lowered}.`;
5692
+ }
5693
+
5694
+ function chooseTaskOwner({ purpose, tag, requestedOwner, root = process.cwd() } = {}) {
5695
+ const members = readTeamMembers(root);
5696
+ const requested = cleanPublicText(requestedOwner, 80);
5697
+ if (requested) {
5698
+ const match = members.find(member => member.slug === requested);
5699
+ if (match) {
5700
+ const description = plainMemberDescription(match);
5701
+ return {
5702
+ owner: match.slug,
5703
+ member: match,
5704
+ source: 'requested',
5705
+ reason: `${match.slug} matches this work${description ? ` because ${description}` : '.'}`,
5706
+ };
5707
+ }
5708
+ return {
5709
+ owner: requested,
5710
+ member: null,
5711
+ source: 'requested',
5712
+ reason: `${requested} was requested, but no matching atris/team member was found.`,
5713
+ };
5714
+ }
5715
+ const words = publicWords(`${purpose || ''} ${tag || ''}`);
5716
+ let best = null;
5717
+ for (const member of members) {
5718
+ const score = scoreTeamMember(member, words, tag);
5719
+ if (!best || score > best.score) best = { member, score };
5720
+ }
5721
+ if (best && best.score > 0) {
5722
+ const member = best.member;
5723
+ const description = plainMemberDescription(member);
5724
+ return {
5725
+ owner: member.slug,
5726
+ member,
5727
+ source: 'team',
5728
+ score: best.score,
5729
+ reason: `${member.slug} fits this work${description ? ` because ${description}` : '.'}`,
5730
+ };
5731
+ }
5732
+ return {
5733
+ owner: DEFAULT_OWNER,
5734
+ member: null,
5735
+ source: 'fallback',
5736
+ reason: `${DEFAULT_OWNER} is handling it because no specific atris/team owner matched this work.`,
5737
+ };
5738
+ }
5739
+
5740
+ function isGenericPlanActor(value) {
5741
+ const actor = cleanPublicText(value, 80).toLowerCase();
5742
+ if (!actor) return true;
5743
+ if (actor === String(DEFAULT_OWNER || '').toLowerCase()) return true;
5744
+ return GENERIC_MEMBER_SLUGS.has(actor) || new Set([
5745
+ 'codex',
5746
+ 'codex-executor',
5747
+ 'claude',
5748
+ 'claude-code',
5749
+ 'cursor',
5750
+ 'devin',
5751
+ ]).has(actor);
5752
+ }
5753
+
5754
+ function taskTextMentionsActor(actor, text) {
5755
+ const actorWords = publicWords(actor);
5756
+ if (!actorWords.length) return false;
5757
+ const words = new Set(publicWords(text));
5758
+ return actorWords.some(word => words.has(word));
5759
+ }
5760
+
5761
+ function buildPublicPlan({ purpose, owner, ownerReason, plan, expected }) {
5762
+ const cleanPurpose = cleanPublicText(purpose, 240);
5763
+ const cleanOwner = cleanPublicText(owner, 80);
5764
+ const cleanReason = cleanPublicText(ownerReason, 240);
5765
+ const cleanPlan = cleanPublicText(plan, 320) || `${cleanOwner || 'The owner'} will make the smallest needed change, then check the result.`;
5766
+ const cleanExpected = cleanPublicText(expected, 240) || 'the check passes and the result is ready to review.';
5767
+ return {
5768
+ purpose: cleanPurpose,
5769
+ owner: cleanOwner,
5770
+ owner_reason: cleanReason,
5771
+ plan: cleanPlan,
5772
+ expected_result: cleanExpected,
5773
+ };
5774
+ }
5775
+
5776
+ function renderPublicPlan(plan) {
5777
+ const lines = [];
5778
+ if (plan.purpose) lines.push(`Purpose: ${plan.purpose}`);
5779
+ if (plan.owner) lines.push(`Owner: ${plan.owner} is handling it.`);
5780
+ if (plan.owner_reason) lines.push(`Why: ${plan.owner_reason}`);
5781
+ if (plan.plan) lines.push(`Plan: ${plan.plan}`);
5782
+ if (plan.expected_result) lines.push(`Expected result: ${plan.expected_result}`);
5783
+ return lines.join('\n');
5784
+ }
5785
+
5786
+ function planTraceData(plan, ownerChoice) {
5787
+ return {
5788
+ schema: 'atris.task_plan_trace.v1',
5789
+ purpose: plan.purpose,
5790
+ owner: plan.owner,
5791
+ owner_reason: plan.owner_reason,
5792
+ plan: plan.plan,
5793
+ expected_result: plan.expected_result,
5794
+ owner_source: ownerChoice && ownerChoice.source || null,
5795
+ owner_score: ownerChoice && ownerChoice.score || null,
5796
+ recorded_at: new Date().toISOString(),
5797
+ };
5798
+ }
5799
+
5800
+ function planTraceNote(plan, ownerChoice) {
5801
+ return `TASK_PLAN_TRACE ${JSON.stringify(planTraceData(plan, ownerChoice))}`;
5802
+ }
5803
+
5804
+ function traceLineFromContent(content, prefix) {
5805
+ const lines = String(content || '').split(/\r?\n/);
5806
+ return lines.find(line => line.startsWith(prefix)) || '';
5807
+ }
5808
+
5809
+ function latestTraceValue(task, prefix, key) {
5810
+ const messages = Array.isArray(task && task.messages) ? task.messages : [];
5811
+ for (let i = messages.length - 1; i >= 0; i--) {
5812
+ const content = String(messages[i] && messages[i].content || '');
5813
+ const line = traceLineFromContent(content, prefix);
5814
+ if (!line) continue;
5815
+ try {
5816
+ const parsed = JSON.parse(line.slice(prefix.length).trim());
5817
+ const value = parsed && parsed[key];
5818
+ if (value !== undefined && value !== null && String(value).trim()) return String(value);
5819
+ } catch (_) {
5820
+ continue;
5821
+ }
5822
+ }
5823
+ return '';
5824
+ }
5825
+
5826
+ function taskHasTrace(task, prefix) {
5827
+ const messages = Array.isArray(task && task.messages) ? task.messages : [];
5828
+ if (messages.some(message => traceLineFromContent(message && message.content, prefix))) return true;
5829
+ const events = Array.isArray(task && task.events) ? task.events : [];
5830
+ return events.some(event => {
5831
+ const payload = event && event.payload && typeof event.payload === 'object' ? event.payload : {};
5832
+ if (traceLineFromContent(payload.stage_packet, prefix)) return true;
5833
+ if (traceLineFromContent(payload.result_packet, prefix)) return true;
5834
+ if (prefix === 'TASK_RESULT_TRACE ' && payload.result_trace && typeof payload.result_trace === 'object') return true;
5835
+ if (prefix === 'TASK_PLAN_TRACE ' && payload.plan_trace && typeof payload.plan_trace === 'object') return true;
5836
+ return false;
5837
+ });
5838
+ }
5839
+
5840
+ function taskPurpose(task) {
5841
+ const metadata = task && task.metadata || {};
5842
+ return cleanPublicText(
5843
+ metadata.task_goal
5844
+ || metadata.goal_objective
5845
+ || metadata.objective
5846
+ || metadata.stage_goal
5847
+ || latestTraceValue(task, 'TASK_PLAN_TRACE ', 'purpose')
5848
+ || task && task.title
5849
+ || '',
5850
+ 240,
5851
+ );
5852
+ }
5853
+
5854
+ function buildPublicResult(task, fields) {
5855
+ const owner = cleanPublicText(
5856
+ fields.owner
5857
+ || latestTraceValue(task, 'TASK_PLAN_TRACE ', 'owner')
5858
+ || taskAssignee(task)
5859
+ || task && task.claimed_by
5860
+ || fields.actor,
5861
+ 80,
5862
+ );
5863
+ const result = {
5864
+ purpose: cleanPublicText(fields.purpose || taskPurpose(task), 240),
5865
+ owner,
5866
+ changed: cleanPublicText(fields.changed, 320),
5867
+ checked: cleanPublicText(fields.checked, 320),
5868
+ passed: cleanPublicText(fields.passed, 240),
5869
+ failed: cleanPublicText(fields.failed, 240),
5870
+ cost: cleanPublicText(fields.cost, 80),
5871
+ saved: cleanPublicText(fields.saved, RESULT_SAVED_TEXT_LIMIT),
5872
+ try_next: cleanPublicText(fields.tryNext, 240),
5873
+ status: cleanPublicText(fields.status, 160) || 'ready for review',
5874
+ };
5875
+ return result;
5876
+ }
5877
+
5878
+ function renderPublicResult(result) {
5879
+ return [
5880
+ `Changed: ${cleanPublicText(result && result.changed, 320) || 'changed the requested work'}`,
5881
+ `Checked: ${cleanPublicText(result && result.checked, 320) || 'checked the result'}`,
5882
+ `Try: ${cleanPublicText(result && result.try_next, 240) || 'try the changed work'}`,
5883
+ ].join('\n');
5884
+ }
5885
+
5886
+ function latestResultTrace(task) {
5887
+ const messages = Array.isArray(task && task.messages) ? task.messages : [];
5888
+ for (let i = messages.length - 1; i >= 0; i--) {
5889
+ const content = String(messages[i] && messages[i].content || '');
5890
+ const line = traceLineFromContent(content, 'TASK_RESULT_TRACE ');
5891
+ if (!line) continue;
5892
+ try {
5893
+ const parsed = JSON.parse(line.slice('TASK_RESULT_TRACE '.length).trim());
5894
+ if (parsed && typeof parsed === 'object') return parsed;
5895
+ } catch (_) {}
5896
+ }
5897
+
5898
+ const events = Array.isArray(task && task.events) ? task.events : [];
5899
+ for (let i = events.length - 1; i >= 0; i--) {
5900
+ const payload = events[i] && events[i].payload && typeof events[i].payload === 'object'
5901
+ ? events[i].payload
5902
+ : {};
5903
+ if (payload.result_trace && typeof payload.result_trace === 'object') return payload.result_trace;
5904
+ const packetLine = traceLineFromContent(payload.result_packet, 'TASK_RESULT_TRACE ');
5905
+ if (!packetLine) continue;
5906
+ try {
5907
+ const parsed = JSON.parse(packetLine.slice('TASK_RESULT_TRACE '.length).trim());
5908
+ if (parsed && typeof parsed === 'object') return parsed;
5909
+ } catch (_) {}
5910
+ }
5911
+ return null;
5912
+ }
5913
+
5914
+ function buildAcceptHumanResult({ task, proof, nextTask, publicSync }) {
5915
+ const trace = latestResultTrace(task) || {};
5916
+ const traceChanged = cleanPublicText(trace.changed, 320);
5917
+ const changed = traceChanged === 'prepared the work for review'
5918
+ ? 'accepted the completed work'
5919
+ : traceChanged
5920
+ || cleanPublicText(task && task.title, 260)
5921
+ || 'accepted the completed work';
5922
+ let checked = cleanPublicText(trace.checked, 320)
5923
+ || cleanPublicText(proof, 320)
5924
+ || 'checked the proof';
5925
+ if (publicSync && publicSync.enabled === true && !publicSync.ok) {
5926
+ const error = cleanPublicText(publicSync.error || 'publish failed', 140);
5927
+ checked = `${checked}; AgentXP publish failed${error ? ` (${error})` : ''}`;
5928
+ }
5929
+ const tryNext = cleanPublicText(nextTask, 240)
5930
+ || cleanPublicText(trace.try_next, 240)
5931
+ || 'try the changed work';
5932
+ return { changed, checked, try_next: tryNext };
5933
+ }
5934
+
5935
+ function renderAcceptLanding({ task, proof, nextTask, publicSync }) {
5936
+ return renderPublicResult(buildAcceptHumanResult({
5937
+ task,
5938
+ proof,
5939
+ nextTask,
5940
+ publicSync,
5941
+ }));
5942
+ }
5943
+
5944
+ async function publishAcceptAgentXp(args, actor) {
5945
+ const token = flag(args, '--token');
5946
+ const syncArgs = ['--all', '--root', process.cwd(), '--public', '--as', actor];
5947
+ if (typeof token === 'string' && token.trim()) syncArgs.push('--token', token.trim());
5948
+ try {
5949
+ const { syncAgentXp } = require('../commands/xp');
5950
+ const result = await syncAgentXp(syncArgs);
5951
+ const server = result && result.server ? result.server : {};
5952
+ const publicCount = Number(server.public_accepted_count);
5953
+ const acceptedCount = Number(server.accepted_count);
5954
+ const published = (
5955
+ (Number.isFinite(publicCount) && publicCount > 0)
5956
+ || (Number.isFinite(acceptedCount) && acceptedCount > 0 && server.private_agentxp !== true)
5957
+ );
5958
+ return {
5959
+ enabled: true,
5960
+ ok: published,
5961
+ result,
5962
+ error: published ? null : 'server accepted no public AgentXP rows',
5963
+ };
5964
+ } catch (error) {
5965
+ return {
5966
+ enabled: true,
5967
+ ok: false,
5968
+ error: error && error.message ? error.message : String(error),
5969
+ };
5970
+ }
5971
+ }
5972
+
5973
+ function todayResultLogName() {
5974
+ const now = new Date();
5975
+ return `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}-${String(now.getDate()).padStart(2, '0')}.md`;
5976
+ }
5977
+
5978
+ function appendResultOwnerLog(root, task, result) {
5979
+ const owner = cleanPublicText(result && result.owner, 80);
5980
+ if (!owner || !/^[A-Za-z0-9._-]+$/.test(owner)) return null;
5981
+ const memberFile = path.join(root, 'atris', 'team', owner, 'MEMBER.md');
5982
+ if (!fs.existsSync(memberFile)) return null;
5983
+ const logName = todayResultLogName();
5984
+ const logDir = path.join(root, 'atris', 'team', owner, 'logs');
5985
+ fs.mkdirSync(logDir, { recursive: true });
5986
+ const logPath = path.join(logDir, logName);
5987
+ const stamp = new Date().toTimeString().slice(0, 5);
5988
+ const lines = [
5989
+ `## ${stamp} - Result`,
5990
+ `- task: ${taskRef(task)}`,
5991
+ `- purpose: ${result.purpose || ''}`,
5992
+ `- result: ${result.changed || ''}`,
5993
+ `- checked: ${result.checked || ''}`,
5994
+ `- saved: ${result.saved || ''}`,
5995
+ `- try: ${result.try_next || ''}`,
5996
+ `- status: ${result.status || ''}`,
5997
+ '',
5998
+ ];
5999
+ fs.appendFileSync(logPath, lines.join('\n'), 'utf8');
6000
+ return {
6001
+ member_log_path: path.relative(root, logPath),
6002
+ };
6003
+ }
6004
+
6005
+ function resultTraceData(result, fields) {
6006
+ return {
6007
+ schema: 'atris.task_result_trace.v1',
6008
+ purpose: result.purpose,
6009
+ owner: result.owner || null,
6010
+ changed: result.changed,
6011
+ checked: result.checked,
6012
+ passed: result.passed || null,
6013
+ failed: result.failed || null,
6014
+ cost: result.cost || null,
6015
+ saved: result.saved || null,
6016
+ try_next: result.try_next || null,
6017
+ status: result.status,
6018
+ files: cleanPublicText(fields.files, 500) || null,
6019
+ commands: cleanPublicText(fields.commands, 500) || null,
6020
+ member_log_path: fields.savedPaths && fields.savedPaths.member_log_path || null,
6021
+ recorded_at: new Date().toISOString(),
6022
+ };
6023
+ }
6024
+
6025
+ function resultTraceNote(result, fields) {
6026
+ return `TASK_RESULT_TRACE ${JSON.stringify(resultTraceData(result, fields))}`;
6027
+ }
6028
+
6029
+ function cmdPlanPreview(args) {
6030
+ const pos = positional(args);
6031
+ const purpose = cleanPublicText(textFlag(args, ['--purpose', '--goal', '--objective']) || pos.join(' '), 240);
6032
+ if (!purpose) failTask('atris task plan-preview', 'missing_purpose', 'purpose required');
6033
+ const tag = textFlag(args, ['--tag']);
6034
+ const requestedOwner = textFlag(args, ['--owner', '--as', '--member']);
6035
+ const planText = textFlag(args, ['--plan', '--action', '--first-move']);
6036
+ const expected = textFlag(args, ['--expected', '--expected-result', '--exit']);
6037
+ const recordRef = textFlag(args, ['--task', '--record']);
6038
+ const taskDb = getTaskDb();
6039
+ const db = taskDb.open();
6040
+ const ownerChoice = chooseTaskOwner({
6041
+ purpose,
6042
+ tag,
6043
+ requestedOwner,
6044
+ root: taskDb.workspaceRoot(),
6045
+ });
6046
+ const publicPlan = buildPublicPlan({
6047
+ purpose,
6048
+ owner: ownerChoice.owner,
6049
+ ownerReason: ownerChoice.reason,
6050
+ plan: planText,
6051
+ expected,
6052
+ });
6053
+ let recorded = null;
6054
+ if (recordRef) {
6055
+ const taskId = requireTaskId(taskDb, db, recordRef, 'atris task plan-preview');
6056
+ const note = taskDb.noteTask(db, {
6057
+ id: taskId,
6058
+ actor: publicPlan.owner || DEFAULT_OWNER,
6059
+ content: planTraceNote(publicPlan, ownerChoice),
6060
+ });
6061
+ if (!note.noted) failTask('atris task plan-preview', note.reason || 'note_failed', `plan-preview failed: ${note.reason || 'note_failed'}`, 1);
6062
+ const { outPath } = writeDefaultProjection(taskDb, db);
6063
+ recorded = {
6064
+ task_id: taskId,
6065
+ version: note.event.version,
6066
+ projection_path: outPath,
6067
+ };
6068
+ }
6069
+ if (wantsJson(args)) {
6070
+ printJson({
6071
+ ok: true,
6072
+ action: 'plan_preview',
6073
+ plan: publicPlan,
6074
+ owner_choice: {
6075
+ owner: ownerChoice.owner,
6076
+ source: ownerChoice.source,
6077
+ score: ownerChoice.score || null,
6078
+ member: ownerChoice.member ? {
6079
+ slug: ownerChoice.member.slug,
6080
+ role: ownerChoice.member.role,
6081
+ description: ownerChoice.member.description,
6082
+ } : null,
6083
+ },
6084
+ recorded,
6085
+ text: renderPublicPlan(publicPlan),
6086
+ });
6087
+ return;
6088
+ }
6089
+ console.log(renderPublicPlan(publicPlan));
6090
+ }
6091
+
6092
+ function buildAutomaticPlanTrace(taskDb, task, { actor, actorExplicit = false, owner, goal, summary, firstMove, exit } = {}) {
6093
+ if (!task) return { trace: null, plan: null, ownerChoice: null, ownerForStage: owner || null };
6094
+ const metadata = task.metadata || {};
6095
+ const purpose = cleanPublicText(goal, 240) || taskPurpose(task);
6096
+ const claimedOwner = cleanPublicText(task.claimed_by, 80);
6097
+ const actorNamed = taskTextMentionsActor(actor, `${purpose} ${task.title || ''} ${task.tag || ''}`);
6098
+ const requestedActor = actorExplicit && (!isGenericPlanActor(actor) || actorNamed) ? actor : null;
6099
+ const requestedOwner = owner || claimedOwner || requestedActor || null;
6100
+ const ownerChoice = chooseTaskOwner({
6101
+ purpose,
6102
+ tag: task.tag,
6103
+ requestedOwner,
6104
+ root: taskDb.workspaceRoot(),
6105
+ });
6106
+ const publicPlan = buildPublicPlan({
6107
+ purpose,
6108
+ owner: ownerChoice.owner,
6109
+ ownerReason: ownerChoice.reason,
6110
+ plan: firstMove || summary || metadata.first_move || metadata.stage_summary || '',
6111
+ expected: exit || metadata.exit_condition || '',
6112
+ });
6113
+ return {
6114
+ trace: planTraceData(publicPlan, ownerChoice),
6115
+ plan: publicPlan,
6116
+ ownerChoice: {
6117
+ owner: ownerChoice.owner,
6118
+ source: ownerChoice.source,
6119
+ score: ownerChoice.score || null,
6120
+ },
6121
+ ownerForStage: publicPlan.owner || actor || DEFAULT_OWNER,
6122
+ };
6123
+ }
6124
+
6125
+ function buildAutomaticResultTrace(taskDb, db, taskId, { actor, proof, changed, checked, passed, failed, cost, saved, tryNext, status, files, commands } = {}) {
6126
+ const task = taskDetail(taskDb, db, taskId);
6127
+ if (!task || taskHasTrace(task, 'TASK_RESULT_TRACE ')) return null;
6128
+ const fields = {
6129
+ actor,
6130
+ changed: cleanPublicText(changed, 320) || 'prepared the work for review',
6131
+ checked: cleanPublicText(checked, 320) || cleanPublicText(proof, 320),
6132
+ passed: cleanPublicText(passed, 240),
6133
+ failed: cleanPublicText(failed, 240),
6134
+ cost: cleanPublicText(cost, 80),
6135
+ saved: cleanPublicText(saved, RESULT_SAVED_TEXT_LIMIT) || 'task trace was updated',
6136
+ tryNext: cleanPublicText(tryNext, 240) || 'review the proof and try the changed work',
6137
+ status: cleanPublicText(status, 160) || 'ready for review',
6138
+ files,
6139
+ commands,
6140
+ };
6141
+ const result = buildPublicResult(task, fields);
6142
+ const savedPaths = appendResultOwnerLog(taskDb.workspaceRoot(), task, result);
6143
+ const trace = resultTraceData(result, { ...fields, savedPaths });
6144
+ return {
6145
+ result,
6146
+ trace,
6147
+ saved_paths: savedPaths,
6148
+ };
6149
+ }
6150
+
6151
+ function cmdResult(args) {
6152
+ const pos = positional(args);
6153
+ const id = pos[0];
6154
+ if (!id) failTask('atris task result', 'missing_id', 'id required');
6155
+ const fields = {
6156
+ purpose: textFlag(args, ['--purpose', '--goal', '--objective']),
6157
+ changed: textFlag(args, ['--changed', '--result', '--done']),
6158
+ checked: textFlag(args, ['--checked', '--check', '--verified']),
6159
+ passed: textFlag(args, ['--passed', '--pass']),
6160
+ failed: textFlag(args, ['--failed', '--fail']),
6161
+ cost: textFlag(args, ['--cost']),
6162
+ saved: textFlag(args, ['--saved', '--savings']),
6163
+ tryNext: textFlag(args, ['--try', '--try-next', '--handoff']),
6164
+ status: textFlag(args, ['--status']),
6165
+ files: textFlag(args, ['--files']),
6166
+ commands: textFlag(args, ['--commands', '--command']),
6167
+ };
6168
+ if (!fields.changed) failTask('atris task result', 'changed_required', '--changed required');
6169
+ if (!fields.checked) failTask('atris task result', 'checked_required', '--checked required');
6170
+ if (!fields.tryNext) failTask('atris task result', 'try_required', '--try required');
6171
+ const actor = String(flag(args, '--as') || DEFAULT_OWNER);
6172
+ const taskDb = getTaskDb();
6173
+ const db = taskDb.open();
6174
+ const taskId = requireTaskId(taskDb, db, id, 'atris task result');
6175
+ const task = taskDetail(taskDb, db, taskId);
6176
+ if (!task) failTask('atris task result', 'not_found', `task not found: ${id}`, 1);
6177
+ const result = buildPublicResult(task, { ...fields, actor });
6178
+ const savedPaths = appendResultOwnerLog(taskDb.workspaceRoot(), task, result);
6179
+ const note = taskDb.noteTask(db, {
6180
+ id: taskId,
6181
+ actor,
6182
+ content: resultTraceNote(result, { ...fields, savedPaths }),
6183
+ });
6184
+ if (!note.noted) failTask('atris task result', note.reason || 'note_failed', `result failed: ${note.reason || 'note_failed'}`, 1);
6185
+ const { projection, outPath } = writeDefaultProjection(taskDb, db);
6186
+ const text = renderPublicResult(result);
6187
+ if (wantsJson(args)) {
6188
+ printJson({
6189
+ ok: true,
6190
+ action: 'result',
6191
+ task_id: taskId,
6192
+ version: note.event.version,
6193
+ projection_path: outPath,
6194
+ result,
6195
+ saved_paths: savedPaths,
6196
+ text,
6197
+ task: compactTaskFromProjection(projection, taskId),
6198
+ });
6199
+ return;
6200
+ }
6201
+ console.log(text);
6202
+ }
6203
+
5323
6204
  function taskPageGoal(task) {
5324
6205
  const metadata = task && task.metadata || {};
5325
6206
  const candidates = [
@@ -5383,7 +6264,7 @@ function taskPageActions(task, { reviewer = 'codex-review', hasExistingReviewFol
5383
6264
  note_command: `atris task note ${ref} "<context>" --as ${owner}`,
5384
6265
  plan_command: `atris task plan ${ref} --goal ${taskCommandQuote(goal)} --exit "<exit condition>" --proof-needed "<verification command>" --first-move "<first move>"`,
5385
6266
  do_command: `atris task do ${ref} --as ${owner} --first-move "<first move>"`,
5386
- 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>"`,
5387
6268
  review_command: `atris task review ${ref} --reward 0 --as ${actor} --proof "<specific proof command/result>" --verify "<safe verifier command>"`,
5388
6269
  };
5389
6270
  if (task && task.status === 'review') {
@@ -5441,7 +6322,7 @@ function taskPageNextAction(task, current, actions, { hasExistingReviewFollowUp
5441
6322
  if (handoff && handoff.next_action === 'human_accept_waiting') {
5442
6323
  return {
5443
6324
  key: 'human_accept_waiting',
5444
- label: 'Waiting for human accept',
6325
+ label: 'Ready to land',
5445
6326
  command: null,
5446
6327
  api: null,
5447
6328
  human_accept_command: actions.human_accept_command || null,
@@ -5462,6 +6343,7 @@ function taskPageContract(task, { reviewer = 'codex-review', hasExistingReviewFo
5462
6343
  const metadata = task && task.metadata || {};
5463
6344
  const current = taskPageCurrentStage(task);
5464
6345
  const actions = taskPageActions(task, { reviewer, hasExistingReviewFollowUp });
6346
+ const review = task && (task.review || taskReviewSummary(task));
5465
6347
  const recentMessages = (task && Array.isArray(task.messages) ? task.messages : []).slice(-10).map(message => ({
5466
6348
  version: message.version || null,
5467
6349
  actor: message.actor || null,
@@ -5505,9 +6387,13 @@ function taskPageContract(task, { reviewer = 'codex-review', hasExistingReviewFo
5505
6387
  },
5506
6388
  actions,
5507
6389
  review: {
5508
- approval_status: task.review && task.review.approval_status || metadata.approval_status || null,
5509
- agent_review_pass_count: task.review && task.review.agent_review_pass_count || metadata.agent_review_pass_count || null,
5510
- 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),
5511
6397
  verification_chat: reviewChat,
5512
6398
  handoff: reviewHandoff,
5513
6399
  human_accept: {
@@ -5558,7 +6444,7 @@ function appendTaskReviewChat(taskDb, db, taskId, { reviewer = 'codex-review', d
5558
6444
  throw taskReviewChatError(`not_reviewable_${task.status}`, `review chat requires a task in Review; current status is ${task.status}`, { status: 409, exitCode: 1 });
5559
6445
  }
5560
6446
  if (!taskAllowsReviewChat(task, { allowCertified: true })) {
5561
- 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 });
5562
6448
  }
5563
6449
  const contract = taskReviewChatContract(task, { reviewer: actor, allowCertified: true });
5564
6450
  let event = null;
@@ -5715,8 +6601,8 @@ function readyHandoffForStep(task, proof, lesson, nextTask, agentCertified) {
5715
6601
  career_xp_status: 'pending_human_accept',
5716
6602
  next_action: agentCertified ? certifiedReviewNextAction(nextTask) : 'agent_review_again',
5717
6603
  rule: agentCertified
5718
- ? 'Agent double-check complete; continue work. AgentXP waits for human accept.'
5719
- : '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.',
5720
6606
  };
5721
6607
  if (reviewChat) {
5722
6608
  handoff.review_chat_command = reviewChat.command;
@@ -5737,7 +6623,7 @@ function runTaskStep(taskDb, db, taskId, options = {}) {
5737
6623
  const reason = initialHandoffState.next_action === 'continue_work'
5738
6624
  ? 'agent_certified_continue_work'
5739
6625
  : 'agent_certified_waiting_human';
5740
- 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 });
5741
6627
  }
5742
6628
  let chat = null;
5743
6629
  const message = String(options.message || '').trim();
@@ -5764,16 +6650,25 @@ function runTaskStep(taskDb, db, taskId, options = {}) {
5764
6650
  let episode = null;
5765
6651
  let xpProjection = null;
5766
6652
  if (current === 'backlog') {
6653
+ const automaticPlan = buildAutomaticPlanTrace(taskDb, task, {
6654
+ actor,
6655
+ owner: actor,
6656
+ goal,
6657
+ summary,
6658
+ firstMove: String(options.firstMove || ''),
6659
+ exit: String(options.exit || ''),
6660
+ });
5767
6661
  const planned = taskDb.stageTask(db, {
5768
6662
  id: taskId,
5769
6663
  actor,
5770
6664
  stage: 'plan',
5771
6665
  goal,
5772
6666
  summary,
5773
- owner: actor,
6667
+ owner: automaticPlan.ownerForStage || actor,
5774
6668
  exit: String(options.exit || ''),
5775
6669
  proofNeeded: String(options.proofNeeded || ''),
5776
6670
  firstMove: String(options.firstMove || ''),
6671
+ planTrace: automaticPlan.trace,
5777
6672
  });
5778
6673
  if (!planned.staged) taskStepFailure('atris task step', planned, actionPage);
5779
6674
  stepAction = 'planned';
@@ -5807,7 +6702,15 @@ function runTaskStep(taskDb, db, taskId, options = {}) {
5807
6702
  }
5808
6703
  const lesson = String(options.lesson || '');
5809
6704
  const nextTask = String(options.nextTask || '');
5810
- const ready = taskDb.readyTask(db, { id: taskId, actor, proof, lesson, nextTask });
6705
+ const resultTrace = buildAutomaticResultTrace(taskDb, db, taskId, { actor, proof });
6706
+ const ready = taskDb.readyTask(db, {
6707
+ id: taskId,
6708
+ actor,
6709
+ proof,
6710
+ lesson,
6711
+ nextTask,
6712
+ resultTrace: resultTrace && resultTrace.trace,
6713
+ });
5811
6714
  if (!ready.ready) taskStepFailure('atris task step', ready, actionPage);
5812
6715
  task = taskDetail(taskDb, db, taskId) || task;
5813
6716
  stepAction = 'ready';
@@ -5822,7 +6725,7 @@ function runTaskStep(taskDb, db, taskId, options = {}) {
5822
6725
  const reason = handoffState.next_action === 'continue_work'
5823
6726
  ? 'agent_certified_continue_work'
5824
6727
  : 'agent_certified_waiting_human';
5825
- 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 });
5826
6729
  }
5827
6730
  const reviewed = appendTaskReviewChat(taskDb, db, taskId, { reviewer, dryRun: Boolean(options.dryRun) });
5828
6731
  stepAction = 'review_chat';
@@ -5930,7 +6833,7 @@ function runCurrentTaskStep(taskDb, db, { owner = DEFAULT_OWNER, reviewer = 'cod
5930
6833
  if (nextActionKey === 'human_accept_waiting') {
5931
6834
  const error = taskStepError(
5932
6835
  'agent_certified_waiting_human',
5933
- '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',
5934
6837
  {
5935
6838
  status: 409,
5936
6839
  exitCode: 1,
@@ -6342,16 +7245,36 @@ function cmdReady(args) {
6342
7245
  requireMeaningfulTaskProof('atris task ready', proof);
6343
7246
  const lesson = flag(args, '--lesson') || '';
6344
7247
  const nextTaskInput = normalizeReviewNextTaskInput(typeof flag(args, '--next') === 'string' ? flag(args, '--next') : '');
7248
+ const landing = landingFlags(args);
6345
7249
  const actor = String(flag(args, '--as') || DEFAULT_OWNER);
7250
+ const resultFields = {
7251
+ changed: textFlag(args, ['--changed', '--result', '--done']),
7252
+ checked: textFlag(args, ['--checked', '--check', '--verified']),
7253
+ passed: textFlag(args, ['--passed', '--pass']),
7254
+ failed: textFlag(args, ['--failed', '--fail']),
7255
+ cost: textFlag(args, ['--cost']),
7256
+ saved: textFlag(args, ['--saved', '--savings']),
7257
+ tryNext: textFlag(args, ['--try', '--try-next', '--handoff']),
7258
+ status: textFlag(args, ['--status']),
7259
+ files: textFlag(args, ['--files']),
7260
+ commands: textFlag(args, ['--commands', '--command']),
7261
+ };
6346
7262
  const taskDb = getTaskDb();
6347
7263
  const db = taskDb.open();
6348
7264
  const taskId = requireTaskId(taskDb, db, id, 'atris task ready');
7265
+ const resultTrace = buildAutomaticResultTrace(taskDb, db, taskId, {
7266
+ actor,
7267
+ proof: String(proof),
7268
+ ...resultFields,
7269
+ });
6349
7270
  const result = taskDb.readyTask(db, {
6350
7271
  id: taskId,
6351
7272
  actor,
6352
7273
  proof: String(proof),
6353
7274
  lesson: typeof lesson === 'string' ? lesson : '',
6354
7275
  nextTask: nextTaskInput.nextTask,
7276
+ resultTrace: resultTrace && resultTrace.trace,
7277
+ landing,
6355
7278
  });
6356
7279
  if (!result.ready) {
6357
7280
  console.error(`ready failed: ${result.reason}`);
@@ -6373,8 +7296,8 @@ function cmdReady(args) {
6373
7296
  career_xp_status: 'pending_human_accept',
6374
7297
  next_action: agentCertified ? certifiedReviewNextAction(nextTaskInput.nextTask) : 'agent_review_again',
6375
7298
  rule: agentCertified
6376
- ? 'Agent double-check complete; continue work. AgentXP waits for human accept.'
6377
- : '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.',
6378
7301
  };
6379
7302
  if (reviewChat) {
6380
7303
  handoff.review_chat_command = reviewChat.command;
@@ -6397,20 +7320,22 @@ function cmdReady(args) {
6397
7320
  review_pass_count: result.event.payload.review_pass_count,
6398
7321
  agent_certified: agentCertified,
6399
7322
  handoff,
7323
+ result_trace: resultTrace,
6400
7324
  ...(nextTaskInput.ignored ? { review_next_task_ignored: nextTaskInput.ignored } : {}),
6401
7325
  projection_path: outPath,
6402
7326
  task: compactTaskFromProjection(projection, taskId),
6403
7327
  });
6404
7328
  return;
6405
7329
  }
6406
- 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}`);
7331
+ if (resultTrace) console.log('Result trace recorded.');
6407
7332
  console.log(handoff.rule);
6408
7333
  for (const hint of policyHints) {
6409
7334
  console.log(`policy (${hint.id}): ${hint.hint}`);
6410
7335
  }
6411
7336
  }
6412
7337
 
6413
- function cmdAccept(args) {
7338
+ async function cmdAccept(args) {
6414
7339
  const pos = positional(args);
6415
7340
  const id = pos[0];
6416
7341
  if (!id) {
@@ -6490,6 +7415,7 @@ function cmdAccept(args) {
6490
7415
  // Inform the gate, never block it: show what the receipts named in the proof
6491
7416
  // actually say so the accepting human isn't trusting prose.
6492
7417
  const evidence = extractReceiptEvidence(proof, projection.workspace_root || process.cwd());
7418
+ const publicSync = hasFlag(args, '--public') ? await publishAcceptAgentXp(args, actor) : null;
6493
7419
  if (wantsJson(args)) {
6494
7420
  printJson({
6495
7421
  ok: true,
@@ -6499,21 +7425,21 @@ function cmdAccept(args) {
6499
7425
  reward: reviewed.episode.reward.value,
6500
7426
  episode: reviewed.episode,
6501
7427
  evidence,
7428
+ public_sync: publicSync,
6502
7429
  xp_projection: xpProjection,
6503
7430
  projection_path: outPath,
6504
7431
  task: compactTaskFromProjection(projection, taskId),
6505
7432
  });
7433
+ if (publicSync && !publicSync.ok) process.exitCode = 1;
6506
7434
  return;
6507
7435
  }
6508
- console.log(`accepted ${taskRef(compactTaskFromProjection(projection, taskId))} reward=${reviewed.episode.reward.value}`);
6509
- if (evidence) {
6510
- evidence.receipts.forEach((receipt) => {
6511
- const verdict = receipt.verifier_passed === true ? ' verifier:passed'
6512
- : receipt.verifier_passed === false ? ' verifier:FAILED' : '';
6513
- console.log(` receipt: ${receipt.path}${verdict}`);
6514
- });
6515
- evidence.missing.forEach((missingPath) => console.log(` receipt: ${missingPath} MISSING`));
6516
- }
7436
+ console.log(renderAcceptLanding({
7437
+ task: taskDetail(taskDb, db, taskId) || compactTaskFromProjection(projection, taskId),
7438
+ proof,
7439
+ nextTask,
7440
+ publicSync,
7441
+ }));
7442
+ if (publicSync && !publicSync.ok) process.exitCode = 1;
6517
7443
  }
6518
7444
 
6519
7445
  function stampAutoAcceptMetadata(taskDb, db, taskId, actor, policy) {
@@ -6693,6 +7619,7 @@ function cmdRevise(args) {
6693
7619
  version: result.event.version,
6694
7620
  approval_status: 'revise',
6695
7621
  revision_count: result.event.payload.revision_count,
7622
+ episode: result.episode || null,
6696
7623
  projection_path: outPath,
6697
7624
  task: compactTaskFromProjection(projection, taskId),
6698
7625
  });
@@ -7620,7 +8547,10 @@ function taskBoardHtml() {
7620
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>',
7621
8548
  '<div class="fact"><b>Goal</b><div id="taskGoal"></div></div>',
7622
8549
  '<div class="fact"><b>Lineage</b><div id="taskLineage"></div></div>',
7623
- '<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>',
7624
8554
  '<div class="fact"><b>Proof / lesson</b><div id="taskProof"></div></div>',
7625
8555
  '<div class="thread">' + (messages || '<div class="empty">No thread yet.</div>') + '</div>',
7626
8556
  '<label>Add context</label><textarea id="note" placeholder="Decision, blocker, context, update..."></textarea>',
@@ -7632,9 +8562,17 @@ function taskBoardHtml() {
7632
8562
  room.querySelector('h3').textContent = task.title;
7633
8563
  $('taskGoal').textContent = task.objective || 'No matching goal yet.';
7634
8564
  $('taskLineage').textContent = 'parent: ' + parent + ' / next: ' + children;
7635
- $('taskSummary').textContent = task.review && task.review.summary
7636
- ? task.review.summary
7637
- : '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.';
7638
8576
  $('taskProof').textContent = task.review && (task.review.proof || task.review.lesson)
7639
8577
  ? ((task.review.proof || 'no proof') + ' / ' + (task.review.lesson || 'no lesson'))
7640
8578
  : 'No proof yet.';
@@ -7975,22 +8913,39 @@ async function handleTaskApi(req, res, taskDb, db) {
7975
8913
  });
7976
8914
  }
7977
8915
  if (op === 'plan') {
8916
+ const actor = String(body.actor || DEFAULT_OWNER);
8917
+ const goal = String(body.goal || body.objective || '');
8918
+ const summary = String(body.summary || body.plan || '');
8919
+ const owner = String(body.owner || body.assignee || '');
8920
+ const exit = String(body.exit || body.exit_condition || '');
8921
+ const firstMove = String(body.first_move || body.firstMove || body.first || '');
8922
+ const task = taskDetail(taskDb, db, taskId);
8923
+ const automaticPlan = buildAutomaticPlanTrace(taskDb, task, {
8924
+ actor,
8925
+ actorExplicit: Boolean(body.actor),
8926
+ owner,
8927
+ goal,
8928
+ summary,
8929
+ firstMove,
8930
+ exit,
8931
+ });
7978
8932
  const result = taskDb.stageTask(db, {
7979
8933
  id: taskId,
7980
- actor: String(body.actor || DEFAULT_OWNER),
8934
+ actor,
7981
8935
  stage: 'plan',
7982
- goal: String(body.goal || body.objective || ''),
7983
- summary: String(body.summary || body.plan || ''),
7984
- owner: String(body.owner || body.assignee || ''),
7985
- exit: String(body.exit || body.exit_condition || ''),
8936
+ goal,
8937
+ summary,
8938
+ owner: automaticPlan.ownerForStage || owner,
8939
+ exit,
7986
8940
  proofNeeded: String(body.proof_needed || body.proofNeeded || body.proof || body.verify || ''),
7987
- firstMove: String(body.first_move || body.firstMove || body.first || ''),
8941
+ firstMove,
7988
8942
  nextButton: String(body.next_button || body.nextButton || ''),
7989
8943
  confidence: body.confidence,
8944
+ planTrace: automaticPlan.trace,
7990
8945
  });
7991
8946
  if (!result.staged) return sendJson(res, 409, { ok: false, reason: result.reason, detail: stageErrorDetail('task plan', result.reason, result) });
7992
8947
  const { projection, outPath } = writeDefaultProjection(taskDb, db);
7993
- return sendJson(res, 200, { ok: true, action: 'planned', task_id: taskId, version: result.event.version, stage_packet: result.stage_packet, projection_path: outPath, task: taskFromProjection(projection, taskId) });
8948
+ return sendJson(res, 200, { ok: true, action: 'planned', task_id: taskId, version: result.event.version, plan_trace: automaticPlan.trace, stage_packet: result.stage_packet, projection_path: outPath, task: taskFromProjection(projection, taskId) });
7994
8949
  }
7995
8950
  if (op === 'do') {
7996
8951
  const firstMove = String(body.first_move || body.firstMove || body.first || '').trim();
@@ -8097,12 +9052,34 @@ async function handleTaskApi(req, res, taskDb, db) {
8097
9052
  const proofIssue = meaningfulTaskProofIssue(proof);
8098
9053
  if (proofIssue) return sendProofIssue(res, proof, proofIssue);
8099
9054
  const nextTaskInput = normalizeReviewNextTaskInput(body.next);
9055
+ const actor = String(body.actor || DEFAULT_OWNER);
9056
+ const resultTrace = buildAutomaticResultTrace(taskDb, db, taskId, {
9057
+ actor,
9058
+ proof,
9059
+ changed: body.changed || body.result || body.done,
9060
+ checked: body.checked || body.check || body.verified,
9061
+ passed: body.passed || body.pass,
9062
+ failed: body.failed || body.fail,
9063
+ cost: body.cost,
9064
+ saved: body.saved || body.savings,
9065
+ tryNext: body.try_next || body.tryNext || body.try || body.handoff,
9066
+ status: body.status,
9067
+ files: body.files,
9068
+ commands: body.commands || body.command,
9069
+ });
8100
9070
  const result = taskDb.readyTask(db, {
8101
9071
  id: taskId,
8102
- actor: String(body.actor || DEFAULT_OWNER),
9072
+ actor,
8103
9073
  proof,
8104
9074
  lesson: String(body.lesson || ''),
8105
9075
  nextTask: nextTaskInput.nextTask,
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
+ },
8106
9083
  });
8107
9084
  if (!result.ready) return sendJson(res, 409, { ok: false, reason: result.reason });
8108
9085
  const { projection, outPath } = writeDefaultProjection(taskDb, db);
@@ -8110,6 +9087,7 @@ async function handleTaskApi(req, res, taskDb, db) {
8110
9087
  ok: true,
8111
9088
  action: 'ready',
8112
9089
  task_id: taskId,
9090
+ result_trace: resultTrace,
8113
9091
  ...(nextTaskInput.ignored ? { review_next_task_ignored: nextTaskInput.ignored } : {}),
8114
9092
  projection_path: outPath,
8115
9093
  task: taskFromProjection(projection, taskId),
@@ -8300,6 +9278,10 @@ async function run(args) {
8300
9278
  case 'continue':
8301
9279
  return cmdContinueWork(rest);
8302
9280
  case 'chat': return cmdChat(rest);
9281
+ case 'plan-preview':
9282
+ case 'preview-plan':
9283
+ case 'plan-card':
9284
+ return cmdPlanPreview(rest);
8303
9285
  case 'note': return cmdNote(rest);
8304
9286
  case 'say': return cmdNote(rest);
8305
9287
  case 'show': return cmdShow(rest);
@@ -8309,6 +9291,7 @@ async function run(args) {
8309
9291
  case 'chat-review':
8310
9292
  return cmdReviewChat(rest);
8311
9293
  case 'ready': return cmdReady(rest);
9294
+ case 'result': return cmdResult(rest);
8312
9295
  case 'accept': return cmdAccept(rest);
8313
9296
  case 'auto-accept-certified':
8314
9297
  case 'auto-accept':