atris 3.30.3 → 3.30.5

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 (2) hide show
  1. package/commands/mission.js +170 -9
  2. package/package.json +1 -1
@@ -851,6 +851,149 @@ function inferRunObjectiveVerifier(objective, root = process.cwd()) {
851
851
  return missionRunSmokeVerifier();
852
852
  }
853
853
 
854
+ function markMissionRunContinuation(mission) {
855
+ return {
856
+ ...mission,
857
+ started_from: 'mission_run_objective',
858
+ continue_on_complete: true,
859
+ continuation_policy: 'decide_and_start_next_useful_mission',
860
+ };
861
+ }
862
+
863
+ function continuationObjective(parent) {
864
+ return `Decide and start the next useful mission after: ${parent.objective}`;
865
+ }
866
+
867
+ function findActiveContinuationMission(parent, root = process.cwd()) {
868
+ return listMissions(root).find((mission) => (
869
+ mission.parent_mission_id === parent.id
870
+ && mission.started_from === 'mission_run_continuation'
871
+ && !TERMINAL_STATUSES.has(mission.status)
872
+ )) || null;
873
+ }
874
+
875
+ function findActiveMissionRunContinuation(root = process.cwd(), excludeId = '') {
876
+ const excluded = String(excludeId || '');
877
+ const candidates = listMissions(root)
878
+ .filter((mission) => (
879
+ mission.id !== excluded
880
+ && mission.started_from === 'mission_run_continuation'
881
+ && mission.continuation_policy === 'choose_next_mission'
882
+ && !TERMINAL_STATUSES.has(mission.status)
883
+ ));
884
+ candidates.sort((a, b) => missionSortTime(b) - missionSortTime(a));
885
+ return candidates[0] || null;
886
+ }
887
+
888
+ function completeActiveContinuationForStartedMission(nextMission, root = process.cwd()) {
889
+ const continuation = findActiveMissionRunContinuation(root, nextMission?.id);
890
+ if (!continuation || !nextMission) return null;
891
+ if (continuation.objective === nextMission.objective) return null;
892
+
893
+ const proof = `Started next mission ${nextMission.id}: ${nextMission.objective}`;
894
+ const completionGate = { ok: true, source: 'mission_run_continuation', forced: false };
895
+ const baseNext = {
896
+ ...continuation,
897
+ status: 'complete',
898
+ completed_at: stampIso(),
899
+ proof,
900
+ completion_gate: completionGate,
901
+ continued_by_mission_id: nextMission.id,
902
+ continued_by_objective: nextMission.objective,
903
+ next_action: 'mission complete',
904
+ };
905
+ const completion = missionCompletionReceipt(baseNext, proof);
906
+ const { mission: saved } = saveMission({
907
+ ...baseNext,
908
+ landing: completion.landing,
909
+ result: completion.result,
910
+ }, root, 'mission_continuation_completed', {
911
+ proof,
912
+ continued_by_mission_id: nextMission.id,
913
+ continued_by_objective: nextMission.objective,
914
+ });
915
+ appendMemberLog(saved.owner, 'Mission continuation completed', {
916
+ mission: saved.objective,
917
+ continued_by: nextMission.id,
918
+ proof,
919
+ }, root);
920
+ return {
921
+ completed: true,
922
+ mission: saved,
923
+ continued_by: {
924
+ mission_id: nextMission.id,
925
+ objective: nextMission.objective,
926
+ },
927
+ };
928
+ }
929
+
930
+ function seedMissionRunContinuation(parent, root = process.cwd(), proof = '') {
931
+ if (!parent || parent.status !== 'complete') return null;
932
+ if (parent.continue_on_complete !== true) return null;
933
+ if (parent.continuation_seeded_mission_id) return {
934
+ inserted: false,
935
+ reason: 'already_seeded',
936
+ mission_id: parent.continuation_seeded_mission_id,
937
+ };
938
+
939
+ const existing = findActiveContinuationMission(parent, root);
940
+ if (existing) return { inserted: false, reason: 'active_continuation_exists', mission: existing };
941
+
942
+ const owner = parent.owner || process.env.ATRIS_AGENT_ID || 'mission-lead';
943
+ const objective = continuationObjective(parent);
944
+ const mission = missionFromArgs([
945
+ objective,
946
+ '--owner',
947
+ owner,
948
+ '--runner',
949
+ 'codex_goal',
950
+ '--lane',
951
+ parent.lane || 'workspace',
952
+ '--cadence',
953
+ 'manual',
954
+ '--stop',
955
+ 'next useful mission is started, or no useful next mission remains',
956
+ ]);
957
+ const nextMission = {
958
+ ...mission,
959
+ started_from: 'mission_run_continuation',
960
+ parent_mission_id: parent.id,
961
+ parent_objective: parent.objective,
962
+ continue_on_complete: false,
963
+ continuation_policy: 'choose_next_mission',
964
+ parent_proof: proof || parent.receipt_path || null,
965
+ next_action: `decide next mission, then run: atris mission run "<next useful mission>" --owner ${owner}`,
966
+ };
967
+
968
+ ensureMemberMissionFile(nextMission.owner, root, nextMission.objective);
969
+ const { mission: saved } = saveMission(nextMission, root, 'mission_continuation_started', {
970
+ parent_mission_id: parent.id,
971
+ parent_objective: parent.objective,
972
+ proof: proof || null,
973
+ });
974
+ const worktreeBaseline = captureMissionWorktreeBaseline(saved, root);
975
+ const seededAt = stampIso();
976
+ const { mission: updatedParent } = saveMission({
977
+ ...parent,
978
+ continuation_seeded_mission_id: saved.id,
979
+ continuation_seeded_at: seededAt,
980
+ continuation_seeded_objective: saved.objective,
981
+ }, root, 'mission_continuation_seeded', {
982
+ continuation_mission_id: saved.id,
983
+ continuation_objective: saved.objective,
984
+ });
985
+ return {
986
+ inserted: true,
987
+ mission: saved,
988
+ parent: updatedParent,
989
+ worktree_baseline: worktreeBaseline ? {
990
+ path: path.relative(root, missionBaselinePath(saved.id, root)),
991
+ dirty_count: worktreeBaseline.dirty_count,
992
+ dirty_hash: worktreeBaseline.dirty_hash,
993
+ } : null,
994
+ };
995
+ }
996
+
854
997
  function startMission(args) {
855
998
  const asJson = wantsJson(args);
856
999
  const mission = missionFromArgs(args);
@@ -931,7 +1074,7 @@ function startMissionFromRunObjective(objective, args) {
931
1074
  if (hasFlag(args, '--always-on')) startArgs.push('--always-on');
932
1075
  if (hasFlag(args, '--xp-task') || hasFlag(args, '--agent-xp')) startArgs.push('--xp-task');
933
1076
 
934
- const mission = missionFromArgs(startArgs);
1077
+ const mission = markMissionRunContinuation(missionFromArgs(startArgs));
935
1078
  const warnings = [missingVerifierWarning(mission)].filter(Boolean);
936
1079
  ensureMemberMissionFile(mission.owner, process.cwd(), mission.objective);
937
1080
  const { mission: saved } = saveMission(mission, process.cwd(), 'mission_started', { objective: mission.objective, source: 'mission_run_objective' });
@@ -944,6 +1087,7 @@ function startMissionFromRunObjective(objective, args) {
944
1087
  verifier: saved.verifier,
945
1088
  });
946
1089
  const worktreeBaseline = captureMissionWorktreeBaseline(saved, process.cwd());
1090
+ const completedContinuationGoal = completeActiveContinuationForStartedMission(saved, process.cwd());
947
1091
  const codexGoalState = refreshCodexGoalController(process.cwd());
948
1092
  printJsonOrText(
949
1093
  {
@@ -959,6 +1103,7 @@ function startMissionFromRunObjective(objective, args) {
959
1103
  dirty_count: worktreeBaseline.dirty_count,
960
1104
  dirty_hash: worktreeBaseline.dirty_hash,
961
1105
  } : null,
1106
+ completed_continuation_goal: completedContinuationGoal,
962
1107
  codex_goal_state: codexGoalState,
963
1108
  next_command: codexGoalState.goal?.next_command || `atris mission tick ${saved.id} --verify`,
964
1109
  },
@@ -2345,6 +2490,7 @@ async function runMission(args) {
2345
2490
  const startedAt = Date.now();
2346
2491
  let backoffAttempt = 0;
2347
2492
  let lastRateLimit = null;
2493
+ let continuationGoal = null;
2348
2494
 
2349
2495
  const sessionLabel = skipWorker
2350
2496
  ? 'caller-session'
@@ -2557,6 +2703,10 @@ async function runMission(args) {
2557
2703
  verifier: verifierResult ? (verifierResult.passed ? 'passed' : 'failed') : 'not_run',
2558
2704
  receipt: receiptPath,
2559
2705
  });
2706
+ if (newStatus === 'complete') {
2707
+ continuationGoal = seedMissionRunContinuation(mission, cwd, receiptPath);
2708
+ if (continuationGoal?.parent) mission = continuationGoal.parent;
2709
+ }
2560
2710
  refreshCodexGoalController(cwd);
2561
2711
 
2562
2712
  console.error(`[tick ${tickIdx}] status=${result.status} reason=${result.reason} verifier=${verifierResult ? (verifierResult.passed ? 'pass' : 'fail') : 'skip'} -> ${receiptPath || '-'}`);
@@ -2634,7 +2784,7 @@ async function runMission(args) {
2634
2784
  const codexGoalState = refreshCodexGoalController(cwd);
2635
2785
 
2636
2786
  printJsonOrText(
2637
- { ok: true, action: 'mission_run', mission, ran_ticks: ranTicks, tick_count: ticks.length, ticks, pause_reason: pauseReason, session_id: sessionId, summary_receipt: finalReceipt, worktree: summaryWorktree, codex_goal_state: codexGoalState },
2787
+ { ok: true, action: 'mission_run', mission, ran_ticks: ranTicks, tick_count: ticks.length, ticks, pause_reason: pauseReason, session_id: sessionId, summary_receipt: finalReceipt, worktree: summaryWorktree, codex_goal_state: codexGoalState, continuation_goal: continuationGoal },
2638
2788
  [
2639
2789
  `Ran mission ${mission.id}`,
2640
2790
  ` objective: ${mission.objective}`,
@@ -2643,6 +2793,7 @@ async function runMission(args) {
2643
2793
  pauseReason ? ` pause: ${pauseReason}` : null,
2644
2794
  ` session: ${sessionId || '(none)'}`,
2645
2795
  ` summary receipt: ${finalReceipt}`,
2796
+ continuationGoal?.mission ? ` next goal: ${continuationGoal.mission.objective}` : null,
2646
2797
  ].filter(Boolean),
2647
2798
  asJson,
2648
2799
  );
@@ -2771,15 +2922,20 @@ function tickMission(args) {
2771
2922
  receipt: receiptPath,
2772
2923
  summary: summary || undefined,
2773
2924
  });
2925
+ const continuationGoal = saved.status === 'complete'
2926
+ ? seedMissionRunContinuation(saved, cwd, receiptPath)
2927
+ : null;
2928
+ const outputMission = continuationGoal?.parent || saved;
2774
2929
  const codexGoalState = refreshCodexGoalController(process.cwd());
2775
2930
  printJsonOrText(
2776
- { ok: true, action: 'mission_tick', mission: saved, tick: tickRecord, verifier_result: verifierResult, receipt_path: receiptPath, log_path: logPath, codex_goal_state: codexGoalState },
2931
+ { ok: true, action: 'mission_tick', mission: outputMission, tick: tickRecord, verifier_result: verifierResult, receipt_path: receiptPath, log_path: logPath, codex_goal_state: codexGoalState, continuation_goal: continuationGoal },
2777
2932
  [
2778
- `Ticked mission: ${saved.objective}`,
2779
- `State: ${saved.status}`,
2933
+ `Ticked mission: ${outputMission.objective}`,
2934
+ `State: ${outputMission.status}`,
2780
2935
  `Tick: ${tickIdx}`,
2781
- `Next: ${saved.next_action}`,
2936
+ `Next: ${outputMission.next_action}`,
2782
2937
  ...(receiptPath ? [`Receipt: ${receiptPath}`] : []),
2938
+ ...(continuationGoal?.mission ? [`Next goal: ${continuationGoal.mission.objective}`] : []),
2783
2939
  ],
2784
2940
  asJson,
2785
2941
  );
@@ -2861,24 +3017,28 @@ function completeMission(args) {
2861
3017
  result: completion.result,
2862
3018
  };
2863
3019
  const { mission: saved } = saveMission(next, process.cwd(), 'mission_completed', { proof, completion_gate: next.completion_gate });
2864
- const logPath = appendMemberLog(saved.owner, 'Mission completed', { mission: saved.objective, proof });
3020
+ const continuationGoal = seedMissionRunContinuation(saved, process.cwd(), proof);
3021
+ const outputMission = continuationGoal?.parent || saved;
3022
+ const logPath = appendMemberLog(outputMission.owner, 'Mission completed', { mission: outputMission.objective, proof });
2865
3023
  const codexGoalState = refreshCodexGoalController(process.cwd());
2866
3024
  printJsonOrText(
2867
3025
  {
2868
3026
  ok: true,
2869
3027
  action: 'mission_completed',
2870
- mission: saved,
3028
+ mission: outputMission,
2871
3029
  landing: completion.landing,
2872
3030
  result: completion.result,
2873
3031
  log_path: logPath,
2874
3032
  codex_goal_state: codexGoalState,
2875
3033
  xp_next_command: xpNextCommand,
3034
+ continuation_goal: continuationGoal,
2876
3035
  },
2877
3036
  [
2878
- `Completed mission: ${saved.objective}`,
3037
+ `Completed mission: ${outputMission.objective}`,
2879
3038
  ...missionResultLines(completion),
2880
3039
  `Proof: ${proof}`,
2881
3040
  ...(xpNextCommand ? [`AgentXP: ${xpNextCommand}`] : []),
3041
+ ...(continuationGoal?.mission ? [`Next goal: ${continuationGoal.mission.objective}`] : []),
2882
3042
  ],
2883
3043
  asJson,
2884
3044
  );
@@ -3059,6 +3219,7 @@ atris mission - durable goal + loop + owner + proof state
3059
3219
  atris mission run ["objective"|id|--due] [--owner <member>] [--max-ticks 4] [--max-wall 3600] [--cadence "15m"]
3060
3220
  [--no-claude] [--no-verify] [--complete-on-pass] [--no-drain] [--json]
3061
3221
  (bare run prompts for mission + owner; --due runs the saved queue)
3222
+ (mission-run completions seed the next visible goal: decide and start the next useful mission)
3062
3223
  atris mission complete <id> --proof "..."
3063
3224
  atris mission stop <id> [--pause] [--reason "..."]
3064
3225
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "atris",
3
- "version": "3.30.3",
3
+ "version": "3.30.5",
4
4
  "main": "bin/atris.js",
5
5
  "bin": {
6
6
  "atris": "bin/atris.js",