atris 3.58.7 → 3.59.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (95) hide show
  1. package/AGENTS.md +3 -2
  2. package/GETTING_STARTED.md +2 -2
  3. package/PERSONA.md +11 -11
  4. package/README.md +1 -1
  5. package/atris/GEMINI.md +7 -6
  6. package/atris/PERSONA.md +4 -4
  7. package/atris/features/README.md +77 -6
  8. package/atris/skills/README.md +44 -15
  9. package/atris/skills/aeo/SKILL.md +10 -10
  10. package/atris/skills/atris/SKILL.md +1 -1
  11. package/atris/skills/engines/SKILL.md +69 -50
  12. package/atris/skills/meta/SKILL.md +4 -4
  13. package/bin/atris.js +8 -6
  14. package/commands/aeo.js +9 -12
  15. package/commands/analytics.js +8 -11
  16. package/commands/autopilot.js +79 -8
  17. package/commands/avail.js +1 -1
  18. package/commands/brain.js +3 -1
  19. package/commands/business.js +13 -8
  20. package/commands/clean.js +17 -0
  21. package/commands/close.js +2 -1
  22. package/commands/computer.js +15 -10
  23. package/commands/context-sync.js +3 -3
  24. package/commands/doc-health.js +55 -2
  25. package/commands/engine.js +384 -4
  26. package/commands/experiments.js +13 -4
  27. package/commands/host.js +53 -0
  28. package/commands/improve.js +1 -1
  29. package/commands/integrations.js +2 -5
  30. package/commands/learn.js +1 -1
  31. package/commands/member.js +231 -53
  32. package/commands/mission.js +129 -19
  33. package/commands/one-lap.js +58 -3
  34. package/commands/pack.js +2 -2
  35. package/commands/probe.js +11 -5
  36. package/commands/pull.js +1 -1
  37. package/commands/push.js +1 -1
  38. package/commands/rainmaker.js +3 -16
  39. package/commands/run.js +4 -1
  40. package/commands/scout.js +1 -1
  41. package/commands/search.js +5 -3
  42. package/commands/spaceship.js +1 -1
  43. package/commands/stream.js +27 -2
  44. package/commands/study.js +15 -9
  45. package/commands/sync.js +1 -1
  46. package/commands/task.js +13 -2
  47. package/commands/unknowns.js +5 -0
  48. package/commands/verify.js +12 -4
  49. package/commands/visualize.js +20 -3
  50. package/commands/workflow.js +23 -7
  51. package/lib/codex-flight.js +28 -5
  52. package/lib/dispatch-scout.js +31 -22
  53. package/lib/engine-registry.js +1482 -13
  54. package/lib/feedback-triage.js +2 -0
  55. package/lib/fleet.js +111 -12
  56. package/lib/host.js +336 -0
  57. package/lib/improve-vitals-html.js +3 -2
  58. package/lib/known-commands.js +1 -1
  59. package/lib/lesson-contradiction.js +2 -2
  60. package/lib/map-refs.js +226 -0
  61. package/lib/member-engine.js +253 -0
  62. package/lib/mission-room.js +4 -4
  63. package/lib/noninteractive.js +15 -4
  64. package/lib/policy-lessons.js +1 -1
  65. package/lib/receipt-block.js +137 -28
  66. package/lib/roster-markdown.js +206 -0
  67. package/lib/roster-models.js +179 -0
  68. package/lib/runner-command.js +95 -15
  69. package/lib/scorecard.js +4 -4
  70. package/lib/self-drive.js +9 -1
  71. package/lib/sleep-sync.js +11 -0
  72. package/lib/state-detection.js +28 -9
  73. package/lib/task-db.js +3 -2
  74. package/lib/todo-fallback.js +1 -1
  75. package/lib/wish-audit.js +10 -6
  76. package/lib/wish-delegate.js +29 -5
  77. package/mcp/atris-mcp/index.mjs +77 -24
  78. package/package.json +2 -5
  79. package/scripts/det/det.js +2 -2
  80. package/scripts/det/pr-description.js +2 -2
  81. package/scripts/det/test.js +2 -2
  82. package/templates/members/host/MEMBER.md +23 -0
  83. package/templates/members/host/MISSION.md +11 -0
  84. package/templates/members/host/SOUL.md +7 -0
  85. package/templates/members/host/context/why.md +3 -0
  86. package/templates/members/host/goals.json +5 -0
  87. package/templates/members/host/goals.md +3 -0
  88. package/templates/members/host/skills/host/SKILL.md +13 -0
  89. package/utils/api.js +1 -1
  90. package/utils/auth.js +17 -7
  91. package/utils/backend-root.js +31 -0
  92. package/utils/config.js +5 -4
  93. package/utils/owner-identity.js +61 -0
  94. package/atris/policies/LESSONS.md +0 -13
  95. package/atris/skills/autopilot/atris-autopilot.md +0 -71
