atris 3.57.4 → 3.58.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 (174) hide show
  1. package/README.md +12 -1
  2. package/atris/skills/clawhub/member-runtime/SKILL.md +9 -9
  3. package/atris/skills/create-member/SKILL.md +16 -13
  4. package/atris/skills/design/SKILL.md +27 -4
  5. package/atris/skills/engines/SKILL.md +55 -12
  6. package/atris/skills/x-search/SKILL.md +8 -6
  7. package/atris/skills/youtube/SKILL.md +37 -8
  8. package/atris/team/brainstormer/MEMBER.md +2 -2
  9. package/atris/team/executor/MEMBER.md +3 -3
  10. package/atris/team/launcher/MEMBER.md +4 -4
  11. package/atris/team/navigator/MEMBER.md +3 -3
  12. package/atris/team/researcher/MEMBER.md +2 -2
  13. package/atris/team/validator/MEMBER.md +1 -1
  14. package/atris.md +14 -0
  15. package/ax +153 -23
  16. package/bin/atris.js +78 -30
  17. package/commands/aeo.js +4 -4
  18. package/commands/agents.js +1 -1
  19. package/commands/align.js +12 -12
  20. package/commands/analytics.js +3 -3
  21. package/commands/app.js +5 -5
  22. package/commands/ask.js +175 -0
  23. package/commands/auth.js +7 -7
  24. package/commands/autoland.js +29 -5
  25. package/commands/autopilot-front.js +6 -6
  26. package/commands/autopilot.js +90 -90
  27. package/commands/avail.js +5 -5
  28. package/commands/bench.js +63 -1
  29. package/commands/browse.js +2 -2
  30. package/commands/business.js +12 -12
  31. package/commands/card.js +2 -2
  32. package/commands/clean.js +13 -13
  33. package/commands/close.js +3 -3
  34. package/commands/compile.js +13 -13
  35. package/commands/computer.js +94 -27
  36. package/commands/console.js +5 -5
  37. package/commands/decide.js +1 -1
  38. package/commands/deck.js +9 -9
  39. package/commands/drive.js +4 -4
  40. package/commands/engine.js +173 -19
  41. package/commands/errors.js +3 -3
  42. package/commands/experiments.js +18 -1
  43. package/commands/feed.js +2 -2
  44. package/commands/feedback.js +4 -4
  45. package/commands/fleet.js +12 -12
  46. package/commands/game.js +1 -1
  47. package/commands/guide.js +49 -0
  48. package/commands/improve.js +19 -17
  49. package/commands/init.js +67 -19
  50. package/commands/interview.js +7 -7
  51. package/commands/land.js +40 -15
  52. package/commands/learn.js +12 -12
  53. package/commands/lesson.js +2 -2
  54. package/commands/log.js +1 -1
  55. package/commands/loops.js +4 -4
  56. package/commands/member.js +75 -50
  57. package/commands/mission.js +90 -82
  58. package/commands/one-lap.js +1 -1
  59. package/commands/pack.js +74 -8
  60. package/commands/plugin.js +6 -6
  61. package/commands/probe.js +9 -9
  62. package/commands/pull.js +35 -35
  63. package/commands/pulse.js +1 -1
  64. package/commands/push.js +21 -21
  65. package/commands/rainmaker.js +1 -1
  66. package/commands/recap.js +7 -7
  67. package/commands/reel.js +2 -2
  68. package/commands/release.js +3 -3
  69. package/commands/report.js +1 -1
  70. package/commands/review.js +5 -5
  71. package/commands/revisions.js +1 -1
  72. package/commands/run.js +1 -1
  73. package/commands/scout.js +3 -3
  74. package/commands/security-review.js +7 -7
  75. package/commands/serve.js +9 -9
  76. package/commands/setup.js +1 -1
  77. package/commands/sign.js +4 -4
  78. package/commands/signup.js +4 -4
  79. package/commands/site.js +8 -3
  80. package/commands/skill.js +6 -6
  81. package/commands/slop.js +18 -18
  82. package/commands/social.js +3 -3
  83. package/commands/soul.js +14 -14
  84. package/commands/spaceship.js +1 -1
  85. package/commands/status.js +2 -2
  86. package/commands/strings.js +13 -13
  87. package/commands/study.js +4 -4
  88. package/commands/sync.js +8 -8
  89. package/commands/task.js +50 -25
  90. package/commands/teach.js +2 -2
  91. package/commands/team.js +1 -1
  92. package/commands/theme.js +2 -2
  93. package/commands/tree.js +34 -0
  94. package/commands/truth.js +7 -7
  95. package/commands/verify.js +12 -12
  96. package/commands/visualize.js +2 -2
  97. package/commands/workflow.js +97 -40
  98. package/commands/worktree.js +1 -1
  99. package/commands/write.js +9 -9
  100. package/commands/x-search.js +89 -17
  101. package/commands/xp.js +6 -3
  102. package/commands/youtube.js +601 -48
  103. package/lib/accept-verify-gate.js +3 -3
  104. package/lib/activity-stream.js +1 -1
  105. package/lib/auto-accept-certified.js +28 -12
  106. package/lib/autoland.js +6 -6
  107. package/lib/bench/engines.js +59 -8
  108. package/lib/bench/report.js +1 -1
  109. package/lib/bench/runner.js +204 -5
  110. package/lib/bench/tree-render.js +76 -0
  111. package/lib/card.js +1 -1
  112. package/lib/chat-log-scan.js +1 -1
  113. package/lib/claude-boot-block.js +2 -0
  114. package/lib/deck-compose.js +1 -1
  115. package/lib/deck-history.js +2 -2
  116. package/lib/deck-layout.js +1 -1
  117. package/lib/deck-review.js +5 -5
  118. package/lib/deck-schema.js +1 -1
  119. package/lib/default-verifier.js +1 -1
  120. package/lib/developer-api.js +3 -0
  121. package/lib/engine-ask.js +45 -7
  122. package/lib/engine-registry.js +15 -2
  123. package/lib/file-ops.js +1 -1
  124. package/lib/fleet.js +71 -35
  125. package/lib/html-render.js +1 -1
  126. package/lib/intents.js +172 -0
  127. package/lib/journal.js +1 -1
  128. package/lib/known-commands.js +2 -2
  129. package/lib/land-green.js +364 -0
  130. package/lib/lesson-contradiction.js +2 -2
  131. package/lib/lesson-preflight.js +1 -1
  132. package/lib/manifest.js +5 -5
  133. package/lib/member-alive.js +9 -5
  134. package/lib/memory-view.js +3 -3
  135. package/lib/mission-protected-lane.js +1 -1
  136. package/lib/mission-root.js +2 -2
  137. package/lib/orb-context.js +10 -10
  138. package/lib/pack-capabilities.js +861 -3
  139. package/lib/permission-grants.js +2 -2
  140. package/lib/policy-lessons.js +6 -6
  141. package/lib/pulse.js +8 -8
  142. package/lib/receipt-evidence.js +1 -1
  143. package/lib/reel.js +1 -1
  144. package/lib/review-integrity.js +1 -1
  145. package/lib/runner-command.js +35 -5
  146. package/lib/scorecard.js +4 -4
  147. package/lib/security-scan.js +5 -5
  148. package/lib/self-drive.js +1 -1
  149. package/lib/site-publish.js +372 -0
  150. package/lib/site.js +1 -1
  151. package/lib/slides-deck.js +2 -2
  152. package/lib/state-detection.js +2 -2
  153. package/lib/sync-telemetry.js +2 -2
  154. package/lib/task-db.js +17 -10
  155. package/lib/task-explanation.js +1 -1
  156. package/lib/task-proof.js +2 -2
  157. package/lib/task-receipt.js +3 -3
  158. package/lib/theme.js +12 -12
  159. package/lib/todo-fallback.js +3 -3
  160. package/lib/todo-sections.js +1 -1
  161. package/lib/tree-hash.js +79 -0
  162. package/lib/usage.js +1 -1
  163. package/lib/wish-delegate.js +1 -1
  164. package/lib/workspace-safety.js +4 -4
  165. package/lib/workspace-scaffold.js +3 -3
  166. package/package.json +1 -1
  167. package/scripts/det/concierge-bakeoff.sh +26 -0
  168. package/scripts/det/ytquote-repair.js +0 -1
  169. package/scripts/det/ytrail-race.js +0 -5
  170. package/scripts/member-operate.mjs +52 -14
  171. package/scripts/outbound-artifact-gate.js +0 -3
  172. package/utils/auth.js +0 -3
  173. package/utils/config.js +0 -1
  174. package/scripts/det/checklist-score.js +0 -191
package/commands/avail.js CHANGED
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  /**
4
- * Booking availability read/update the windows that power /book/{username}.
4
+ * Booking availability: read/update the windows that power /book/{username}.
5
5
  *
6
6
  * atris avail # show current settings
7
7
  * atris avail set --wake 2:30pm --sleep 9pm --where Ibiza --days all --apply
@@ -172,13 +172,13 @@ function summarizeHours(availableHours) {
172
172
  }
173
173
 
174
174
  // The booking link is the funnel: an operator shares /book/{username} so a
175
- // stranger can pick a slot. With no claimed handle there is no link say so and
175
+ // stranger can pick a slot. With no claimed handle there is no link, say so and
176
176
  // name the fix instead of silently dropping the line.
177
177
  function formatBookingLink(username, appBase) {
178
178
  const handle = String(username || '').trim();
179
179
  const base = String(appBase || '').replace(/\/+$/, '');
180
180
  if (!handle) {
181
- return ' link: none yet claim a handle with `atris signup` to get your /book link';
181
+ return ' link: none yet, claim a handle with `atris signup` to get your /book link';
182
182
  }
183
183
  return ` link: ${base}/book/${handle}`;
184
184
  }
@@ -263,7 +263,7 @@ async function setAvail(argv) {
263
263
  console.log(summarizeHours(payload.available_hours));
264
264
 
265
265
  if (!opts.apply) {
266
- console.log('\n Dry run add --apply to push to your profile.');
266
+ console.log('\n Dry run, add --apply to push to your profile.');
267
267
  return 0;
268
268
  }
269
269
 
@@ -308,7 +308,7 @@ async function slotsAvail(argv) {
308
308
  if (!username) {
309
309
  const identity = await apiRequestJson('/identity/me', { method: 'GET', token });
310
310
  if (!identity.ok || !identity.data?.username) {
311
- console.error('Could not resolve username pass --user keshav');
311
+ console.error('Could not resolve username, pass --user keshav');
312
312
  return 1;
313
313
  }
314
314
  username = identity.data.username;
package/commands/bench.js CHANGED
@@ -8,9 +8,11 @@ const {
8
8
  packMetadata,
9
9
  readResultRecords,
10
10
  runBench,
11
+ runBenchPair,
11
12
  taskMetadata,
12
13
  } = require('../lib/bench/runner');
13
14
  const { buildBenchReport, renderBenchReportText } = require('../lib/bench/report');
15
+ const { materializeCandidate } = require('../lib/bench/tree-render');
14
16
  const { hasFlag } = require('../lib/arg-parser');
15
17
 
16
18
  function isAtrisCliRepo(cwd = process.cwd()) {
@@ -51,16 +53,32 @@ function readRepeatedFlag(args, name) {
51
53
  }
52
54
 
53
55
  function printHelp() {
54
- console.log('Usage: atris bench run [--pack <id>] [--engine <name>] [--task <id> ...] [--label baseline|candidate] [--experiment <id>] [--update-baseline] [--json]');
56
+ console.log('Usage: atris bench run [--pack <id>] [--engine <name>] [--model <id>] [--task <id> ...] [--label baseline|candidate] [--experiment <id>] [--update-baseline] [--json]');
57
+ console.log('Usage: atris bench pair --candidate <dir|git-ref> [--pack <id>] [--engine <name>] [--model <id>] [--task <id> ...] [--min-win-margin <n>] [--repeats <n>] [--current <dir>] [--json]');
55
58
  console.log('Usage: atris bench results [--last N] [--json]');
56
59
  console.log('Usage: atris bench tasks [--pack <id>] [--json]');
57
60
  console.log('Usage: atris bench packs [--json]');
58
61
  console.log('Usage: atris bench report [--pack agents-v1] [--json]');
62
+ console.log('packs: core-v1, agents-v1, harness-v1');
59
63
  console.log('');
60
64
  console.log('Bench runs product gates and only belongs in the atris CLI repo.');
61
65
  console.log('Pass --here to allow running outside that repo.');
62
66
  }
63
67
 
68
+ function pairScoreText(score) {
69
+ if (score === null) return 'could-not-run';
70
+ return score === 1 ? 'pass' : 'fail';
71
+ }
72
+
73
+ function printPairText(record) {
74
+ const width = Math.max(0, ...record.tasks.map((task) => task.id.length)) + 3;
75
+ for (const task of record.tasks) {
76
+ console.log(`${task.id.padEnd(width)}current ${pairScoreText(task.current)} candidate ${pairScoreText(task.candidate)} ${task.outcome}`);
77
+ }
78
+ console.log(`wins ${record.wins} losses ${record.losses} ties ${record.ties}`);
79
+ console.log(`verdict: ${record.verdict}, ${record.reason}`);
80
+ }
81
+
64
82
  function printRunText(record) {
65
83
  console.log(record.summary);
66
84
  for (const task of record.tasks) {
@@ -76,6 +94,7 @@ async function runCommand(args) {
76
94
  const result = await runBench({
77
95
  pack: readInlineFirstFlag(args, '--pack') || undefined,
78
96
  engine: readInlineFirstFlag(args, '--engine') || undefined,
97
+ model: readInlineFirstFlag(args, '--model') || undefined,
79
98
  taskIds: readRepeatedFlag(args, '--task'),
80
99
  label: readInlineFirstFlag(args, '--label'),
81
100
  experiment: readInlineFirstFlag(args, '--experiment'),
@@ -102,6 +121,48 @@ async function runCommand(args) {
102
121
  }
103
122
  }
104
123
 
124
+ async function pairCommand(args) {
125
+ const asJson = hasFlag(args, '--json');
126
+ const candidateSource = readInlineFirstFlag(args, '--candidate');
127
+ let candidateRoot = null;
128
+ let removeCandidate = false;
129
+ try {
130
+ if (!candidateSource) throw new BenchInfraError('candidate is required');
131
+ try {
132
+ removeCandidate = !fs.statSync(path.resolve(candidateSource)).isDirectory();
133
+ } catch {
134
+ removeCandidate = true;
135
+ }
136
+ candidateRoot = materializeCandidate(candidateSource, process.cwd());
137
+ const result = await runBenchPair({
138
+ pack: readInlineFirstFlag(args, '--pack') || undefined,
139
+ engine: readInlineFirstFlag(args, '--engine') || undefined,
140
+ model: readInlineFirstFlag(args, '--model') || undefined,
141
+ taskIds: readRepeatedFlag(args, '--task'),
142
+ minWinMargin: readInlineFirstFlag(args, '--min-win-margin') || undefined,
143
+ repeats: readInlineFirstFlag(args, '--repeats') || undefined,
144
+ currentRoot: readInlineFirstFlag(args, '--current') || undefined,
145
+ candidateRoot,
146
+ stateRoot: process.cwd(),
147
+ });
148
+ if (asJson) console.log(JSON.stringify(result.record));
149
+ else printPairText(result.record);
150
+ return result.exitCode;
151
+ } catch (err) {
152
+ const message = err && err.message ? err.message : String(err);
153
+ if (asJson) {
154
+ console.log(JSON.stringify({ schema: 'atris.bench.error.v1', error: message }));
155
+ } else {
156
+ console.error(`atris bench: ${message}`);
157
+ }
158
+ return 2;
159
+ } finally {
160
+ if (removeCandidate && candidateRoot) {
161
+ fs.rmSync(candidateRoot, { recursive: true, force: true });
162
+ }
163
+ }
164
+ }
165
+
105
166
  function tasksCommand(args) {
106
167
  const pack = readInlineFirstFlag(args, '--pack') || undefined;
107
168
  const tasks = taskMetadata({ pack });
@@ -200,6 +261,7 @@ async function benchCommand(args = []) {
200
261
  return 2;
201
262
  }
202
263
  if (subcommand === 'run') return runCommand(rest);
264
+ if (subcommand === 'pair') return pairCommand(rest);
203
265
  if (subcommand === 'tasks') return tasksCommand(rest);
204
266
  if (subcommand === 'packs') return packsCommand(rest);
205
267
  if (subcommand === 'results') return resultsCommand(rest);
@@ -32,7 +32,7 @@ async function browseAtris() {
32
32
  const result = await apiRequestJson(endpoint, { method: 'GET', token: creds.token });
33
33
 
34
34
  if (!result.ok) {
35
- // API failed try local templates
35
+ // API failed, try local templates
36
36
  console.log(' API unavailable, checking local templates...');
37
37
  const localDir = path.join(os.homedir(), '.atris', 'templates');
38
38
  if (!fs.existsSync(localDir)) {
@@ -61,7 +61,7 @@ async function browseAtris() {
61
61
  desc = meta.description || '';
62
62
  } catch {}
63
63
  }
64
- console.log(` ${name}${desc ? '' + desc : ''}`);
64
+ console.log(` ${name}${desc ? ': ' + desc : ''}`);
65
65
  }
66
66
  console.log('');
67
67
  return;
@@ -58,14 +58,14 @@ function validateBusinessCreateName(name) {
58
58
  return {
59
59
  ok: false,
60
60
  usage: BUSINESS_CREATE_USAGE,
61
- detail: `Refusing to create a business named "${name}" looks like a flag, not a name.`,
61
+ detail: `Refusing to create a business named "${name}", looks like a flag, not a name.`,
62
62
  };
63
63
  }
64
64
  if (isAccidentalHelpBusiness(name)) {
65
65
  return {
66
66
  ok: false,
67
67
  usage: BUSINESS_CREATE_USAGE,
68
- detail: `Refusing to create a business named "${name}" looks like accidental help output, not a real business.`,
68
+ detail: `Refusing to create a business named "${name}", looks like accidental help output, not a real business.`,
69
69
  };
70
70
  }
71
71
  return { ok: true };
@@ -883,7 +883,7 @@ function collectBusinessOperatingState(cwd = process.cwd(), nowMs = Date.now())
883
883
  }
884
884
 
885
885
  function seedBusinessStarterTask(cwd, todoPath, starterAction) {
886
- const title = `${starterAction.title} ${starterAction.action}`;
886
+ const title = `${starterAction.title}: ${starterAction.action}`;
887
887
  try {
888
888
  const taskDb = require('../lib/task-db');
889
889
  const db = taskDb.open();
@@ -1626,12 +1626,12 @@ ${stagedSources.length > 0 ? `- Local evidence files: ${stagedSources.length}` :
1626
1626
  '- Before sharing the workspace, run `atris business share --write` and send the handoff.',
1627
1627
  ].filter(Boolean).join('\n') + '\n';
1628
1628
  fs.writeFileSync(cheatSheetPath, operatorSummary, 'utf8');
1629
- fs.writeFileSync(onePagerPath, operatorSummary.replace('# ', '# One Pager '), 'utf8');
1629
+ fs.writeFileSync(onePagerPath, operatorSummary.replace('# ', '# One Pager: '), 'utf8');
1630
1630
 
1631
1631
  const todoPath = path.join(cwd, 'atris', 'TODO.md');
1632
1632
  if (fs.existsSync(todoPath)) {
1633
1633
  let todoContent = fs.readFileSync(todoPath, 'utf8');
1634
- const taskLine = `- **Onboard:** ${starterAction.title} ${starterAction.action} [execute]\n`;
1634
+ const taskLine = `- **Onboard:** ${starterAction.title}: ${starterAction.action} [execute]\n`;
1635
1635
  const backlogMatch = todoContent.match(/^## Backlog\s*$/m);
1636
1636
  if (backlogMatch) {
1637
1637
  const insertAt = backlogMatch.index + backlogMatch[0].length;
@@ -1761,7 +1761,7 @@ function detectBusinessSlug(explicitSlug) {
1761
1761
  async function findExistingBusinessBySlug(slug, token) {
1762
1762
  if (!slug) return null;
1763
1763
 
1764
- // Local cache first no network round-trip needed.
1764
+ // Local cache first, no network round-trip needed.
1765
1765
  const local = loadBusinesses();
1766
1766
  if (local[slug]) {
1767
1767
  return { id: local[slug].business_id, name: local[slug].name, slug, source: 'local' };
@@ -1774,7 +1774,7 @@ async function findExistingBusinessBySlug(slug, token) {
1774
1774
 
1775
1775
  if (!token) return null;
1776
1776
 
1777
- // Cloud lookup covers businesses the user is a member of but hasn't added.
1777
+ // Cloud lookup, covers businesses the user is a member of but hasn't added.
1778
1778
  const direct = await apiRequestJson(`/business/by-slug/${encodeURIComponent(slug)}`, {
1779
1779
  method: 'GET',
1780
1780
  token,
@@ -1878,7 +1878,7 @@ async function listBusinesses(opts = {}) {
1878
1878
 
1879
1879
  /**
1880
1880
  * Walk ~/arena/atris-business/ and print a fleet status table for every
1881
- * customer workspace. Pure local no API calls, no rate-limit risk.
1881
+ * customer workspace. Pure local, no API calls, no rate-limit risk.
1882
1882
  *
1883
1883
  * Classifies each dir as: ready, flat, unbound, nested, bare, or superseded.
1884
1884
  *
@@ -1986,7 +1986,7 @@ function listBusinessesLocal(opts = {}) {
1986
1986
  }
1987
1987
 
1988
1988
  console.log('');
1989
- console.log(`Atris Fleet ${rootDir}`);
1989
+ console.log(`Atris Fleet: ${rootDir}`);
1990
1990
  console.log('═'.repeat(86));
1991
1991
  console.log(' CUSTOMER STATE FILES BIZ.JSON ATRIS/ ACTION');
1992
1992
  console.log(' ' + '─'.repeat(83));
@@ -2016,7 +2016,7 @@ function listBusinessesLocal(opts = {}) {
2016
2016
  const counts = {};
2017
2017
  for (const c of customers) counts[c.state] = (counts[c.state] || 0) + 1;
2018
2018
  const summary = order.filter((s) => counts[s]).map((s) => `${counts[s]} ${s}`).join(', ');
2019
- console.log(` ${customers.length} customers ${summary}`);
2019
+ console.log(` ${customers.length} customers: ${summary}`);
2020
2020
  console.log('');
2021
2021
 
2022
2022
  const needsWork = customers.filter((c) => ['flat', 'unbound', 'flat-unbound', 'nested'].includes(c.state));
@@ -3086,7 +3086,7 @@ async function connectService(connector, ...flags) {
3086
3086
  for (const s of integrations) {
3087
3087
  console.log(` ${s}`);
3088
3088
  }
3089
- if (integrations.length === 0) console.log(' (none found install skills first)');
3089
+ if (integrations.length === 0) console.log(' (none found, install skills first)');
3090
3090
  return;
3091
3091
  }
3092
3092
 
@@ -3540,7 +3540,7 @@ function printBusinessHelp() {
3540
3540
  }
3541
3541
 
3542
3542
  async function businessCommand(subcommand, ...args) {
3543
- // Help intercept without this, `atris business init --help` would treat
3543
+ // Help intercept, without this, `atris business init --help` would treat
3544
3544
  // `--help` as a business name and create one. Same for any subcommand that
3545
3545
  // takes a positional name/slug.
3546
3546
  if (!subcommand || isHelpToken(subcommand)) {
package/commands/card.js CHANGED
@@ -1,4 +1,4 @@
1
- // atris card one line of text -> a beautiful, on-brand image (your theme).
1
+ // atris card, one line of text -> a beautiful, on-brand image (your theme).
2
2
  //
3
3
  // atris card "Ship faster" --kind statement --theme brand --size og
4
4
  // atris card "It just works" --kind quote --by "a happy user"
@@ -64,7 +64,7 @@ function run(argv) {
64
64
  const { flags, pos } = parseFlags(argv);
65
65
 
66
66
  if (pos[0] === 'help' || flags.help) {
67
- console.log(`\n atris card one line of text into an on-brand image\n
67
+ console.log(`\n atris card, one line of text into an on-brand image\n
68
68
  atris card "Your headline" [--kind statement|quote|stat] [--theme <name>] [--size og|wide|square|story]
69
69
  flags: --sub --kicker --by --number --label --brand --version --out <file.png> --html-only\n
70
70
  examples:
package/commands/clean.js CHANGED
@@ -122,7 +122,7 @@ function cleanAtris(options = {}) {
122
122
  if (unhealable.length > 0) {
123
123
  console.log(`⚠ ${unhealable.length} MAP.md ${unhealable.length === 1 ? 'ref' : 'refs'} couldn't be healed:`);
124
124
  unhealable.slice(0, 3).forEach(ref => {
125
- console.log(` • ${ref.file}:${ref.line} ${ref.reason}`);
125
+ console.log(` • ${ref.file}:${ref.line}: ${ref.reason}`);
126
126
  });
127
127
  if (unhealable.length > 3) {
128
128
  console.log(` ... and ${unhealable.length - 3} more`);
@@ -156,7 +156,7 @@ function cleanAtris(options = {}) {
156
156
  if (stalePages.length > 0) {
157
157
  console.log(`⚠ ${stalePages.length} stale ${stalePages.length === 1 ? 'page' : 'pages'} (source changed since last compiled):`);
158
158
  stalePages.forEach(sp => {
159
- console.log(` • ${path.relative(cwd, sp.page)} stale source: ${sp.staleSource}`);
159
+ console.log(` • ${path.relative(cwd, sp.page)}: stale source: ${sp.staleSource}`);
160
160
  });
161
161
  console.log('');
162
162
  } else {
@@ -165,7 +165,7 @@ function cleanAtris(options = {}) {
165
165
 
166
166
  // Dead code
167
167
  if ((results.deadFiles || []).length > 0) {
168
- console.log(`⚠ ${results.deadFiles.length} dead ${results.deadFiles.length === 1 ? 'file' : 'files'} (unreachable and unreferenced atris slop dead for detail):`);
168
+ console.log(`⚠ ${results.deadFiles.length} dead ${results.deadFiles.length === 1 ? 'file' : 'files'} (unreachable and unreferenced, atris slop dead for detail):`);
169
169
  results.deadFiles.slice(0, 5).forEach((f) => console.log(` • ${f}`));
170
170
  if (results.deadFiles.length > 5) console.log(` ... and ${results.deadFiles.length - 5} more`);
171
171
  console.log('');
@@ -252,8 +252,8 @@ function firstLine(text) {
252
252
  function mapScriptLine(step) {
253
253
  const script = MAP_SCRIPTS.find(s => step.script.endsWith(s.file));
254
254
  if (step.status === 'would_run') return `✓ ${script.dryLabel} (${step.script})`;
255
- if (step.status === 'skipped') return `○ Skipped ${step.script} ${step.detail}`;
256
- if (step.status === 'failed') return `✗ ${script.failLabel} ${step.detail}`;
255
+ if (step.status === 'skipped') return `○ Skipped ${step.script}: ${step.detail}`;
256
+ if (step.status === 'failed') return `✗ ${script.failLabel}: ${step.detail}`;
257
257
  return `✓ ${script.ranLabel}`;
258
258
  }
259
259
 
@@ -368,8 +368,8 @@ function healBrokenMapRefs(cwd, atrisDir, dryRun = false, homeDir = os.homedir()
368
368
 
369
369
  // Match both `file.js:123` and `file.js:123-456` with surrounding context
370
370
  // [^\S\n] = horizontal whitespace only (no newlines)
371
- // Required delimiter [(,[,—,-] prevents bleeding into adjacent refs on same line
372
- const refPattern = /(`?)([a-zA-Z0-9_~\-./\\]+\.(js|ts|py|go|rs|rb|java|c|cpp|h|hpp|md|json|yaml|yml)):(\d+)(?:-(\d+))?(`?)([^\S\n]*[\(\[—\-][^\S\n]*([^)\]\n]+))?/g;
371
+ // Required delimiter set, including the u2014 escape, prevents bleeding into adjacent refs on same line
372
+ const refPattern = /(`?)([a-zA-Z0-9_~\-./\\]+\.(js|ts|py|go|rs|rb|java|c|cpp|h|hpp|md|json|yaml|yml)):(\d+)(?:-(\d+))?(`?)([^\S\n]*[\(\[\u2014\-][^\S\n]*([^)\]\n]+))?/g;
373
373
 
374
374
  // Function Inventory form: `symbolName()` → `file:line[-line]`
375
375
  // Pre-scan to build a (file:line) → symbol map so refs with the symbol BEFORE them still verify.
@@ -518,7 +518,7 @@ function findFunctionEnd(lines, startLine) {
518
518
  }
519
519
 
520
520
  /**
521
- * Extract a symbol name from context like "(interactiveEntry function)" or "Main entry"
521
+ * Extract a symbol name from context like "(interactiveEntry function)" or "Main entry" after punctuation
522
522
  */