@@ -4,10 +4,12 @@ const crypto = require('crypto');
4
4
  const os = require('os');
5
5
  const { execFileSync, spawnSync } = require('child_process');
6
6
  const { loadCredentials } = require('../utils/auth');
7
+ const { resolveBackendRoot } = require('../utils/backend-root');
7
8
  const { apiRequestJson } = require('../utils/api');
8
9
  const { runAliveTick } = require('../lib/member-alive');
9
10
  const { defaultObjectiveRunner } = require('../lib/default-runner');
10
11
  const { readJson, writeJson } = require('../lib/json-file');
12
+ const { sleepSync } = require('../lib/sleep-sync');
11
13
  const { hasFlag, readFlag, readNumberFlag } = require('../lib/arg-parser');
12
14
  const { ensureMemberBundle, memberBundlePresent } = require('../lib/member-scaffold');
13
15
  const { memberProcessPrompt, MEMBER_PROCESS_PATH } = require('../lib/member-context');
@@ -686,6 +688,15 @@ const MEMBER_RUN_START_VALUE_FLAGS = [
686
688
  '--mission-id',
687
689
  '--owner',
688
690
  '--runner',
691
+ '--engine',
692
+ '--budget',
693
+ '--repo',
694
+ '--native-goal-status',
695
+ '--native-goal-objective',
696
+ '--visible-goal-status',
697
+ '--visible-goal-objective',
698
+ '--max-idle-ticks',
699
+ '--slots',
689
700
  '--lane',
690
701
  '--cadence',
691
702
  '--verify',
@@ -717,10 +728,49 @@ const MEMBER_RUN_START_BOOLEAN_FLAGS = [
717
728
  '--stop-when-done',
718
729
  '--choose-work',
719
730
  '--auto',
731
+ '--headless',
732
+ '--self-drive',
733
+ '--no-claude',
734
+ '--complete-on-pass',
735
+ '--no-drain',
736
+ '--create-next',
737
+ '--preflight',
738
+ '--no-preflight',
739
+ '--room-preflight',
740
+ '--no-room-preflight',
741
+ '--room-auto-run',
742
+ '--no-room-auto-run',
743
+ '--manual-ack',
744
+ '--allow-native-goal-supersede',
745
+ '--supersede-paused-native-goal',
746
+ '--take-goal-slot',
747
+ '--due',
748
+ '--detach',
749
+ '--fleet',
750
+ '--dry-run',
751
+ '--land',
752
+ '--cloud',
720
753
  ];
721
754
 
755
+ function memberRunMissionWordIndices(args = []) {
756
+ const values = new Set(MEMBER_RUN_START_VALUE_FLAGS);
757
+ const booleans = new Set(['--full', '--verbose', ...MEMBER_RUN_START_BOOLEAN_FLAGS]);
758
+ const indices = [];
759
+ for (let i = 0; i < args.length; i += 1) {
760
+ const arg = String(args[i]);
761
+ const key = arg.includes('=') ? arg.slice(0, arg.indexOf('=')) : arg;
762
+ if (booleans.has(key)) continue;
763
+ if (values.has(key)) {
764
+ if (!arg.includes('=') && args[i + 1] && !String(args[i + 1]).startsWith('--')) i += 1;
765
+ continue;
766
+ }
767
+ indices.push(i);
768
+ }
769
+ return indices;
770
+ }
771
+
722
772
  function memberRunMissionText(args = []) {
723
- return stripKnownFlags(args, MEMBER_RUN_START_VALUE_FLAGS, MEMBER_RUN_START_BOOLEAN_FLAGS).join(' ').trim();
773
+ return memberRunMissionWordIndices(args).map((index) => args[index]).join(' ').trim();
724
774
  }
725
775
 
726
776
  function cleanMemberRunPhrase(value) {
@@ -811,6 +861,22 @@ function pushFlagWhenPresent(out, args, name) {
811
861
  if (hasFlag(args, name)) out.push(name);
812
862
  }
813
863
 
864
+ // The runner a new member mission starts with. An explicit --runner or
865
+ // --engine wins. With neither, a member the roster names runs on "auto", so
866
+ // every tick picks the member's engine from ROSTER.md (edits apply on the next
867
+ // tick). Otherwise the old default holds.
868
+ function memberRunRunner(owner, args = [], cwd = process.cwd()) {
869
+ const explicit = readFlag(args, '--runner', '');
870
+ if (explicit) return explicit;
871
+ // codex_goal stalls unattended (no live codex session drives its native goal
872
+ // slot). Default to claude unless a live codex session is signalled; explicit
873
+ // --runner still wins. Proven footgun 2026-07-16. See lib/default-runner.js.
874
+ const fallback = defaultObjectiveRunner(args);
875
+ if (fallback !== 'claude' || readFlag(args, '--engine', '')) return fallback;
876
+ const { memberRosterEngine } = require('../lib/member-engine');
877
+ return memberRosterEngine(owner, cwd) ? 'auto' : fallback;
878
+ }
879
+
814
880
  function buildMemberRunStartArgs(owner, missionText, args = [], cwd = process.cwd()) {
815
881
  const startArgs = [
816
882
  'mission',
@@ -818,11 +884,8 @@ function buildMemberRunStartArgs(owner, missionText, args = [], cwd = process.cw
818
884
  missionText,
819
885
  '--owner',
820
886
  owner,
821
- // codex_goal stalls unattended (no live codex session drives its native goal
822
- // slot). Default to claude unless a live codex session is signalled; explicit
823
- // --runner still wins. Proven footgun 2026-07-16. See lib/default-runner.js.
824
887
  '--runner',
825
- readFlag(args, '--runner', defaultObjectiveRunner(args)),
888
+ memberRunRunner(owner, args, cwd),
826
889
  '--lane',
827
890
  readFlag(args, '--lane', 'workspace'),
828
891
  ];
@@ -834,10 +897,8 @@ function buildMemberRunStartArgs(owner, missionText, args = [], cwd = process.cw
834
897
  pushFlagValue(startArgs, args, '--minutes');
835
898
  pushFlagValue(startArgs, args, '--hours');
836
899
  pushFlagValue(startArgs, args, '--base');
837
- // A member-run mission is driven immediately by its runner, not parked on
838
- // the queue, so the mission-start verifier gate (built for parked planning
839
- // wishes) would only kill autonomous legs like autopilot's "member chooses
840
- // useful work". When the caller named no verifier, opt out explicitly.
900
+ // Member runs choose and drive work immediately, so an absent check must not
901
+ // leave autonomous callers with a mission that never ran.
841
902
  if (hasFlag(args, '--no-verify') || !readFlag(args, '--verify', '')) {
842
903
  startArgs.push('--no-verify');
843
904
  }
@@ -853,7 +914,7 @@ function buildMemberRunStartArgs(owner, missionText, args = [], cwd = process.cw
853
914
  if (!hasFlag(args, '--shared-checkout') && !hasFlag(args, '--no-worktree') && insideGitRepo(cwd)) {
854
915
  startArgs.push('--worktree');
855
916
  }
856
- if (hasFlag(args, '--json')) startArgs.push('--json');
917
+ startArgs.push('--json');
857
918
  return startArgs;
858
919
  }
859
920
 
@@ -871,15 +932,52 @@ function startMemberRunMission(name, missionText, args = []) {
871
932
  const paths = requireMemberDir(name);
872
933
  const owner = paths.storageName || name;
873
934
  const startArgs = buildMemberRunStartArgs(owner, missionText, args);
935
+ return memberRunCli(startArgs, process.cwd());
936
+ }
874
937
 
938
+ function memberRunCli(args, cwd, { live = false, missionId = '' } = {}) {
875
939
  const cliPath = path.join(__dirname, '..', 'bin', 'atris.js');
940
+ const runsDir = path.join(cwd, 'atris', 'runs');
941
+ const previousReceipts = live && fs.existsSync(runsDir) ? new Set(fs.readdirSync(runsDir)) : new Set();
942
+ const result = spawnSync(process.execPath, [cliPath, ...args], {
943
+ cwd,
944
+ encoding: 'utf8',
945
+ maxBuffer: 64 * 1024 * 1024,
946
+ stdio: live ? 'inherit' : ['ignore', 'pipe', 'pipe'],
947
+ });
948
+ if (result.error) throw result.error;
949
+ if (live) {
950
+ const receiptName = fs.existsSync(runsDir)
951
+ ? fs.readdirSync(runsDir).filter((name) => !previousReceipts.has(name) && name.startsWith(`mission-${missionId}-`) && name.endsWith('.json')).sort().at(-1)
952
+ : null;
953
+ const receipt = receiptName ? readJson(path.join(runsDir, receiptName), null) : null;
954
+ return { status: result.status, payload: receipt?.result?.kind === 'mission_run_summary'
955
+ ? { ran_ticks: receipt.result.ran_ticks } : null, stderr: '', errorReported: result.status !== 0 };
956
+ }
957
+ let payload = null;
876
958
  try {
877
- execFileSync(process.execPath, [cliPath, ...startArgs], {
878
- cwd: process.cwd(),
879
- stdio: 'inherit',
880
- });
959
+ payload = JSON.parse(result.stdout);
881
960
  } catch (error) {
882
- process.exitCode = Number(error?.status) || 1;
961
+ if (result.status === 0) throw new Error(`member run: mission command returned invalid json: ${error.message}`);
962
+ }
963
+ return { status: result.status, payload, stderr: String(result.stderr || '').trim() };
964
+ }
965
+
966
+ function memberRunConfiguredVerifier(paths) {
967
+ const frontmatter = parseFrontmatter(fs.readFileSync(paths.memberFile, 'utf8')) || {};
968
+ const check = frontmatter.verify || frontmatter.verifier;
969
+ return typeof check === 'string' ? check.trim() : '';
970
+ }
971
+
972
+ function printMemberRunResult(result, asJson) {
973
+ if (asJson) {
974
+ console.log(JSON.stringify(result));
975
+ } else if (result.started) {
976
+ console.log(`started ${result.mission_id} (${result.state})`);
977
+ }
978
+ if (result.ok === false || (!result.started && result.ok !== true)) {
979
+ if (!result.errorReported) console.error(result.error);
980
+ process.exitCode = 1;
883
981
  }
884
982
  }
885
983
 
@@ -967,48 +1065,96 @@ function memberPing(name, ...args) {
967
1065
 
968
1066
  function memberRun(name, ...args) {
969
1067
  if (!name || name === '--help' || name === '-h' || hasFlag(args, '--help') || hasFlag(args, '-h')) {
970
- console.log('Usage: atris member run <name> ["mission text"] [--minutes N|--hours N] [--json]');
971
- console.log('New mission: atris member run growth "Improve onboarding proof" --minutes 30 --json');
972
- console.log('Choose work: atris member run growth --industry logistics --value "reduce support time" --minutes 30 --json');
973
- console.log('Existing mission: atris member run growth --mission <mission-id> --max-ticks 1 --json');
974
- console.log('Meaning: if mission text is omitted, the member chooses one useful bounded task from Atris state.');
975
- console.log('Truth: useful work must improve revenue, reliability, speed, security, clarity, or trust, then show plain proof.');
976
- console.log('Isolation: new missions use --worktree by default; add --shared-checkout to stay here.');
1068
+ console.log('usage: atris member run <name> ["mission text"] [--verify "<cmd>"] [--minutes N|--hours N] [--json]');
1069
+ console.log('new mission: atris member run growth "improve onboarding proof" --verify "npm test" --minutes 30 --json');
1070
+ console.log('choose work: atris member run growth --verify "npm test" --industry logistics --minutes 30 --json');
1071
+ console.log('existing mission: atris member run growth --mission <mission-id> --max-ticks 1 --json');
1072
+ console.log('meaning: if mission text is omitted, the member chooses one useful bounded task from atris state.');
1073
+ console.log('truth: useful work must improve revenue, reliability, speed, security, clarity, or trust, then show plain proof.');
1074
+ console.log('isolation: new missions use --worktree by default; add --shared-checkout to stay here.');
1075
+ console.log('check: --verify or a verify field in the member file adds a mission check.');
977
1076
  return;
978
1077
  }
979
1078
 
1079
+ const asJson = hasFlag(args, '--json');
1080
+ const paths = requireMemberDir(name);
1081
+ const requestedVerifier = readFlag(args, '--verify', '') || memberRunConfiguredVerifier(paths);
980
1082
  const missionText = memberRunMissionText(args);
981
1083
  const hasMissionOverride = Boolean(readFlag(args, '--mission', '') || readFlag(args, '--mission-id', ''));
1084
+ let mission = null;
1085
+ let startedNewMission = false;
1086
+ let nodeModulesLinked = false;
1087
+ let nodeModulesSource = null;
982
1088
  if (missionText && !hasMissionOverride) {
983
- startMemberRunMission(name, missionText, args);
984
- return;
1089
+ const start = startMemberRunMission(name, missionText, requestedVerifier ? [...args, '--verify', requestedVerifier] : args);
1090
+ if (start.status !== 0 || !start.payload?.mission) {
1091
+ printMemberRunResult({ started: false, state: null, mission_id: start.payload?.mission?.id || null,
1092
+ error: start.payload?.detail || start.payload?.error || start.stderr || 'mission creation failed' }, asJson);
1093
+ return;
1094
+ }
1095
+ mission = start.payload.mission;
1096
+ startedNewMission = true;
1097
+ nodeModulesLinked = Boolean(start.payload.node_modules_linked);
1098
+ nodeModulesSource = start.payload.node_modules_source || null;
1099
+ } else {
1100
+ const missionId = resolveMemberRunMissionId(name, args);
1101
+ if (!missionId) {
1102
+ const start = startMemberRunMission(name, memberRunAutoMissionText(name, args), requestedVerifier ? [...args, '--verify', requestedVerifier] : args);
1103
+ if (start.status !== 0 || !start.payload?.mission) {
1104
+ printMemberRunResult({ started: false, state: null, mission_id: start.payload?.mission?.id || null,
1105
+ error: start.payload?.detail || start.payload?.error || start.stderr || 'mission creation failed' }, asJson);
1106
+ return;
1107
+ }
1108
+ mission = start.payload.mission;
1109
+ startedNewMission = true;
1110
+ nodeModulesLinked = Boolean(start.payload.node_modules_linked);
1111
+ nodeModulesSource = start.payload.node_modules_source || null;
1112
+ } else {
1113
+ mission = require('./mission').resolveMission(missionId);
1114
+ if (!mission) {
1115
+ printMemberRunResult({ started: false, state: null, mission_id: missionId, error: `mission ${missionId} was not found` }, asJson);
1116
+ return;
1117
+ }
1118
+ }
985
1119
  }
986
1120
 
987
- const missionId = resolveMemberRunMissionId(name, args);
988
- if (!missionId) {
989
- startMemberRunMission(name, memberRunAutoMissionText(name, args), args);
990
- return;
1121
+ if (startedNewMission && !asJson) {
1122
+ if (mission.worktree?.path) console.log(`worktree: ${mission.worktree.path}`);
1123
+ if (nodeModulesLinked) console.log(`linked node_modules from ${nodeModulesSource}`);
1124
+ }
1125
+
1126
+ const cwd = mission.worktree?.path || process.cwd();
1127
+ const verifier = readFlag(args, '--verify', '') || mission.verifier || requestedVerifier;
1128
+ if (verifier && verifier !== mission.verifier) {
1129
+ const changed = memberRunCli(['mission', 'set-verifier', mission.id, verifier, '--json'], cwd);
1130
+ if (changed.status !== 0) {
1131
+ printMemberRunResult({ started: false, state: mission.status, mission_id: mission.id, mission,
1132
+ error: changed.payload?.detail || changed.payload?.error || changed.stderr || 'could not set mission check' }, asJson);
1133
+ return;
1134
+ }
1135
+ mission.verifier = verifier;
991
1136
  }
992
1137
 
993
- const runArgs = stripKnownFlags(args, ['--mission', '--mission-id', '--minutes', '--hours'], ['--worktree', '--shared-checkout', '--no-worktree']);
1138
+ const missionWordIndices = missionText && !hasMissionOverride ? new Set(memberRunMissionWordIndices(args)) : new Set();
1139
+ const executionArgs = args.filter((_, index) => !missionWordIndices.has(index));
1140
+ const runArgs = stripKnownFlags(executionArgs,
1141
+ ['--mission', '--mission-id', '--minutes', '--hours', '--industry', '--domain', '--value', '--outcome', '--truth', '--proof', '--base'],
1142
+ ['--worktree', '--shared-checkout', '--no-worktree', '--choose-work', '--auto']);
994
1143
  const budgetSeconds = memberRunBudgetSeconds(args);
995
- // A timed run is a loop contract, not one tick: keep picking the next useful
996
- // move until the wall clock (--max-wall) spends the budget. Only untimed runs
997
- // default to a single tick.
998
1144
  if (!readFlag(runArgs, '--max-ticks', '')) {
999
1145
  runArgs.push('--max-ticks', budgetSeconds ? String(Math.max(4, Math.ceil(budgetSeconds / 300))) : '1');
1000
1146
  }
1001
1147
  if (!readFlag(runArgs, '--max-wall', '')) runArgs.push('--max-wall', String(budgetSeconds || 900));
1002
-
1003
- const cliPath = path.join(__dirname, '..', 'bin', 'atris.js');
1004
- try {
1005
- execFileSync(process.execPath, [cliPath, 'mission', 'run', missionId, ...runArgs], {
1006
- cwd: process.cwd(),
1007
- stdio: 'inherit',
1008
- });
1009
- } catch (error) {
1010
- process.exitCode = Number(error?.status) || 1;
1011
- }
1148
+ const run = memberRunCli(['mission', 'run', mission.id, ...runArgs], cwd, { live: !asJson, missionId: mission.id });
1149
+ const payload = run.payload || {};
1150
+ const finalMission = payload.mission || require('./mission').resolveMission(mission.id, cwd) || mission;
1151
+ const started = run.status === 0 && payload.ok !== false && Number(payload.ran_ticks) > 0;
1152
+ const noWorkStepExpected = hasFlag(runArgs, '--detach') || hasFlag(runArgs, '--dry-run');
1153
+ const ok = run.status === 0 && payload.ok !== false && (started || noWorkStepExpected);
1154
+ printMemberRunResult({ ...payload, ok, started, state: finalMission.status, mission_id: mission.id,
1155
+ mission: finalMission, node_modules_linked: nodeModulesLinked, node_modules_source: nodeModulesSource,
1156
+ errorReported: run.errorReported,
1157
+ ...(!ok ? { error: payload.detail || payload.error || payload.reason || run.stderr || 'mission did not run a work step' } : {}) }, asJson);
1012
1158
  }
1013
1159
 
1014
1160
  function loadTeamScoreEvidence(scoreJsonPath) {
@@ -1831,8 +1977,8 @@ function problemSignalSources(root = process.cwd()) {
1831
1977
  sources.push(source);
1832
1978
  }
1833
1979
  for (const source of problemSignalFilesForStateDir(path.join(root, '.atris', 'state'))) push(source);
1834
- const siblingBackend = path.resolve(root, '..', 'atrisos-backend');
1835
- if (fs.existsSync(siblingBackend)) {
1980
+ const siblingBackend = resolveBackendRoot(root);
1981
+ if (siblingBackend) {
1836
1982
  for (const source of problemSignalFilesForStateDir(path.join(siblingBackend, '.atris', 'state'), 'backend')) push(source);
1837
1983
  }
1838
1984
  for (const source of configuredProblemSignalSources(root)) push(source);
@@ -4170,7 +4316,25 @@ function memberInstall(slug) {
4170
4316
  const memberDir = path.join(process.cwd(), 'atris', 'team', slug);
4171
4317
  const already = memberBundlePresent(memberDir);
4172
4318
  fs.mkdirSync(path.join(memberDir, 'logs'), { recursive: true });
4173
- if (already) {
4319
+ const templateDir = path.join(__dirname, '..', 'templates', 'members', slug);
4320
+ const copied = [];
4321
+ if (fs.existsSync(templateDir)) {
4322
+ const copyMissing = (source, relative = '') => {
4323
+ for (const entry of fs.readdirSync(source, { withFileTypes: true })) {
4324
+ const rel = path.join(relative, entry.name);
4325
+ const target = path.join(memberDir, rel);
4326
+ if (entry.isDirectory()) {
4327
+ fs.mkdirSync(target, { recursive: true });
4328
+ copyMissing(path.join(source, entry.name), rel);
4329
+ } else if (entry.isFile() && !fs.existsSync(target)) {
4330
+ fs.writeFileSync(target, fs.readFileSync(path.join(source, entry.name)), { flag: 'wx' });
4331
+ copied.push(rel);
4332
+ }
4333
+ }
4334
+ };
4335
+ copyMissing(templateDir);
4336
+ }
4337
+ if (already && copied.length === 0) {
4174
4338
  console.log(`MEMBER already installed for ${slug}`);
4175
4339
  return;
4176
4340
  }
@@ -4179,7 +4343,7 @@ function memberInstall(slug) {
4179
4343
  name: slug,
4180
4344
  source: 'install',
4181
4345
  });
4182
- for (const rel of created) {
4346
+ for (const rel of [...copied, ...created]) {
4183
4347
  console.log(`atris/team/${slug}/${rel.split(path.sep).join('/')}`);
4184
4348
  }
4185
4349
  console.log(`MEMBER installed for ${slug}.`);
@@ -4594,12 +4758,6 @@ function printJsonOrText(payload, lines, asJson) {
4594
4758
  for (const line of lines) console.log(line);
4595
4759
  }
4596
4760
 
4597
- function sleepSync(ms) {
4598
- const duration = Math.max(0, Number(ms) || 0);
4599
- if (duration <= 0) return;
4600
- Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, duration);
4601
- }
4602
-
4603
4761
  function memberGoal(name, ...args) {
4604
4762
  const paths = requireMemberDir(name);
4605
4763
  const asJson = hasFlag(args, '--json');
@@ -7660,6 +7818,24 @@ function wakeDecision(name, paths, { force = false, runtimeKind = memberRuntimeK
7660
7818
  return null;
7661
7819
  }
7662
7820
 
7821
+ // The engine this member's work runs on, from the roster: its own team line,
7822
+ // else its job's pick, else the router. Never throws.
7823
+ function wakeMemberEngine(name, root = process.cwd()) {
7824
+ try {
7825
+ const { resolveEngineForMember } = require('../lib/member-engine');
7826
+ const picked = resolveEngineForMember(name, root);
7827
+ return {
7828
+ id: picked.engine ? picked.engine.id : null,
7829
+ model: picked.model || null,
7830
+ job: picked.job,
7831
+ source: picked.source,
7832
+ reason: picked.reason,
7833
+ };
7834
+ } catch {
7835
+ return null;
7836
+ }
7837
+ }
7838
+
7663
7839
  async function runMemberWake(name, { execute = false, confirmed = false, force = false, domainInput = {} } = {}) {
7664
7840
  const paths = requireMemberDir(name);
7665
7841
  const runtimeKind = paths.runtimeKind || memberRuntimeKind(name);
@@ -7824,6 +8000,7 @@ async function runMemberWake(name, { execute = false, confirmed = false, force =
7824
8000
  ok: true,
7825
8001
  action: 'wake',
7826
8002
  member: name,
8003
+ engine: wakeMemberEngine(name),
7827
8004
  mode,
7828
8005
  decision,
7829
8006
  reason,
@@ -7946,6 +8123,7 @@ function wakeBootLines(name, result) {
7946
8123
  ` goal ${goal ? clipText(goal, 70) : s.dim('none yet')}`,
7947
8124
  ...(experiment ? [` working ${clipText(experiment, 70)}`] : []),
7948
8125
  ...(laneCandidates !== null ? [` lane ${laneCandidates} task${laneCandidates === 1 ? '' : 's'} waiting in ${name}'s lane`] : []),
8126
+ ...(result.engine && result.engine.id ? [` engine ${clipText(String(result.engine.reason || '').replace(`${name} does `, ''), 70)}`] : []),
7949
8127
  '',
7950
8128
  ` ${s.bold(name)} looked around and ${s.bold(decisionText)}.`,
7951
8129
  ` ${s.dim(`Why: ${wakeReasonText(result.reason)}.`)}`,
@@ -9261,7 +9439,7 @@ async function memberCommand(subcommand, ...args) {
9261
9439
  console.log(' atris member goal-from-mission growth --json');
9262
9440
  console.log(' atris member goal-from-score growth --score-json team-score.json --json');
9263
9441
  console.log(' atris member wake growth --json');
9264
- console.log(' atris member run growth "Improve onboarding proof" --minutes 30 --json');
9442
+ console.log(' atris member run growth "improve onboarding proof" --verify "npm test" --minutes 30 --json');
9265
9443
  console.log(' atris member run growth --mission <mission-id> --max-ticks 1 --json');
9266
9444
  console.log(' atris member wake growth --execute --confirm-autonomy-policy');
9267
9445
  console.log(' atris member supervisor recommendations --json');