523
523
  function extractSymbol(context) {
524
524
  if (!context) return null;
@@ -529,7 +529,7 @@ function extractSymbol(context) {
529
529
  const unquoted = context.trim().replace(/`/g, '');
530
530
  const wrapper = unquoted.match(/^([\(\[])/)?.[1];
531
531
  let cleaned = unquoted
532
- .replace(/^[\(\[—\-:]+\s*/, '')
532
+ .replace(/^[\(\[\u2014\-:]+\s*/, '')
533
533
  .trim();
534
534
  if (wrapper === '(') cleaned = cleaned.replace(/\)$/, '').trim();
535
535
  if (wrapper === '[') cleaned = cleaned.replace(/\]$/, '').trim();
@@ -553,13 +553,13 @@ function extractSymbol(context) {
553
553
 
554
554
  /**
555
555
  * Find the line number where a symbol is defined (strict patterns only)
556
- * Returns null if only loose matches found prevents healing to wrong locations
556
+ * Returns null if only loose matches found, prevents healing to wrong locations
557
557
  */
558
558
  function findSymbolLine(fileContent, symbol) {
559
559
  const lines = fileContent.split('\n');
560
560
  const esc = escapeRegExp(symbol);
561
561
 
562
- // Strict definition patterns only no loose fallback
562
+ // Strict definition patterns only, no loose fallback
563
563
  const patterns = [
564
564
  new RegExp(`^\\s*(async\\s+)?function\\s+${esc}\\s*\\(`), // function name(
565
565
  new RegExp(`^\\s*(const|let|var)\\s+${esc}\\s*=`), // const/let/var name =
@@ -652,7 +652,7 @@ function checkPageStaleness(cwd, filePath) {
652
652
 
653
653
  // Check each source's mtime against last_compiled.
654
654
  // Source entries can include a line range and/or a parenthesized annotation,
655
- // e.g. "bin/atris.js:199-340 (showHelp function)" strip both before stat.
655
+ // e.g. "bin/atris.js:199-340 (showHelp function)". Strip both before stat.
656
656
  for (const source of sources) {
657
657
  const filePart = source
658
658
  .replace(/\s*\([^)]*\)\s*$/, '') // drop trailing "(annotation)"
@@ -670,7 +670,7 @@ function checkPageStaleness(cwd, filePath) {
670
670
  };
671
671
  }
672
672
  } catch {
673
- // Source file doesn't exist that's also a staleness signal
673
+ // Source file doesn't exist, that's also a staleness signal
674
674
  return {
675
675
  page: filePath,
676
676
  staleSource: source + ' (missing)',
package/commands/close.js CHANGED
@@ -472,7 +472,7 @@ function commandAdd(args, context = {}) {
472
472
  return 0;
473
473
  }
474
474
 
475
- // atris close check [id] run the live probes on open incidents. This is the
475
+ // atris close check [id]: run the live probes on open incidents. This is the
476
476
  // first move for any session entering an area: if a probe still fails, the
477
477
  // recorded theory is still live; do not diagnose fresh. After
478
478
  // PROBE_STOP_AFTER_FAILED_CHECKS failing re-checks the verdict is automatic:
@@ -893,7 +893,7 @@ function pulseLivenessState(cwd, now) {
893
893
  // decommissioned or intermittent loop leaves stale receipts frozen; without a
894
894
  // guard the last N finished ticks stay net-negative forever and the flag can
895
895
  // never clear (no fresh positive receipt arrives). It also must not treat a
896
- // scattered set e.g. one tick today plus five from days ago as a "run":
896
+ // scattered set, e.g. one tick today plus five from days ago, as a "run":
897
897
  // summing rewards across a multi-day gap is meaningless. So we only consider
898
898
  // finished ticks from the last few days and require a full window of them; a
899
899
  // silent or sparse loop is the pulse-liveness sensor's concern, and here it
@@ -1524,7 +1524,7 @@ function run(args = [], context = {}) {
1524
1524
  printHelp();
1525
1525
  return 0;
1526
1526
  }
1527
- // `close <sub> --help` is a help request, not a run sweep and scan mutate
1527
+ // `close <sub> --help` is a help request, not a run, sweep and scan mutate
1528
1528
  // the ledger (escalations, auto-closes), so never execute a subcommand just
1529
1529
  // to show usage.
1530
1530
  if (rest.includes('--help') || rest.includes('-h')) {
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Atris Compile learn like AI, run like code.
2
+ * Atris Compile: learn like AI, run like code.
3
3
  *
4
4
  * The compile loop: execution records → compile-to-deterministic-code →
5
5
  * statistical backtest → gated promote. The LLM is only in the build path;
@@ -328,7 +328,7 @@ function deepEqual(a, b) {
328
328
  function loadRunner(root, name) {
329
329
  const p = runnerPath(root, name);
330
330
  if (!fs.existsSync(p)) {
331
- throw new Error(`no compiled artifact at ${path.relative(root, p)} run "atris compile build ${name}" first`);
331
+ throw new Error(`no compiled artifact at ${path.relative(root, p)}, run "atris compile build ${name}" first`);
332
332
  }
333
333
  const resolved = require.resolve(p);
334
334
  delete require.cache[resolved]; // always load the latest compiled version
@@ -344,7 +344,7 @@ function loadRunner(root, name) {
344
344
  async function runBacktest(root, name, options = {}) {
345
345
  const records = readRecords(root, name);
346
346
  if (records.length === 0) {
347
- throw new Error(`no execution records for "${name}" add some with "atris compile record ${name} --input ... --output ..."`);
347
+ throw new Error(`no execution records for "${name}", add some with "atris compile record ${name} --input ... --output ..."`);
348
348
  }
349
349
  const runner = loadRunner(root, name);
350
350
  const manifest = readManifest(root, name) || defaultManifest(name);
@@ -395,7 +395,7 @@ async function runBacktest(root, name, options = {}) {
395
395
  function promoteProcess(root, name, options = {}) {
396
396
  const manifest = readManifest(root, name);
397
397
  if (!manifest) {
398
- throw new Error(`no manifest for "${name}" compile and backtest it first`);
398
+ throw new Error(`no manifest for "${name}", compile and backtest it first`);
399
399
  }
400
400
  if (options.threshold !== undefined) {
401
401
  // the only way to change the standing gate: deliberate, at promote time
@@ -403,13 +403,13 @@ function promoteProcess(root, name, options = {}) {
403
403
  }
404
404
  const bt = manifest.backtest;
405
405
  if (!bt) {
406
- throw new Error(`"${name}" has no backtest run "atris compile backtest ${name}" first`);
406
+ throw new Error(`"${name}" has no backtest, run "atris compile backtest ${name}" first`);
407
407
  }
408
408
  if (bt.version !== manifest.version) {
409
- throw new Error(`backtest is for version ${bt.version} but current version is ${manifest.version} re-run the backtest`);
409
+ throw new Error(`backtest is for version ${bt.version} but current version is ${manifest.version}, re-run the backtest`);
410
410
  }
411
411
  if (bt.accuracy < manifest.threshold) {
412
- throw new Error(`backtest accuracy ${(bt.accuracy * 100).toFixed(2)}% is below the ${(manifest.threshold * 100).toFixed(2)}% gate (${bt.passed}/${bt.total}) not promoting`);
412
+ throw new Error(`backtest accuracy ${(bt.accuracy * 100).toFixed(2)}% is below the ${(manifest.threshold * 100).toFixed(2)}% gate (${bt.passed}/${bt.total}), not promoting`);
413
413
  }
414
414
  manifest.status = 'active';
415
415
  manifest.promotedAt = new Date().toISOString();
@@ -436,10 +436,10 @@ Write the compiled artifact to ${runRel} with this exact contract:
436
436
  - CommonJS module exporting { run }: \`module.exports = { run };\`
437
437
  - \`run(input)\` takes one input value and returns the output (sync return or promise both fine)
438
438
  - deterministic: no network calls, no Date.now()/new Date() in outputs, no randomness, no environment reads
439
- - Node.js built-ins only zero npm dependencies
439
+ - Node.js built-ins only, zero npm dependencies
440
440
  - handle malformed input by throwing a clear Error, never by guessing
441
441
 
442
- The artifact will be verified by replaying every execution record through run() and requiring near-perfect accuracy, so generalize the rules do not hardcode a lookup table of the sample records.
442
+ The artifact will be verified by replaying every execution record through run() and requiring near-perfect accuracy, so generalize the rules, do not hardcode a lookup table of the sample records.
443
443
 
444
444
  Reply [COMPILE_COMPLETE] when ${runRel} is written.`;
445
445
  }
@@ -448,7 +448,7 @@ function executeBuild(root, name, options = {}) {
448
448
  const { verbose = false, timeout = 600000, notes = '', cmdOverride } = options;
449
449
  const records = readRecords(root, name);
450
450
  if (records.length === 0) {
451
- throw new Error(`no execution records for "${name}" record real runs first, then compile`);
451
+ throw new Error(`no execution records for "${name}", record real runs first, then compile`);
452
452
  }
453
453
 
454
454
  if (!cmdOverride) {
@@ -526,7 +526,7 @@ function formatAccuracy(accuracy) {
526
526
  function printBacktest(name, result, failureCount, manifest) {
527
527
  console.log(`backtest ${name} v${result.version}: ${result.passed}/${result.total} passed (${formatAccuracy(result.accuracy)}), gate ${formatAccuracy(result.threshold)}`);
528
528
  if (result.total < LOW_RECORD_WARNING) {
529
- console.log(`note: only ${result.total} record${result.total === 1 ? '' : 's'} accuracy means more with ${LOW_RECORD_WARNING}+`);
529
+ console.log(`note: only ${result.total} record${result.total === 1 ? '' : 's'}, accuracy means more with ${LOW_RECORD_WARNING}+`);
530
530
  }
531
531
  if (failureCount > 0) {
532
532
  console.log(`${failureCount} mismatch${failureCount === 1 ? '' : 'es'}. first ${Math.min(failureCount, 3)}:`);
@@ -675,7 +675,7 @@ function showCompileHelp() {
675
675
  console.log('Server commands require --agent <id> and Atris auth from atris login or ATRIS_TOKEN.');
676
676
  console.log('Loop: record real runs -> build -> backtest -> promote -> exec --record');
677
677
  console.log('When backtest accuracy drops below the gate, the process is marked drifted');
678
- console.log('and a recompile is suggested self-healing against process drift.');
678
+ console.log('and a recompile is suggested, self-healing against process drift.');
679
679
  console.log('');
680
680
  }
681
681
 
@@ -830,7 +830,7 @@ async function compileCommand(subcommand, ...rawArgs) {
830
830
  }
831
831
  const manifest = readManifest(root, name);
832
832
  if (manifest && manifest.status === 'drifted') {
833
- console.error(`warning: ${name} is marked drifted outputs may be stale. recompile with "atris compile build ${name}"`);
833
+ console.error(`warning: ${name} is marked drifted, outputs may be stale. recompile with "atris compile build ${name}"`);
834
834
  }
835
835
  const runner = loadRunner(root, name);
836
836
  const output = await runner.run(input);